I have JSON file like this
[
{
"id": 1,
"country": "Afghanistan",
"city": ["Eshkashem","Fayzabad","Jurm","Khandud"]
},
{
"id": 2,
"country": "Italy",
"city": ["Milano","Rome","Torino","Venezia"]
}
]
and I want to iterate through array placed in the city. Idea is to have two selects, where the first select is reserved for countries and the second is reserved for cities. Whenever the user selects a country, I want to populate the second select with a list of cities. Problem is that I receive only one array of all cities for that country. Here is my code:
export default class DiffCountries extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
contacts: [],
selectedCountry: [],
selectedCity: []
}
}
onChangeHandler = (event) => {
const test = CountriesData[event.target.value - 1];
this.setState({
selectedCountry: test,
selectedCity: this.state.selectedCountry.city
})
console.log(this.state.selectedCity);
}
render() {
const { contacts } = this.state;
return (
<div>
<select name="" id="" onChange={this.onChangeHandler}>
{CountriesData.map(item => {
const { id, country } = item;
return <option key={id} value={id}>{country}</option>
})}
</select>
<select name="" id="">
{this.state.selectedCountry !== undefined ?
<option value="">{this.state.selectedCountry.city}</option> :
null
}
</select>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
And here is the screenshot of my problem
Thank you in advance!
You need to use map() on the city array.
<select name = "" id = "" > {
this.state.selectedCountry !== undefined ?
this.state.selectedCountry.city.map((x,i) => <option value={x} key={i}>{x}</option>)
:null
}
</select>
You need to iterate through the array.
this.state.selectedCountry.city.map((city, index) => {
return <option value={city} key={index}>{city}</option>
})
Be aware, that using the index as a key is considered an anti pattern. You could use the name of the city as a key as well. E.g.:
this.state.selectedCountry.city.map(city => {
return <option value={city} key={city}>{city}</option>
})
edit to add link to mdn docs as suggested in comments: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Example:
const CountriesData = [
{
id: 1,
country: 'Afghanistan',
city: ['Eshkashem', 'Fayzabad', 'Jurm', 'Khandud'],
},
{
id: 2,
country: 'Italy',
city: ['Milano', 'Rome', 'Torino', 'Venezia'],
},
];
class DiffCountries extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedCountry: null,
};
}
onChangeHandler = event => {
const selectedCountry = CountriesData[event.target.value - 1];
this.setState({
selectedCountry,
});
};
render() {
const { selectedCountry } = this.state;
return (
<div>
<select
name="country"
defaultValue="country"
onChange={this.onChangeHandler}
>
<option disabled value="country">
Select country
</option>
{CountriesData.map(({ id, country }) => (
<option key={id} value={id}>
{country}
</option>
))}
</select>
{selectedCountry && (
<select name="city" defaultValue="city">
<option disabled value="city">
Select city
</option>
{selectedCountry.city.map(item => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
)}
</div>
);
}
}
ReactDOM.render(<DiffCountries />, document.getElementById('container'));
Related
I'm creating JSX element with the onClick on the button:
<div onClick={() => addWeek()} >
add week
</div>
then I update state (add new items to array) :
//my state that has one object initially (I want to add more)
const [weekly, setweekly] = useState([
{
id: 'n1',
day_week: null,
start_time: null,
end_time: null,
},
]);
const addWeek = () => {
setweekly([
...weekly,
{
id: `n${weekly.length + 1}`,
day_week: null,
start_time: null,
end_time: null,
},
]);
}
after I create JSX element I have an onChange event on that element :
NOTE: This element created with onClick and I have two objects inside my state now.
<select
onChange={(event) => handleWeekly(event)}
id={`n${weekly.length + 1}`}
>
//Some options
</select>
but in here I can't access the updated state I get one object.
const handleWeekly = (event) => {
// I get one object
console.log(weekly);
};
CODE SAND BOX :
https://codesandbox.io/s/strange-nightingale-l6qg3?file=/src/App.js:0-1372
I would approach this problem differently. Instead of putting markup in the state you can map through your data and render your components this way:
export default function App() {
const [weeks, setWeek] = useState([
{
id: "n1",
day_week: null,
start_time: null,
end_time: null
}
]);
const addWeek = () => {
setWeek([
...weeks,
{
id: `n${weeks.length + 1}`,
day_week: null,
start_time: null,
end_time: null
}
]);
};
const handleWeekly = (event) => {
console.log(weeks);
};
return (
<div className="App">
<div onClick={() => addWeek()}>add week</div>
{weeks.map((week) => {
return (
<select
onChange={(event) => handleWeekly(event)}
id={`n${week.id}`}
key={`n${week.id}`}
>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
);
})}
</div>
);
}
It's easier to keep track of your state this way.
Sandbox Example
I created a form with redux-form. It works correctly but if I don't change select (I'll leave the default value), the value won't send, but if I change the value it will be sent in post method.
export const mySelect = (props) => {
const mySelectOptions = (key, index) => {
return (
<option key={props.options[key].id} value={props.options[key].id}>
{props.options[key].name}
</option>
);
};
if (props && props.options) {
return (
<div className="mv3 w-100">
<div className="b sans-serif pv2 w-100">{props.label}</div>
<select
{...props.input}
className="pa2 input-reset ba b--black-40 w-100"
>
{Object.keys(props.options).map(renderSelectOptions)}
</select>
</div>
);
}
return <div></div>;
};
Example
<Field
name="gender"
label="gender"
component={input.mySelect}
options={[
{
id: 1,
name: "male",
},
{
id: 2,
name: "female",
},
]}
/>;
I tried to set a default value in my SQL table but every time it sends null. Do you know how can I solve my problem?
Hello I am trying to render out options based on an array that I have in my state using map but I get undefined when using it in the return
Here is the array
this.state = { countries: ["Australia","Brazil","China","Sweden"]}
I try to use it as such
this.state.countries.map((country, i) =>
<option value={this.state.countries[i]}>{this.state.countries[i]}</option>)
but I get i is undefined. I also try to use country only but always get undefined. But if i in my render do like this
this.state.countries.map((country, i) => console.log(country + " " + i));
I get it perfect both the country and the i is correct. Why cant i use the values in the return?
Here is the whole component
class BoxForm extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
weight: "",
color: "",
country: "",
countries: ["Australia","Brazil","China","Sweden"]
}
this.handleNameChange = this.handleNameChange.bind(this);
this.handleWeightChange = this.handleWeightChange.bind(this);
}
handleNameChange(e) {
this.setState({ name: e.target.value });
}
handleWeightChange(e) {
this.setState({ weight: e.target.value });
}
render() {
this.state.countries.map((country, i) => console.log(country + " " + i));
return (
<div className="formGroup">
<form>
<FormGroup>
<h4 className="nameHeader">Name</h4>
<FormControl
className="textfield"
type="text"
value={this.state.name}
placeholder="Enter text"
onChange={this.handleNameChange}
/>
<h4 className="weightHeader">Weight</h4>
<FormControl
className="textfield"
type="number"
value={this.state.weight}
placeholder="Enter text"
onChange={this.handleWeightChange}
/>
<h4 className="colorHeader">Box Color</h4>
<FormControl
className="textfield"
type="text"
value={this.state.color}
placeholder="Enter text"
/>
<h4 className="destinationHeader">Destination Country</h4>
<FormControl
componentClass="select"
className="textfield"
type="dropdown"
value={this.state.country}
placeholder="Enter text"
>
this.state.countries.map((country, i) =>
<option value={this.state.countries[i]}>{this.state.countries[i]}</option>)
</FormControl>
</FormGroup>
</form>
</div>
);
}
}
export default BoxForm;
You need to wrap your returned from map array with {}
<FormControl
componentClass="select"
className="textfield"
type="dropdown"
value={this.state.country}
placeholder="Enter text"
>
{this.state.countries.map((country, i) => (
<option value={country}>{country}</option>
))}
</FormControl>;
Please check the working select option component created :).
https://codesandbox.io/s/xjpw5wq65w
import React, { Component } from "react";
class SelectOptionComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "",
countries: ["Australia", "Brazil", "China", "Sweden"]
};
}
handleChange = e => {
const { value } = e.target;
this.setState({
value: value
});
console.log("Selected Item", value);
};
renderData() {
return this.state.countries.map((item, index) => {
return (
<option value={item} key={index}>
{item}
</option>
);
});
}
render() {
return (
<select value={this.state.value} onChange={this.handleChange}>
{this.renderData()}
</select>
);
}
}
export default SelectOptionComponent;
I am updating data from by getting it into form. In form I am getting data from Database. The problem is when I am getting data in selectbox and want to show default selected value if it is match with database field data.
<select disabled ref = {(input)=> this.menu = input} value={this.state.category} onChange={this.handleCat} className="form-control" name="category" data-toggle="tooltip" data-trigger="hover"className="form-control tooltips" title="Select Course Category">
<option disabled>Select Category</option>
{
this.state.categoryData.map((item, index) =>(
<option key={index} value={item._id}>{item.category}</option>
))
}
</select>
You can write value={defaultValue} on the select tag itself:
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {
options: [
{
value: "Grapefruit",
default: false
},
{
value: "Lime",
default: true
},
{
value: "Coconut",
default: false
},
{
value: "Mango",
default: false
}
]
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.getDefaultValue = this.getDefaultValue.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
getDefaultValue() {
return this.state.options.filter( ( el ) => {
return el.default == true;
} )[0].value;
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Pick your favorite La Croix flavor:
<select value={this.getDefaultValue()} onChange={this.handleChange}>
{
this.state.options.map( (el, index) => {
return( <Option value={el.value} key={index} /> );
} )
}
</select>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
const Option = ( props ) => (
<option value={props.value}>{props.value}</option>
);
ReactDOM.render(
<FlavorForm />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Read more here.
The example code in the react-bootstrap site shows the following. I need to drive the options using an array, but I'm having trouble finding examples that will compile.
<Input type="select" label="Multiple Select" multiple>
<option value="select">select (multiple)</option>
<option value="other">...</option>
</Input>
You can start with these two functions. The first will create your select options dynamically based on the props passed to the page. If they are mapped to the state then the select will recreate itself.
createSelectItems() {
let items = [];
for (let i = 0; i <= this.props.maxValue; i++) {
items.push(<option key={i} value={i}>{i}</option>);
//here I will be creating my options dynamically based on
//what props are currently passed to the parent component
}
return items;
}
onDropdownSelected(e) {
console.log("THE VAL", e.target.value);
//here you will see the current selected value of the select input
}
Then you will have this block of code inside render. You will pass a function reference to the onChange prop and everytime onChange is called the selected object will bind with that function automatically. And instead of manually writing your options you will just call the createSelectItems() function which will build and return your options based on some constraints (which can change).
<Input type="select" onChange={this.onDropdownSelected} label="Multiple Select" multiple>
{this.createSelectItems()}
</Input>
My working example
this.countryData = [
{ value: 'USA', name: 'USA' },
{ value: 'CANADA', name: 'CANADA' }
];
<select name="country" value={this.state.data.country}>
{this.countryData.map((e, key) => {
return <option key={key} value={e.value}>{e.name}</option>;
})}
</select>
bind dynamic drop using arrow function.
class BindDropDown extends React.Component {
constructor(props) {
super(props);
this.state = {
values: [
{ name: 'One', id: 1 },
{ name: 'Two', id: 2 },
{ name: 'Three', id: 3 },
{ name: 'four', id: 4 }
]
};
}
render() {
let optionTemplate = this.state.values.map(v => (
<option value={v.id}>{v.name}</option>
));
return (
<label>
Pick your favorite Number:
<select value={this.state.value} onChange={this.handleChange}>
{optionTemplate}
</select>
</label>
);
}
}
ReactDOM.render(
<BindDropDown />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root">
<!-- This element's contents will be replaced with your component. -->
</div>
// on component load, load this list of values
// or we can get this details from api call also
const animalsList = [
{
id: 1,
value: 'Tiger'
}, {
id: 2,
value: 'Lion'
}, {
id: 3,
value: 'Dog'
}, {
id: 4,
value: 'Cat'
}
];
// generage select dropdown option list dynamically
function Options({ options }) {
return (
options.map(option =>
<option key={option.id} value={option.value}>
{option.value}
</option>)
);
}
<select
name="animal"
className="form-control">
<Options options={animalsList} />
</select>
Basically all you need to do, is to map array. This will return a list of <option> elements, which you can place inside form to render.
array.map((element, index) => <option key={index}>{element}</option>)
Complete function component, that renders <option>s from array saved in component's state. Multiple property let's you CTRL-click many elements to select. Remove it, if you want dropdown menu.
import React, { useState } from "react";
const ExampleComponent = () => {
const [options, setOptions] = useState(["option 1", "option 2", "option 3"]);
return (
<form>
<select multiple>
{ options.map((element, index) => <option key={index}>{element}</option>) }
</select>
<button>Add</button>
</form>
);
}
component with multiple select
Working example: https://codesandbox.io/s/blue-moon-rt6k6?file=/src/App.js
A 1 liner would be:
import * as YourTypes from 'Constants/YourTypes';
....
<Input ...>
{Object.keys(YourTypes).map((t,i) => <option key={i} value={t}>{t}</option>)}
</Input>
Assuming you store the list constants in a separate file (and you should, unless they're downloaded from a web service):
# YourTypes.js
export const MY_TYPE_1="My Type 1"
....
You need to add key for mapping otherwise it throws warning because each props should have a unique key. Code revised below:
let optionTemplate = this.state.values.map(
(v, index) => (<option key={index} value={v.id}>{v.name}</option>)
);
You can create dynamic select options by map()
Example code
return (
<select className="form-control"
value={this.state.value}
onChange={event => this.setState({selectedMsgTemplate: event.target.value})}>
{
templates.map(msgTemplate => {
return (
<option key={msgTemplate.id} value={msgTemplate.text}>
Select one...
</option>
)
})
}
</select>
)
</label>
);
I was able to do this using Typeahead. It looks bit lengthy for a simple scenario but I'm posting this as it will be helpful for someone.
First I have created a component so that it is reusable.
interface DynamicSelectProps {
readonly id: string
readonly options: any[]
readonly defaultValue: string | null
readonly disabled: boolean
onSelectItem(item: any): any
children?:React.ReactNode
}
export default function DynamicSelect({id, options, defaultValue, onSelectItem, disabled}: DynamicSelectProps) {
const [selection, setSelection] = useState<any[]>([]);
return <>
<Typeahead
labelKey={option => `${option.key}`}
id={id}
onChange={selected => {
setSelection(selected)
onSelectItem(selected)
}}
options={options}
defaultInputValue={defaultValue || ""}
placeholder="Search"
selected={selection}
disabled={disabled}
/>
</>
}
Callback function
function onSelection(selection: any) {
console.log(selection)
//handle selection
}
Usage
<div className="form-group">
<DynamicSelect
options={array.map(item => <option key={item} value={item}>{item}</option>)}
id="search-typeahead"
defaultValue={<default-value>}
disabled={false}
onSelectItem={onSelection}>
</DynamicSelect>
</div>