Here in my array there are 5 values that I am getting in "Options" props , but I want to set default value Please Select ,that I have defined in state.
And trying to get in Value props . But its getting error . Label of undefined
Basically when I open my page in drop down "Please Select" and after that that array value should come .
Please suggest .
below select field I have created separate component .
<AndroidPicker
mode="dropdown"
iosIcon={<Icon name="chevron-down" type="MaterialCommunityIcons"/>}
placeholder=""
placeholderStyle={{ color: "#bfc6ea" }}
placeholderIconColor="#007aff"
style={{ width: '100%' }}
selectedValue={value}
onValueChange={(e) => onChange.bind(that, this.props, e)()}
>
{
options.map((item, indx) => {
let { label, value } = item;
return <AndroidPicker.Item key={indx} label={label} value={value}/>;
})
}
</AndroidPicker>
this.state={
religionValue:'Please Select'
}
religion: Array(5)
0: {name: "Hindu", code: "H", __typename: "TroubleTicketMasterCode"}
1: {name: "Christian", code: "C", __typename: "TroubleTicketMasterCode"}
2: {name: "ISLAM", code: "I", __typename: "TroubleTicketMasterCode"}
3: {name: "Others", code: "O", __typename: "TroubleTicketMasterCode"}
4: {name: "Not Disclosed", code: "ND", __typename: "TroubleTicketMasterCode"}
religionChanged = (key, val) => {
this.handlereligionChanged({ field: "religionValue" }, val);
};
handlereligionChanged = (props, e) => {
let tempObj = this.state.religionValue;
tempObj[props.field] = e;
this.setState({ religionValue: e });
};
let religionOptions = [];
religion.map(({ code: value, name: label }) => {
religionOptions.push({ value, label });
});
<SelectField
label="Religion"
value={this.state.religionValue}
options={religionOptions}
node="Religion"
onChange={this.religionChanged}
that={this}
setIcon={true}
/>
Use the SelectField attribute called defaultValue. Check out the docs at react-md site
Related
I am making a simple react application where there are dropdowns in which one dependent on another.
-> Here dropdown 1 has the value as type of game like Indoor and Outdoor.
-> Here dropdown 2 has the value as type of sport like Chess , Tennis and Football .
Requirement:
The following different use cases needs to be covered,
Scenarios:
-> User selects Indoor from dropdown 1, then in dropdown 2 only the value of Chess needs to be enabled and others needs to be
disabled.
-> User selects Outdoor from dropdown 1, then in dropdown 2 only the value of Tennis and Football needs to be enabled and option Chess
needs to be disabled.
Vice versa:
-> User selects Chess from dropdown 2, then in dropdown 1 only the value of Indoor needs to be enabled and others needs to be
disabled.
-> User selects Tennis or Football from dropdown 2, then in dropdown 1 only the value of Outdoor needs to be enabled and others needs to be disabled.
Here we provide option of allowClear so that user can reset their selection in any select box selection (the close icon) and do the above mentioned scenario in any way like selecting option from first dropdown or in second dropdown based on which the another dropdown make the option enable or disable.
Right now I have a data like this and open for modification to achieve the expected result.
const data = {
games: {
type: [
{ id: 1, value: "Indoor", sportId: [2] },
{ id: 2, value: "Outdoor", sportId: [1, 3] }
],
sport: [
{ id: 1, value: "Tennis", typeId: [2] },
{ id: 2, value: "Chess", typeId: [1] },
{ id: 3, value: "Football", typeId: [2] }
]
}
}
The property names may vary so I cannot rely on the hard coded/static name inside code like data.games.type or data.games.sport.
And hence I tried with dynamic approach like,
{Object.entries(data.games).map((item, index) => {
return (
<div className="wrapper" key={index}>
<h4> {item[0]} </h4>
<Select
defaultValue="selectType"
onChange={handleChange}
allowClear
>
<Option value="selectType"> Select {item[0]} </Option>
{item[1].map((option, j) => (
<Option key={j} value={option.value}>
{option.value}
</Option>
))}
</Select>
<br />
</div>
);
})}
Reactjs sandbox:
Note: The options needs to be disabled (only) and should not be removed from select box as user can clear any select box selection and
select value from any of the dropdown.
Pure Javascript Approach: (Ignore reset of dropdown in this JS example which handled in reactjs with help of clear icon (close icon))
Also here is the Pure JS (working) way of approach tried with hard coded select boxes with id for each element respectively and also with some repetition of code in each addEventListener,
const data = {
games: {
type: [
{ id: 1, value: "Indoor", sportId: [2] },
{ id: 2, value: "Outdoor", sportId: [1, 3] }
],
sport: [
{ id: 1, value: "Tennis", typeId: [2] },
{ id: 2, value: "Chess", typeId: [1] },
{ id: 3, value: "Football", typeId: [2] }
]
}
}
const typeSelect = document.getElementById('type')
const sportSelect = document.getElementById('sport')
const createSelect = (values, select) => {
values.forEach(t => {
let opt = document.createElement('option')
opt.value = t.id
opt.text = t.value
select.append(opt)
})
}
createSelect(data.games.type, typeSelect)
createSelect(data.games.sport, sportSelect)
typeSelect.addEventListener('change', (e) => {
const val = e.target.value
const type = data.games.type.find(t => t.id == val)
Array.from(sportSelect.querySelectorAll('option')).forEach(o => o.disabled = true)
type.sportId.forEach(sId =>
sportSelect.querySelector(`option[value="${sId}"]`).disabled = false)
})
sportSelect.addEventListener('change', (e) => {
const val = e.target.value
const sport = data.games.sport.find(s => s.id == val)
Array.from(typeSelect.querySelectorAll('option')).forEach(o => o.disabled = true)
sport.typeId.forEach(sId =>
typeSelect.querySelector(`option[value="${sport.typeId}"]`).disabled = false)
})
<select id="type"></select>
<select id="sport"></select>
Could you please kindly help me to achieve the result of disabling the respective options from respective select box based on the conditions mentioned in the above mentioned scenario's in pure reactjs way?
For the comment given by #Andy, there is a reset option available in the select I am using, with close icon, so using that user can clear the select box and select the other dropdown option. This option is provided under allowClear in the antd select . Kindly please see the select box that I have in the above codesandbox, it has clear icon in the last.
Here's what I have as a working solution with my understanding of your question. You want dynamic options that can easily validate against other dynamic options. It's about the best I could come up with that wasn't completely unmaintainable. It's about 98% dynamic but for the validation purposes some properties do need to be defined.
Example:
Setup the interfaces and types
interface IState { // <-- need to be known
type: number;
sport: number;
}
interface IOption {
id: number;
value: string;
valid: Record<keyof IState, number[]>;
}
type Valid = "sport" & "type"; // <-- this needs to be known
interface Data {
games: {
[key: string]: Array<Record<Valid, IOption[]>>;
};
}
Data
const data: Data = {
games: {
type: [
{ id: 1, value: "Indoor", valid: { sport: [2] } },
{ id: 2, value: "Outdoor", valid: { sport: [1, 3] } }
],
sport: [
{ id: 1, value: "Tennis", valid: { type: [2] } },
{ id: 2, value: "Chess", valid: { type: [1] } },
{ id: 3, value: "Football", valid: { type: [2] } }
],
}
};
Create component state to hold the selected option values. These should match the known selection types in the data. The idea here is that we are converting the select inputs to now be controlled inputs so we can validate options against selected state.
export default function App() {
const [state, setState] = React.useState<IState>({
type: -1,
sport: -1,
category: -1
});
const changeHandler = (key: keyof IState) => (value: number) => {
setState((state) => ({
...state,
[key]: value
}));
};
This is the meat of the addition. Validates options against currently selected state values according to the data configuration. Looks through each option's valid object and compares against current selected state. Returns if a current option is a valid selectable option or not.
const isValid = (key: keyof IState, option: IOption) => {
const { valid } = option;
return (Object.entries(valid) as [[keyof IState, number[]]]).every(
([validKey, validValues]) => {
const selectedValue = state[validKey];
if (!selectedValue || selectedValue === -1) return true;
return validValues.includes(state[validKey]);
}
);
};
return (
<>
<br />
{(Object.entries(data.games) as [[keyof IState, IOption[]]]).map(
([key, options]) => {
return (
<div className="wrapper" key={key}>
<h4>{key}</h4>
<Select
value={state[key] || -1}
onChange={changeHandler(key)}
allowClear
>
<Option disabled value={-1}>
Select {key}
</Option>
{options.map((option) => (
<Option
key={option.id}
value={option.id}
disabled={!isValid(key, option)} // if not valid, then disable
>
{option.value}
</Option>
))}
</Select>
<br />
</div>
);
}
)}
</>
);
}
https://codesandbox.io/s/react-typescript-forked-gt7gvy?file=/src/App.tsx
I added keeping state of chosen values in each select and conditional disabling of options in the select.
import "antd/dist/antd.min.css";
import { Select } from "antd";
import * as React from "react";
import "./styles.css";
const { Option } = Select;
const data = {
games: {
type: [
{ id: 1, value: "Indoor", sportId: [2] },
{ id: 2, value: "Outdoor", sportId: [1, 3] }
],
sport: [
{ id: 1, value: "Tennis", typeId: [2] },
{ id: 2, value: "Chess", typeId: [1] },
{ id: 3, value: "Football", typeId: [2] }
]
}
};
export default function App() {
const [category, setCategory] = React.useState(null);
const [sport, setSport] = React.useState(null);
const handleChange = (value: any, index: number) => {
console.log(value);
const valueToSet = value.startsWith("select") ? null : value;
if (index === 0) {
setCategory(valueToSet);
} else if (index === 1) {
setSport(valueToSet);
}
};
return (
<>
<br />
{Object.entries(data.games).map((item, index) => {
return (
<div className="wrapper" key={index}>
<h4> {item[0]} </h4>
<Select
defaultValue="selectType"
onChange={(value) => handleChange(value, index)}
allowClear
>
<Option value="selectType"> Select {item[0]} </Option>
{item[1].map((option, j) => (
<Option
key={j}
value={option.value}
disabled={
'typeId' in option && // index === 1 or just belong to sports
category &&
data.games.type.find((x) => x.value === category)?.id !==
option.typeId[0]
}
>
{option.value}
</Option>
))}
</Select>
<br />
</div>
);
})}
</>
);
}
In a list of certain records where I'm populating in the dropdown of an input component. The default value is set on page loading but, I need to update it on click event of a button. Refer to the code below.
Home.js
const allList = [
{ id: "1", value: "Fruits" },
{ id: "2", value: "Vegetables" },
{ id: "3", value: "Grains" },
{ id: "4", value: "Milk" },
{ id: "5", value: "Meat" },
{ id: "6", value: "Fish" },
{ id: "7", value: "Pulses" }
];
const [itemList, setItemList] = useState(allList);
const [newValue, setNewValue] = useState(allList[0].value)
// I want to set the value on click of cancel button
const handleCancel = (e) => {
setNewValue(allList[2].value)
setPopup(false);
};
return (
<>
<DataList
defaultValue={newValue}
list="itemListOptions"
id="itemList"
placeholder="Search/select items"
data={itemList}
onSelectionChange={itemChanged}
></DataList>
{popup === true ? (
<Popup okbtnClick={handleOK} canclebtnclick={handleCancel} />
) : null}
</>
)
DataList.js
<input
defaultValue={props?.defaultValue ?? ""}
className="form-control cpselect"
id={props?.id ?? ""}
list={props?.list ?? ""}
placeholder={props?.placeholder ?? ""}
onChange={props?.onSelectionChange ?? ""}
/>
<datalist key={props.id} id={props?.list ?? ""}>
{props.data.map((d) => {
return <option key={d.id} id={d.id} value={d.value}></option>;
})}
</datalist>
My intention is to change the defaultValue inside the input field, on click of cancel button. Here it loads the first element and on click event should load third element. What is the best optimal solution?
Please refer to the Codesandbox link: https://codesandbox.io/s/clever-rumple-ig0wwj
What you want to do is to use value instead of defaultValue.
defaultValue property is configured in such a way that it reads the prop only once and then creates an inner state that handles changes.
I have a window where a group of 3 HTML combo box(select box) are generated on every button click.
lets say category,subcategory and data. options in sub category and data are dynamically rendered based on its previous selected value.
i am storing the selected values as an array of object like
const [exerciseData, setExerciseData] = useState([[]]);
Sample data:
exerciseData : [
//first group
[
category:"",
subcategory:"",
name:""
],
//second group
[
category:"",
subcategory:"",
name:""
]
]
So literally first group of input can be indicated by exerciseData[0] and next by exerciseData[1].category etc..
The problem is i want there groups like to be reordered by the user like moving up 1 group or moving down by 1.So i need the select boxes to be a controlled element. But how can i set that?
I tried like this :
<select name="category" value={exerciseData[i].category} ... > {options} </select>
when i give like this the select box value cannot be changed.it always stays at its default value.
any suggestion will be appreciated
Thanks in advance
To make an input controlled, you need to manually handle the input value via state. In your example, the input value seems to be fixed/static.
The following snippet is an example of how to create a controlled select input in React.
Update:
Made a full demo snippet. Please let me know if that is what you was looking for.
const {useState, useCallback, Fragment} = React;
const categories = [
{
id: 'CAT.1',
data: [
{
id: 'CAT.1 SUB.A',
data: [
{id: 'CAT.1 SUB.A OPT.1'},
{id: 'CAT.1 SUB.A OPT.2'},
{id: 'CAT.1 SUB.A OPT.3'}
]
},
{
id: 'CAT.1 SUB.B',
data: [
{id: 'CAT.1 SUB.B OPT.1'},
{id: 'CAT.1 SUB.B OPT.2'},
{id: 'CAT.1 SUB.B OPT.3'}
]
}
]
},
{
id: 'CAT.2',
data: [
{
id: 'CAT.2 SUB.A',
data: [
{id: 'CAT.2 SUB.A OPT.1'},
{id: 'CAT.2 SUB.A OPT.2'},
{id: 'CAT.2 SUB.A OPT.3'}
]
},
{
id: 'CAT.2 SUB.B',
data: [
{id: 'CAT.2 SUB.B OPT.1'},
{id: 'CAT.2 SUB.B OPT.2'},
{id: 'CAT.2 SUB.B OPT.3'}
]
}
]
},
];
function Select(props) {
const {
name,
options = [],
onIndex
} = props;
const [value, setValue] = useState(options[0] && options[0].id);
const onChangeHandler = useCallback((e) => {
onIndex && onIndex(options.findIndex((item) => item.id === e.target.value));
setValue(e.target.value);
}, [onIndex, options]);
return (
<label>
{name}:
<select value={value} onChange={onChangeHandler}>
{
options.map((item) => {
const {id} = item;
return <option value={id}>{id}</option>;
})
}
</select>
</label>
);
}
function Field() {
const [catIndex, setCatIndex] = useState(0);
const [subIndex, setSubIndex] = useState(0);
return (
<div>
<Select name='Category' options={categories} onIndex={setCatIndex}/>
<Select name='Sub Category' options={categories[catIndex].data} onIndex={setSubIndex}/>
<Select name='Option' options={categories[catIndex].data[subIndex].data}/>
</div>
);
}
function App() {
const [fields, setFields] = useState(() => [<Field />]);
const addField = useCallback(() => {
setFields((prevFields) => {
const index = prevFields.lenght;
return [...prevFields, <Field key={index} />];
});
}, []);
return (
<Fragment>
<form>
{fields}
</form>
<button type='button' onClick={addField}>Add Field</button>
</Fragment>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
form {
display: flex;
flex-direction: column;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id='root'></div>
Please let me know if you have any doubt.
I have a scrolling menu items, and the titles of each item is hardcoded into a const, along side with the id
const list = [
{ name: "category1", id: 0 },
{ name: "category2", id: 1 },
{ name: "category3", id: 2 },
{ name: "category4", id: 3 },
{ name: "category5", id: 4 },
{ name: "category6", id: 5 },
{ name: "category7", id: 6 },
{ name: "category8", id: 7 }
];
I have a json file that contains the category name for each child:
{
"results": [
{
"category": "category1",
"name": {
"title": "mr",
"first": "ernesto",
"last": "roman"
},
"email": "ernesto.roman#example.com",
"id": {
"name": "DNI",
"value": "73164596-W"
},
"picture": {
"large": "https://randomuser.me/api/portraits/men/73.jpg",
"medium": "https://randomuser.me/api/portraits/med/men/73.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/men/73.jpg"
}
},
{
"category": "category2",
"name": {
"title": "mr",
"first": "adalbert",
"last": "bausch"
},
"email": "adalbert.bausch#example.com",
"id": {
"name": "",
"value": null
} etc....
I want to show these categories "category": "category1", as the titles of my menu, I now that I need to start stateless and add them from the JSON, the fetching part from the JSON is done locally in componentDidMount, but I am not sure how can I map them into appearing as menu names to make the menu dynamic, I basically want the same output but from the json not hardcoded. here is a sandbox snippet, would appreciate the help.
https://codesandbox.io/s/2prw4j729p?fontsize=14&moduleview=1
Just convert the JSON output to an object like list with a map function from the results and then set is as MenuItems on the state, which is what you pass to the function on render(). Like that.
import React, { Component } from "react";
import ScrollMenu from "react-horizontal-scrolling-menu";
import "./menu.css";
// One item component
// selected prop will be passed
const MenuItem = ({ text, selected }) => {
return (
<div>
<div className="menu-item">{text}</div>
</div>
);
};
// All items component
// Important! add unique key
export const Menu = list =>
list.map(el => {
const { name, id } = el;
return <MenuItem text={name} key={id} />;
});
const Arrow = ({ text, className }) => {
return <div className={className}>{text}</div>;
};
export class Menucat extends Component {
state = {
selected: "0",
MenuItems: []
};
componentDidMount() {
fetch("menu.json")
.then(res => res.json())
.then(result => {
const items = result.results.map((el, idx) => {
return { name: el.category, id: idx };
});
this.setState({
isLoaded: true,
MenuItems: items
});
});
}
render() {
const { selected, MenuItems } = this.state;
// Create menu from items
const menu = Menu(MenuItems, selected);
return (
<div className="App">
<ScrollMenu
data={menu}
selected={selected}
onSelect={this.onSelect}
alignCenter={true}
tabindex="0"
/>
</div>
);
}
}
export default Menucat;
Cheers!
Looks like you don't have to hard code your category list at all. In your componentDidMount() fetch the json and group the results into separate categories like this:
const json = {
"results": [
{
category: "category1",
name: "Fred"
},
{
category: "category1",
name: "Teddy"
},
{
category: "category2",
name: "Gilbert"
},
{
category: "category3",
name: "Foxy"
},
]
}
const grouped = json.results.reduce((acc, cur) => {
if (!acc.hasOwnProperty(cur.category)) {
acc[cur.category] = []
}
acc[cur.category].push(cur)
return acc;
}, { })
// parent object now has 3 properties, namely category1, category2 and category3
console.log(JSON.stringify(grouped, null, 4))
// each of these properties is an array of bjects of same category
console.log(JSON.stringify(grouped.category1, null, 4))
console.log(JSON.stringify(grouped.category2, null, 4))
console.log(JSON.stringify(grouped.category3, null, 4))
Note that this json has 4 objects in result array, 2 of cat1, and 1 of cat 2 and cat3. You can run this code in a separate file to see how it works. Ofcourse you will be fetching the json object from server. I just set it for demonstration.
Then set teh state:
this.setState({ grouped })
Then in render() you only show the categories that have items like:
const menuBarButtons = Object.keys(this.state.grouped).map((category) => {
/* your jsx here */
return <MenuItem text={category} key={category} onClick={this.onClick} blah={blah}/>
/* or something , it's up to you */
})
I'm assuming you're showing the items based on the currently selected category this.state.selected. So after you have rendered your menu, you would do something like:
const selectedCatItems = this.state.grouped[this.state.selected].map((item) => {
return <YourItem name={item.name} key={item.id} blah={blah} />
})
Then render it:
return (
<div className="app">
<MenuBar blah={blah}>
{menuBarButtons}
</Menubar>
<div for your item showing area>
{selectedCatItems}
</div>
</div>
)
Also, don't forget to change your onClick() so that it sets this.state.selected state properly. I believe you can figure that out yourself.
Hope it helps.
PS: I didn't write a whole copy/paste solution to your problem simply because I'm reluctant to read and understand your UI details and the whole component to component data passing details..
I made a component containing two dropdown lists. The options in the second dropdown menu is supposed to be filtered depending on the selected option from the first dropdown menu.
Now, I want to map a filtered array that is stored in a const similary to the way i map options1:
render() {
const options1 = [
{value: 'one', label: 'One'},
{value: 'two', label: 'Two'}
];
const options2 = [
{value: 'one-a', label: 'One A', link: 'one'},
{value: 'one-b', label: 'One B', link: 'one'},
{value: 'two-a', label: 'Two A', link: 'two'},
{value: 'two-b', label: 'Two B', link: 'two'}
];
const filteredOptions = options2.filter(o => o.link === this.state.selectedOption.value);
return (
<div style={style}>
<div>
<label>Select one</label>
<select
value={this.state.selectedOption.value}
onChange={this.handleChange1}
>
{options1.map(tag => <option>{tag.value}</option>)}
</select>
</div>
<div>
<label>Then the other</label>
<select
value={this.state.selectedOption2.value}
onChange={this.handleChange2}
>
{filteredOptions.map(tag => <option>{tag.value}</option>)}
</select>
</div>
</div>
)
}
The first mapping of options1 works just fine. However, my select tag gets rendered empty for the mapping of filteredOptions.
I have no idea why it won't work. Anyone happen to have an idea?
Full code: https://www.codepile.net/pile/evNqergA
Here is a working example for what you're trying to do.
import React, { Component } from "react";
const options1 = [
{ value: "one", label: "One" },
{ value: "two", label: "Two" }
];
const options2 = [
{ value: "one-a", label: "One A", link: "one" },
{ value: "one-b", label: "One B", link: "one" },
{ value: "two-a", label: "Two A", link: "two" },
{ value: "two-b", label: "Two B", link: "two" }
];
export default class SelectsComponent extends Component {
handleChange1 = e => {
console.log(e);
this.setState({
selectedOption: { value: e.target.value }
});
};
handleChange2 = e => {
this.setState({
selectedOption2: { value: e.target.value }
});
};
constructor(props) {
super(props);
this.state = {
selectedOption: { value: "one" },
selectedOption2: { value: "one-a" }
};
}
render() {
const filteredOptions = options2.filter(
o => o.link === this.state.selectedOption.value
);
return (
<div>
<div>
<label>Select one</label>
<select
value={this.state.selectedOption.value}
onChange={this.handleChange1}
>
{options1.map(tag => (
<option>{tag.value}</option>
))}
</select>
</div>
<div>
<label>Then the other</label>
<select
value={this.state.selectedOption2.value}
onChange={this.handleChange2}
>
{filteredOptions.map(tag => (
<option>{tag.value}</option>
))}
</select>
</div>
</div>
);
}
}
In your scenario filteredOptions would be an empty Array.
The check for o.link === this.state.selectedOption.value is doing something wrong.
Check the value of this.state.selectedOption.value, this is not set correctly.
The best way to do this wouldn't be inside of the render method.
1) move your arrays into state or other instance members
2) make sure to only trigger the sorting once
this.setState(lastState => ({
...lastState,
options2: lastState.options2.filter(yourFilterFn)
}))
3) map the filtered array into JSX inside of your render method
Side-note: this uses immutable setState (which I gather is important given you create a new filtered array from the options2 in your example). If you want to follow an even more functional pattern, you can do the filtering inside of your render method (although I don't recommend it). If you decided to filter inside of your render method, consider using a memoization technique from React 16.7 (which is currently in Alpha).