I am trying to use Material UI multiselect with checkboxes. So far, i am able to make multiple selects and get the values but i am unable to render the actual names of selected values or get all selected values. Any leads to a new approach i can use or useful links to help get the ids of all selected values in my array will be appreciated.
I created a sandbox that has a mock of my data from an api as well here : sandbox
My select looks like this :
const [selected, setSelected] = useState([]);
const isAllSelected =
options.length > 0 && selected.length === options.length;
const handleChange = (event) => {
console.log("vals", event.target);
const value = event.target.value;
if (value[value.length - 1] === "all") {
setSelected(selected.length === options.length ? [] : options.title);
return;
}
setSelected(value);
console.log("values", selected);
};
<FormControl className={classes.formControl}>
<InputLabel id="mutiple-select-label">Multiple Select</InputLabel>
<Select
labelId="mutiple-select-label"
multiple
variant="outlined"
value={selected}
onChange={handleChange}
renderValue={(selected) => selected}
MenuProps={MenuProps}
>
<MenuItem
value="all"
classes={{
root: isAllSelected ? classes.selectedAll : ""
}}
>
<ListItemIcon>
<Checkbox
classes={{ indeterminate: classes.indeterminateColor }}
checked={isAllSelected}
indeterminate={
selected.length > 0 && selected.length < options.length
}
/>
</ListItemIcon>
<ListItemText
classes={{ primary: classes.selectAllText }}
primary="Select All"
/>
</MenuItem>
{options.map((option) => (
<MenuItem key={option.id} value={option.id}>
<ListItemIcon>
<Checkbox checked={selected.includes(option.id)} />
</ListItemIcon>
<ListItemText primary={option.title} />
</MenuItem>
))}
</Select>
<p>{selected}</p>
</FormControl>
I did a few fix in your code so it works, in this sandbox:
To display some text in your menu, you are supposed to display the text in the component ListItemText:
<ListItemText>{option}</ListItemText>
An other thing is that you cannot access selected directly, since it is a state, so it is set asynchronously. To solve this, you can simply access it like this:
selected?.length
This way, even if selected is still undefined, it will not throw any errors
Related
Changes are made in all selections, instead of the one I choose. When I check the checkbox in one selection, all the others change. Changing How can I change that?
applications should change the name to the name of the marked checkbox in each selected selection. However, it changes in all the selections I have.
const handleSelect = (e,id) => {
let newValue = [1];
if (e.target.value.length) {
newValue = e.target.value.filter((item) => {
return item > 1;
});
}
const finalString = newValue
.reduce((acumulatedFinal, pass) => {
return acumulatedFinal + ", " + exportOptionNames[pass - 1];
}, "")
.replace(",", "")
.trim();
setSelectedSelections(finalString.length ? finalString : "Export Option");
setExportOption(newValue);
};
<FormControl }>
<Select onClick={(e)=> e.stopPropagation()}
labelId="label"
id="select"
value={exportOption}
multiple
onChange={(e) => handleSelect (e, item.id)}
key={item.id}
renderValue={() => {
return selectedOptionNames;
}}
input={<BootstrapInput />}
>
<MenuItem value={1} disabled>
Export Report
</MenuItem>
<MenuItem value={2}>
<Checkbox checked={exportOption.includes(2)} />
<ListItemText>{exportOptionNames[1]}</ListItemText>
</MenuItem>
<MenuItem value={3}>
<Checkbox checked={exportOption.includes(3)} />
<ListItemText>{exportOptionNames[2]}</ListItemText>
</MenuItem>
<MenuItem value={4}>
<Checkbox checked={exportOption.includes(4)} />
<ListItemText>{exportOptionNames[3]}</ListItemText>
</MenuItem>
</Select>
I have a list of users and a Select dropdown from material UI with some values. I am able to console.log the values of the select but how can I know to which user in the List they refer to?
<List>
{userList.map((user:any) => (
<ListItem key={user.key}>
<ListItemAvatar>
<Avatar>
<PersonIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={user.name} />
<Select
value={userValue}
onChange={handleChange}
>
{dropdownvalues.map(
(g: { value: string}) => (
<MenuItem key={g.value} value={g.value}>
{g.value}
</MenuItem>
)
)}
</Select>
</ListItem>
))}
</List>
const handleChange=(e:any,index:any) => {
console.log(e.target.value)//here I am able to console log just the value how can I bind the user too given the fact that this funciton doesnt accept another parameter
}
Just add index as value as it is uniquely identifiable in the following code -
{dropdownvalues.map(
(g: { value: string},index:number) => (
<MenuItem key={g.value} value={index}>
{g.value}
</MenuItem>
)
)}
After that just access your selected user as -
const handleChange = (e:any) => {
const selectedInd = e.target.value;
console.log('index->',selectedInd);
console.log(dropdownvalues[e.target.value]);
}
Simplest way is to extend your handleChange function and call like this:
// ...
onChange={(evt) => handleChange(user)}
// ... And then extend the function:
const handleChange=(user:any) => {
console.log(user)
}
I am creating the following component:
It will contain an array of objects, where each object is a prescription, with the medicine name from the select and a TextField for the Dosis.
My problem is that the TextField loses focus on every onChange() and is very frustrating because it cannot be edited on a single focus.
This is my component :
const MedicineSelect = ({ medications, setMedications, ...props }) => {
const { medicines } = useMedicines()
const classes = useStyles()
const handleChange = (index, target) => {
// setAge(event.target.value)
const newMedications = cloneDeep(medications)
newMedications[index][target.name] = target.value
setMedications(newMedications)
}
const handleAddMedicine = () => {
const newMedications = cloneDeep(medications)
newMedications.push({ medicine: '', dosis: '', time: '' })
setMedications(newMedications)
}
const handleDeleteMedicine = (index) => {
console.log('DELETE: ', index)
const newMedications = cloneDeep(medications)
newMedications.splice(index, 1)
setMedications(newMedications)
}
return (
<Paper style={{ padding: 5 }}>
<List>
{medications.map((medication, index) => (
<ListItem key={nanoid()} divider alignItems='center'>
<ListItemIcon>
<Tooltip title='Eliminar'>
<IconButton
className={classes.iconButton}
onClick={() => handleDeleteMedicine(index)}
>
<HighlightOffOutlinedIcon />
</IconButton>
</Tooltip>
</ListItemIcon>
<FormControl className={classes.formControl}>
<InputLabel
id={`${index}-select-${medication}-label`}
>
Medicamento
</InputLabel>
<Select
labelId={`${index}-select-${medication}-label`}
id={`${index}-select-${medication}`}
name='medicine'
value={medication.medicine}
onChange={(event) =>
handleChange(index, event.target)
}
>
{medicines.map((medicine) => (
<MenuItem
key={nanoid()}
value={medicine.name}
>
{medicine.name}
</MenuItem>
))}
</Select>
</FormControl>
<TextField
// fullWidth
id={`${index}-text-${medication}`}
label='Dosis'
name='dosis'
onChange={(event) =>
handleChange(index, event.target)
}
value={medication.dosis}
/>
</ListItem>
))}
<Button onClick={handleAddMedicine}>+ agregar</Button>
</List>
</Paper>
)
}
And here is where I set the component:
const [medications, setMedications] = useState([
{ medicine: '', dosis: '', time: '' },
])
...
<Grid item md={12} xs={12}>
<Accordion>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls='panel1a-content'
id='panel1a-header'
>
<Typography variant='h4'>
Tratamiento:
</Typography>
</AccordionSummary>
<AccordionDetails>
<Container disableGutters>
<MedicineSelect
medications={medications}
setMedications={setMedications}
/>
</Container>
</AccordionDetails>
</Accordion>
</Grid>
...
Adding and removing objects from the array works perfect. selecting the medicine from the select, also works perfect. the only problem I have is when editing the Dosis TextField, with every character, the focus is lost and I have to click again on the TextField.
Please help me getting this fixed!!!
After searching a lot, finally I found the solution. Actually when using nanoid() to create unique keys, on every state update React re-renders all components and since the id of both the List and the TextField component are regenerated by nanoid on every render, React loses track of the original values, that is why Focus was lost.
What I did was keeping the keys unmuttable:
<ListItem key={`medication-${index}`} divider alignItems='center'>
and
<TextField
key={`dosis-${index}`}
fullWidth
// id={`${index}-dosis-${medication}`}
label='Dosis'
name='dosis'
onChange={(event) =>
handleChange(index, event.target)
}
value={medication.dosis}
/>
I am trying to set the selected prop with react mui menu item. I have a multiple select menu list, where I have a field allValues, that on clicking it, toggles the selection of all menu items. And that part works just fine. The code looks like this:
<Select
multiple
value={selectedValues.map(klasse => klasse.id)}
onChange={(event) => handleChange(event.target.value, onChange, idToValues)}
input={<Input id="select-multiple-chip"/>}
classes={{root: classes.select}}
renderValue={selectedIds => (
<div className={classes.chips}>
{selectedIds.map(classId => (
<Chip
key={classId}
label={idToValues[classId] && idToValues[classId].classCode}
className={classes.chip}
onDelete={(event) => onChange(selectedValues.filter(class => class.id !== classId))}/>
))}
</div>
)}
MenuProps={MenuProps}
>
{!!allValues.length &&
<MenuItem value="allValues" selected={allValues.length === selectedValues.length}>
All classes
</MenuItem>
}
{allValues.map(class => (
<MenuItem key={class.id} value={class.id}>
{class.classCode}
</MenuItem>
))}
</Select>
I can see in the dev tools that allValues and selectedValues are of the equal length, but the selected prop is still false. How is that possible, and how can I fix this?
Try adding () brackets like:
selected={(allValues.length === selectedValues.length)}
I have a picker that I'm testing on iOS right now with two options. Every time I drag down from the first option to the second option, the picker immediately returns to the first option.
This is what my the code for my picker looks like.
<Picker
style={{
width: 100,
}}
selectedValue={(this.state && this.state.pickerValue) || 'a'}
onValueChange={(value) => {
this.setState({value});
}} itemStyle={{color: 'white'}}>
<Picker.Item label={'Hello'} value={'a'} />
<Picker.Item label={'World'} value={'b'} />
</Picker>
I want the selector to stay at the newly scrolled-to option. I've also removed the || 'a' part of the selectedValue attribute but that didn't solve the issue either.
On value change you need to specify which property of the state changed and change it accordingly with this.setState
onValueChange={(value) => {this.setState({pickerValue: value});
Complete Code
<Picker
style={{
width: 100,
}}
selectedValue={(this.state && this.state.pickerValue) || 'a'}
onValueChange={(value) => {
this.setState({pickerValue: value});
}} itemStyle={{color: 'white'}}>
<Picker.Item label={'Hello'} value={'a'} />
<Picker.Item label={'World'} value={'b'} />
</Picker>
I just came across this and was facing the same issue, the scrolling reaches the new item and resets to the first item.
I have done this using stateless component (Hooks):
I have an array of objects as the value and option as key
const data = useState({
"options":[{
"name":"Dish 1","price":0},{"name":"Dish 2","price":0}]})
const [selected, setSelected] = useState(0)
The Picker component:
<PickerIOS
selectedValue={selected_choice}
onValueChange={(value, index) => {
set_selected_choice(index)
}}
>
{data?.map((item, index) => (
<PickerIOS.Item
key={item}
value={index}
label={item.name}
/>
))}
</PickerIOS>
Here, I have stored the index of the array elements in the selected state and have updated it from the PickerIOS Item, keeping the value as index.
I used this "hack":
render() {
const values = ['1', '2'];
return (
<Picker
value={this.state.value}
onValueChange={this.onValueChange.bind(this)}
>
{
<Picker
value={this.state.value}
onValueChange={this.onValueChange.bind(this)}
>
{
[<Picker.Item
label="n/a"
value={null}
/>].concat(values.map(value => {
return (
<Picker.Item
label={value}
value={value}
/>
)
})
)
}
</Picker>
);
}