Can't get selected value from dropdown - javascript

I have a form that contains an input and a dropdown that's filled with elements from an API. But I'm having a probem, whenever I submit the form It only passes the value from the input and not the dropdown.
It's weird because when I click inspect element on the form it show that each option has a value and a label.
My code is simple, I have a form that has an input and a dropdown, I get an int from the input and a value from the dropdown and it creates an element via POST request, but that happens in the background since I only pass the parameters here.
I am using the Redux Form library for my form controls
here's my code:
import React from 'react';
import {reduxForm, Field} from 'redux-form';
import {Input} from 'reactstrap';
import {connect} from 'react-redux';
import { renderField } from '../form';
import {ticketAdd} from "../actions/actions";
import {Message} from "./Message";
const mapDispatchToProps = {
ticketAdd
};
class AmendeForm extends React.Component {
onSubmit(values) {
const { ticketAdd, parkingId } = this.props;
return ticketAdd(parseInt(values.matricule),parkingId,parseInt(values.montant));
}
render() {
const { handleSubmit, submitting, voitureList } = this.props;
console.log(voitureList);
if (null === voitureList) {
return (<Message message="Pas de voitures"/>);
}
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Input type="select" name="matricule" id="exampleSelect" label="Matricule">
{
voitureList.map(voiture => {
return (
<option value={voiture.id} key={voiture.id}>{voiture.matricule}</option>
);
})
}
</Input>
<Field name="montant" type="number" label="Montant" component={renderField}/>
<button type="submit" className="btn btn-primary btn-big btn-block" disabled={submitting}>Ajouter ticket</button>
</form>
)
}
}
export default reduxForm({
form: 'AmendeForm'
})(connect(null, mapDispatchToProps)(AmendeForm))

It's because your dropdown form field isn't wrapped by redux-form <Field /> component.
You have to create a custom dropdown Component (let's name it <Dropdown />) and later pass it to the Field as follows:
<Field name='matricule' component={Dropdown} />
Keep in mind that in your <Dropdown /> component, you have to adapt the props passed down by <Field /> with your custom Dropdown props. For example, <Field /> will pass down input prop, which includes itself onChange, onBlur and other handlers, these should be passed down to your custom Dropdown too.
Here's a basic example how to create such a custom Dropdown component:
const Dropdown = ({ input, label, options }) => (
<div>
<label htmlFor={label}>{label}</label>
<select {...input}>
<option>Select</option>
{ options.map( o => (
<option key={o.id} value={o.id}>{o.label}</option>
)
)}
</select>
</div>
)
Usage
const options = [
{ id: 1, label: 'Example label 1' },
{ id: 2, label: 'Example label 2' }
]
<Field name='marticule' component={Dropdown} options={options} />
For advanced use-cases, please refer to the docs.
Also, you're using reactstrap, and here's a discussion of how to create such custom Dropdown component, which is adapted with redux-form.

Related

Why default value in the input is setting after type any button on the keyboard

I have an input in my app where I'm trying to set a default value using function and later send that value to the server. The value is set only when I click any button from the keyboard. If I don't do that, value is empty and I can't send it to the server. I want to set that default value without clicking anything. Any idea?
The input is:
<div>
<input
className="form-control"
name="frequency"
value={this.state.frequency}
onChange={this.handleConstFrequency}
placeholder={'30'}
/>
</div>
handleConstFrequency function:
handleConstFrequency = () => {
this.setState({ frequency: '30' })
}
You could use React Hooks and define your state and then update it.
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
export default function App() {
const [frequency, setFrequency] = useState(20);
const handleConstFrequency = (e) => {
setFrequency("30"); // Or override with value e.target.value
};
return (
<>
<div>
<input
className="form-control"
name="frequency"
value={frequency}
onChange={handleConstFrequency}
placeholder={"20"}
/>
</div>
</>
);
}

Form submit for react-country-region-selector using Redux

I have to implement country-region selection (dropdown) in ReactJS.
I have used react-country-region-selector and created a component CountryRegion which has the CountryDropDown and RegionDropDown implementation.
My app uses redux-form. I need to pass the user selected values for counry and region to the parent form in which I am using the CountryRegion component.
I tried making using of redux-form "Fields" but it throws this error:
Uncaught TypeError: Cannot read property 'onChange' of undefined.
This is the CountryRegion.jsx -
import React, { Component } from 'react';
import 'react-select/dist/react-select.css'
import 'react-virtualized/styles.css'
import 'react-virtualized-select/styles.css'
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
class CountryRegion extends Component {
constructor (props) {
super(props);
this.state = { country: '', region: '' };
}
selectCountry (val) {
this.setState({ country: val });
}
selectRegion (value) {
this.setState({ region: value });
}
render () {
const {input, name, className} = this.props;
const {country, region } = this.state;
return (
<div>
<label className={"col-form-label"}>Work Country</label>
<CountryDropdown class="form-control" name="COUNTRY"
value={country}
valueType="short" priorityOptions={["US","CA"]} showDefaultOption={false}
onChange={(val) => {input.onChange(this.selectCountry(val)); }}/>
<label className={"col-form-label"}>Work State / Province</label>
<RegionDropdown class="form-control" name="STATE"
country={this.state.country}
value={this.state.region}
valueType="short"
countryValueType="short"
showDefaultOption={false}
onChange={(value) => {input.onChange(this.selectRegion(value));}}/>
</div>
);
}
}
export default CountryRegion;
This is how I am referring the CountryRegion code in parent form:
{<Fields names={[COUNTRY,STATE]} component={CountryRegion}/>}
How do I bind the value from the two drop downs to form attribute or Fields in redux form every time user selects or changes the dropdown values?
I used props to bind the properties for form submission. The code is below for onchange method-
<RegionDropdown className="form-control" name="STATE"
country={country}
value={region}
valueType="short"
countryValueType="short"
showDefaultOption={true}
defaultOptionLabel={"Select State"}
onChange={(value) =>
{this.props.state.input.onChange(this.selectRegion(value));}}/>

Semantic UI react - implement popup on dropdown select

I have a dropdown with a few options using semantic-ui-react. I want to be able to show the user a brief description on the choice they made after selection from the dropdown. Semantic also has a <Popup/> module that I've been trying to use along with <Dropdown/> to make this work.
I'm looking through the prop list of the dropdown module and don't see anything in particular that fits my case. I've tried using dropdown inside of popup, but with no luck.
Sandbox with the example: https://codesandbox.io/s/5zo52qyrxk
import React from "react";
import ReactDOM from "react-dom";
import { Dropdown, Popup, Input } from "semantic-ui-react";
import "semantic-ui-css/semantic.min.css";
import "./styles.css";
const offsetOptions = [
{
text: "fromEarliest",
value: "fromEarliest"
},
{
text: "fromLatest",
value: "fromLatest"
}
];
const DropdownExample = () => (
<Dropdown
placeholder="Select offset position"
clearable
fluid
selection
options={offsetOptions}
header=" Something about offset "
/>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
offset: ""
};
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div>
<fieldset>
<h1> Implement Popup on this Dropdown - semantic ui </h1>
<DropdownExample />
</fieldset>
</div>
</form>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
If you're trying to show a popup on each dropdown option, then you can use the subcomponent API to create the dropdown options rather than using the options prop.
<Dropdown text='File'>
<Dropdown.Menu>
<Popup trigger={<Dropdown.Item text='Close' description="Close" value="Close" />} content="Hello there" />
<Popup trigger={<Dropdown.Item text='Open' description='Open' value="Open" />} content="Open me"/>
{/* more options would go here... */}
</Dropdown.Menu>
</Dropdown>
There is a warning on the Semantic-UI-React site that states
Dropdown state is not fully managed when using the subcomponent API. The shorthand props API fully manages state but needs to be extended to support the markup shown here.
So I would take this suggestion with a grain of salt, but it does seem to accomplish what you're looking for.
Here is a solution that allows you to use a <Popup/> inside your <Dropdown.Item/> while still using the shorthand props like options, value and onChange:
// Main component
const DropdownWithPopups = ({ className, options, value, onChange, ...props }) => {
return (
<Dropdown
className='dropdown-with-popups'
selection
options={options}
value={value}
onChange={onChange}
{...props}
/>
);
};
// Popup that will be inside the default <Item/>
const ItemPopup = ({ text, popupContent }) => {
return (
<Popup
basic
position='left center'
hoverable
pinned
trigger={(
<div className='item-popup-trigger'>
{text}
</div>
)}
content={(
<div className='item-popup-content'>
{popupContent}
</div>
)}
popperModifiers={{
preventOverflow: {
boundariesElement: 'window',
},
}}
/>
);
};
// What your options should look like
const getOptions = (optionsData) => {
return _.map(optionsData, (option) => {
return {
value: option.value,
// `text` so the selected option is displayed
text: option.text,
// `children`is incompatible with `text` prop, so Semantic UI React will throw a warning,
// but it works as expected
children: (
<ItemPopup
text={option.text}
// whatever you need to render inside your Popup
popupContent={...}
/>
),
};
});
};
However I only tested this on an old version of semantic-ui-react (v0.88.2)
Actually there is another way to render items of dropdown with popups with using additional parameter content during data mapping
For example you have some received data to place in dropdown, then map of options would be:
const options = data.map(elem => ({
key: elem.id,
text: elem.name,
value: elem.name,
content: (
<Popup
content={ elem.example }
trigger={ <div>{ elem.name }</div> } />
),
})
this.setState({
dropdown: {
options: options
}
})
Then pass options as <Dropdown /> parameter:
<Dropdown
fluid
selection
placeholder="Data"
options={ this.state.dropdown.options }
value={ this.state.dropdown.selected } />

Update one Field based on another

As a part of my form I have a field for selecting items and a button which the client clicks in order to add the items to their list of units. I wish to show the list of units selected by the user in the units text field. How do I implement the onclick function for the button so that it takes the currently selected value for the select component before appending it to the list as presented by the textbox? I have a separate save button which will take the form data and send it to the backend when clicked.
let EditUserForm = (props) => {
const { handleSubmit, units } = props;
return (
<form onSubmit={handleSubmit}>
<Field name="units" component="input" type="text" readOnly/>
<Field name="unit" component="select" >
{
units.map(u => <option value={u} key={u}>{u}</option>)
}
</Field>
<button type="button" onClick={props.updateUserUnits}>Add unit</button>
</form>
);
};
You could do something like in the code below. This combines several redux-form concepts.
Basically you want to intercept the onChange event from the select component, so you can attach some logic to it.
In this case we'll use the change prop that is passed down by redux-form. This will allow you to change the value of another field in the form.
Combine this with the formValueSelector which allows you to get a value from a specific form field.
import React from 'react'
import { connect } from 'react-redux';
import { Field, FieldArray, formValueSelector, reduxForm } from 'redux-form'
const EditUser = (props) => {
const { change, handleSubmit, selectedUnits = [], units } = props;
const handleUnitChange = (event, value) => {
const newUnits = [ ...selectedUnits, value ];
change('units', newUnits);
}
return (
<form onSubmit={handleSubmit}>
<Field name="units" component="input" type="text" readOnly/>
<Field name="unit" component="select" onChange={handleUnitChange}>
{units.map(u => <option value={u} key={u}>{u}</option>)}
</Field>
<button type="button" onClick={props.sendData}>Add unit</button>
</form>
);
};
const form = 'editUserForm';
const EditUserForm = reduxForm({
form
})(EditUser);
const selector = formValueSelector(form);
export default connect(state => ({
selectedUnits: selector(state, 'units')
}))(EditUserForm);

Could not type any text inside textfield

I am using redux-form-material-ui for my form. With below code, i cannot type anything on the textfield. I mean to say nothing gets type in the textfield. There are two textfield in my form and one Autocomplete. One is for device name and another for timeout value. Autocomplete works though. Why is it not showing the text i have typed?
What might be the reason for this issue? Have i done somewhere mistake?
Please see an image attached. I cant write anything in device name and timeout input box. Only autocomplete box can be selected and is shown on input box.
Here is my code
import React, { Component } from 'react';
import _ from 'lodash';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import {
AutoComplete as MUIAutoComplete, MenuItem,
FlatButton, RaisedButton,
} from 'material-ui';
import {
AutoComplete,
TextField
} from 'redux-form-material-ui';
const validate = values => {
const errors = {};
const requiredFields = ['deviceName', 'deviceIcon', 'deviceTimeout'];
requiredFields.forEach(field => {
if (!values[field]) {
errors[field] = 'Required';
}
});
return errors;
};
class DeviceName extends Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.handleNext();
}
render() {
const {
handleSubmit,
fetchIcon,
stepIndex,
handlePrev,
pristine,
submitting
} = this.props;
return (
<div className="device-name-form">
<form>
<div>
<Field
name="deviceName"
component={TextField} {/* cannot type */}
floatingLabelStyle={{ color: '#1ab394' }}
hintText="Device Name"
onChange={(e) => this.setState({ deviceName: e.target.name })}
/>
</div>
<div>
<Field
name="deviceIcon"
component={AutoComplete} {/* works */}
hintText="icon"
openOnFocus
filter={MUIAutoComplete.fuzzyFilter}
className="autocomplete"
dataSource={listOfIcon}
onNewRequest={(e) => { this.setState({ deviceIcon: e.id }); }}
/>
</div>
<div>
<Field
name="deviceTimeout"
component={TextField} {/* cannot type */}
floatingLabelStyle={{ color: '#1ab394' }}
hintText="Device Timeout"
ref="deviceTimeout" withRef
onChange={(e) => this.setState({ deviceTimeout: e.target.name })}
/>
</div>
<div style={{ marginTop: 12 }}>
<RaisedButton
label={stepIndex === 4 ? 'Finish' : 'Next'}
primary
disabled={pristine || submitting}
className="our-btn"
onTouchTap={(e) => handleSubmit(e)}
/>
</div>
</form>
</div>
);
}
}
const mapStateToProps = ({ fetchIcon }) => ({
fetchIcon
});
const DeviceForm = reduxForm({
form: 'DeviceForm',
validate,
})(DeviceName);
export default connect(mapStateToProps)(DeviceForm);
By adding onChange to your Fields, aren't you preventing redux form from accepting the new values from that input field? Is there a reason you are attempting to add these to your Component state?
The examples in the documentation certainly suggest you should not need to do this - http://redux-form.com/6.1.1/examples/material-ui/
I guess you can simplify your form as-
class DeviceName extends Component {
handleSubmit = (values) => {
console.log(values); // Do something with values
}
render() {
const {
....
handleSubmit //***Change
} = this.props;
return (
<div className="device-name-form">
<form onSubmit={handleSubmit(this.handleSubmit)}>
<div>
<Field
name="deviceName"
component={TextField} {/* cannot type */}
floatingLabelStyle={{ color: '#1ab394' }}
hintText="Device Name"
//*** line removed
/>
</div>
.....
.....
<div style={{ marginTop: 12 }}>
<RaisedButton
type="submit" // setting type
label={stepIndex === 4 ? 'Finish' : 'Next'}
primary
disabled={pristine || submitting}
className="our-btn"
/>
</div>
</form>
</div>
);
}
}
I would guess that the problem is in your onChange handlers - you probably need to use target.value rather than target.name:
onChange={(e) => this.setState({ deviceTimeout: e.target.value})
I had the same issue, turned out I used the wrong import. I was using the normal import:
import { Field, reduxForm } from 'redux-form'
Because my store is immutable (using Immutable.js) I had to use the immutable import:
import { Field, reduxForm } from 'redux-form/immutable'
Some kind of error logging for redux-form would have been handy in this case.

Categories

Resources