How can I display the default value in the drop-down list using material-ui? I tried to add displayEmpty="true", but nothing changed.
I would like to have the first option A selected by default, so that a user can see it in UI. Currently, a user should click on the drop-down menu in order to select an item from the list. No item is selected by default (the selected item is blank by default).
const options = [
{label:"A",value:483.93},
{label:"B",value:8033.86},
{label:"C",value:1246.3}
]
<Grid item xs={true}>
<FormControl
className={this.props.styles.formControl}
margin="normal">
<InputLabel shrink htmlFor="distanceTarget-label-placeholder">
Target:
</InputLabel>
<Select
onChange={(event) => this.props.handleChange("distanceTarget", event)}
value={this.props.state.distanceTarget}
input={<Input name="distanceTarget" id="distanceTarget-label-placeholder" />}
displayEmpty="true"
name="distanceTarget"
>
{options && options.length && options.map((option, i) => {
return <MenuItem value={option.value} key={i}>{option.label}</MenuItem>
})}
</Select>
</FormControl>
</Grid>
UPDATE:
This is what I tried as suggested in comments, however I still have the same issue:
{options && options && options((option, i) => {
if (i===0) {
return <MenuItem value={option.value} key={i} selected={true}>{option.label}</MenuItem>
}
else {
return <MenuItem value={option.value} key={i}>{option.label}</MenuItem>
}
})}
Try this one. Add displayEmpty not displayEmpty="true".
<Select
onChange={(event) => this.props.handleChange("distanceTarget", event)}
value={this.props.state.distanceTarget}
input={<Input name="distanceTarget" id="distanceTarget-label-placeholder" />}
renderValue={value => ${value}`} // you should add your default value here
name="distanceTarget"
Render the selected value. You can only use it when the native prop is false (default). (material-ui docs)
In order to have a particular item selected by default, you should initialize the state that control's the Select's value to have that item's value. For instance, in my modified version of your code, I am initializing the Select's value to options[0].value.
import React from "react";
import ReactDOM from "react-dom";
import {
FormControl,
Input,
InputLabel,
Select,
MenuItem
} from "#material-ui/core";
const options = [
{ label: "A", value: 483.93 },
{ label: "B", value: 8033.86 },
{ label: "C", value: 1246.3 }
];
function App() {
const [distanceTarget, setDistanceTarget] = React.useState(options[0].value);
return (
<FormControl margin="normal">
<InputLabel shrink htmlFor="distanceTarget-label-placeholder">
Target:
</InputLabel>
<Select
onChange={event => setDistanceTarget(event.target.value)}
value={distanceTarget}
input={
<Input name="distanceTarget" id="distanceTarget-label-placeholder" />
}
name="distanceTarget"
>
{options &&
options.length &&
options.map((option, i) => {
return (
<MenuItem value={option.value} key={i}>
{option.label}
</MenuItem>
);
})}
</Select>
</FormControl>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Related
This is my current code, what I want here is after selecting on Select option (CHIP) and if the user type on the textfield I want to clear what the user selected on CHIP, What should I do to get what i want functionality?
const names = [
'Oliver Hansen',
'Van Henry',
'April Tucker',
];
function getStyles(name, personName, theme) {
return {
fontWeight:
personName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}
export default function MultipleSelectChip() {
const theme = useTheme();
const [personName, setPersonName] = React.useState([]);
const handleChange = (event) => {
const {
target: { value },
} = event;
setPersonName(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value
);
};
const handleChangeTextField = (event) => {
setPersonName(null);
};
return (
<div>
<FormControl sx={{ m: 1, width: 300 }}>
<InputLabel id="demo-multiple-chip-label">Chip</InputLabel>
<Select
labelId="demo-multiple-chip-label"
id="demo-multiple-chip"
multiple
value={personName}
onChange={handleChange}
input={<OutlinedInput id="select-multiple-chip" label="Chip" />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
))}
</Box>
)}
MenuProps={MenuProps}
>
{names.map((name) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, personName, theme)}
>
{name}
</MenuItem>
))}
</Select>
<TextField
variant="outlined"
label="Type anything to remove the value of Chip"
onChange={handleChangeTextField} />
</FormControl>
</div>
This is my current code, what I want here is after selecting on Select option (CHIP) and if the user type on the textfield I want to clear what the user selected on CHIP, What should I do to get what i want functionality?
I would set your textfield to be controlled (ie backed by a state variable) and add an effect hook to watch it.
When it receives a value, clear the selected names by setting personNames back to an empty array.
const [text, setText] = useState("");
useEffect(() => {
if (text) {
setPersonName([]);
}
}, [text]);
const handleChangeTextField = ({ target: { value } }) => {
setText(value);
};
<TextField
value={text}
variant="outlined"
label="Type anything to remove the value of Chip"
onChange={handleChangeTextField}
/>
You might also want to clear the text field when selecting names by adding this into handleChange...
setText("");
I have these 2 fields size and design in which the user can add more of these 2 fields as many times as they want.
Example:
I selected M for the size. It does show in the console:
Additionally, why is it rendering two of size and design at the first load of the screen? Also, add
And now selecting a design:
It will remove the value that was previously selected in the size field.
And in the console, the value of size has been replaced with design2
codesandbox link: https://codesandbox.io/s/form-1-ls6rx?file=/demo.js
import React, { useState, useEffect } from "react";
import Box from "#mui/material/Box";
import InputLabel from "#mui/material/InputLabel";
import MenuItem from "#mui/material/MenuItem";
import FormControl from "#mui/material/FormControl";
import Select from "#mui/material/Select";
import { TextField, Button } from "#mui/material";
export default function BasicSelect() {
const [prod, setProd] = useState("");
const [qty, setQty] = useState(0);
const [design, setDesign] = useState("");
const [sizeList, setSizeList] = useState([{ size: "", design: "" }]);
const handleChange = (event) => {
setProd(event.target.value);
};
const handleSubmit = async (e) => {
e.preventDefault();
console.log(prod, qty, sizeList, design);
};
//helper method
const handleAdd = () => {
setSizeList([...sizeList, { size: "", design: "" }]);
};
const handleRemove = (index) => {
const list = [...sizeList];
list.splice(index, 1);
setSizeList(list);
};
const handleSizeChange = (e, index) => {
const { value } = e.target;
setSizeList((prev) =>
Object.assign([...prev], {
[index]: { size: value }
})
);
};
useEffect(() => {
console.log(sizeList);
}, [sizeList]);
return (
<Box sx={{ minWidth: 120 }}>
<form onSubmit={handleSubmit}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Product</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={prod}
label="Product"
onChange={handleChange}
>
<MenuItem value="Item1">Item1</MenuItem>
<MenuItem value="Item2">Item2</MenuItem>
<MenuItem value="Item3">Item3</MenuItem>
</Select>
</FormControl>
<br />
<br />
<br />
<br />
{sizeList.map((singleSize, index) => (
<div key={index}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Size</InputLabel>
<Select
labelId="demo-simple-select-label"
id="size"
value={singleSize.size}
label="Product"
onChange={(e) => handleSizeChange(e, index)}
>
<MenuItem value="S">Small</MenuItem>
<MenuItem value="M">Medium</MenuItem>
<MenuItem value="L">Large</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">
Choose Design
</InputLabel>
<Select
labelId="demo-simple-select-label"
id="design"
value={singleSize.design}
label="Product"
onChange={(e) => handleSizeChange(e, index)}
>
<MenuItem value="Design1">Design1</MenuItem>
<MenuItem value="Design2">Design2</MenuItem>
<MenuItem value="Design3">Design3</MenuItem>
</Select>
</FormControl>
<br />
<br />
{sizeList.length > 1 && (
<Button
onClick={() => handleRemove(index)}
variant="contained"
color="secondary"
>
Remove{" "}
</Button>
)}
<br />
<br />
{sizeList.length - 1 === index && (
<Button variant="contained" onClick={handleAdd}>
{" "}
Add Quantity
</Button>
)}
</div>
))}
<br />
<br />
<br />
<br />
<Button type="submit">Submit </Button>
</form>
<Button>Add more Product </Button>
</Box>
);
}
You are using the same handler that is supposed to handle and update states for both the design and size, also there lies a problem in how you are updating the state using Object.assign, this is also leading to additional warnings in the console regarding the value passed, the issue is most likely due to the event conflict.
To put things in place, simply use different handlers to handle updates of different object attributes. A simple solution is to create a new array, make the necessary updates and set the new array to be the updated state, I tested this and it works as expected.
const handleSizeChange = (e, index) => {
const { value } = e.target;
console.log(value)
const arr = [...sizeList] //Shallow copy the existing state
arr[index].size = value //Update the size to the selected size
setSizeList([...arr]); //Set the updated array to be the new state
};
Add a new handler for updating the design value.
const handleDesignChange = (e,index)=>{
const { value } = e.target;
console.log(value)
const arr = [...sizeList]
arr[index].design = value
// console.log(arr)
setSizeList([...arr]);
}
Alternatively, you could club both the handlers into a single handler by adding conditional checks.
I have created a dropdown menu
const [splittingMethod, setSplittingMethod] = useState('');
const handleChange = (e: any) => {
//setSplittingMethod(e)
console.log('value', splittingMethod)
}
....
<FormControl variant="filled" >
<InputLabel id="demo-simple-select-filled-label" className={classes.field}>Splitting Method</InputLabel>
<Select
labelId="demo-simple-select-filled-label"
id="demo-simple-select-filled"
value={splittingMethod}
onChange={(e)=>handleChange(e)}
>
<MenuItem value="random">
Random
</MenuItem>
<MenuItem value={'timeEnd'}>Time End</MenuItem>
<MenuItem value={'slidingWindow'}>Sliding Window</MenuItem>
</Select>
</FormControl>
and I want to save the selected item's value into the splittingMethod variable. How can I achieve this? My current method would give me an error:
react-dom.development.js:13231 Uncaught Error: Objects are not valid as a React child
You have to access e.target.value to get the value
import React, { useState } from "react";
import { FormControl, InputLabel, Select, MenuItem } from "#material-ui/core";
const App = () => {
const [splittingMethod, setSplittingMethod] = useState("");
const handleChange = (
e: React.ChangeEvent<{
name?: string | undefined;
value: unknown;
}>
) => {
// type checking
const value = typeof e.target.value === "string" ? e.target.value : "";
setSplittingMethod(value);
};
return (
<FormControl variant="filled" style={{ width: "100%" }}>
<InputLabel
id="demo-simple-select-filled-label"
style={{ width: "100%" }}
>
Splitting Method
</InputLabel>
<Select
labelId="demo-simple-select-filled-label"
id="demo-simple-select-filled"
value={splittingMethod}
onChange={handleChange}
>
<MenuItem value="random">Random</MenuItem>
<MenuItem value={"timeEnd"}>Time End</MenuItem>
<MenuItem value={"slidingWindow"}>Sliding Window</MenuItem>
</Select>
</FormControl>
);
};
export default App;
Live demo
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 have a custom component that takes in values from an API and then displays them to a user, however within this component I give a 'required' flag to give an asterisk to the label, however I only want one field as seen below to have an asterisk not both as is currently happening.
<Grid item xs={12} sm={6}>
<SearchUsers
name="primaryOfficerId"
label="PO Responsible"
id="primaryOfficerId"
onSelect={change.bind(null, 'primaryOfficerId')}
error={touched.primaryOfficerId && Boolean(errors.primaryOfficerId)}
/>
</Grid>
<Grid item xs={12} sm={6}>
<SearchUsers
name="supportOfficerId"
label="Support Officer"
id="supportOfficerId"
onSelect={change.bind(null, 'supportOfficerId')}
/>
</Grid>
And now my custom component
const Search = (props) => {
const [data, setData] = useState([]);
const [select, setSelect] = useState(0);
const { type: TYPE, name: NAME, label: LABEL, onSelect, filter } = props;
const applyFilter = (data) => {
let result = data;
if (filter) {
result = filter(data);
}
return result;
};
useEffect(() => {
getLookupData(TYPE)
.then((response) => {
setData(response);
})
.catch((error) => {
if (error === HttpStatus.NOT_FOUND) {
setData([]);
} else {
throw error;
}
});
}, [TYPE]);
const options = applyFilter(data).map((item) => (
<MenuItem value={item.id} key={item.id}>
{item[NAME]}
</MenuItem>
));
const handleChange = (event) => {
setSelect(event.target.value);
onSelect && onSelect(event);
};
const { classes } = props;
return (
<FormControl required className={classes.formControl} id={NAME} error={props.error}>
<FormControlLabel control={<InputLabel htmlFor={NAME}>{LABEL}</InputLabel>} />
<Select
name={TYPE}
value={select}
onChange={handleChange}
disabled={props.disabled || options.length === 0}
input={<Input name={TYPE} id={NAME} />}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
{options}
</Select>
</FormControl>
);
};
Below you can see an image of my problem, both PO, and So responsible have an asterisk. I need only PO to have this asterisk but my component does not currently allow for individuals
Simply make your component customizable by passing it a required prop as boolean then in your component make it dynamic :
<FormControl required={props.required}>
// ...
</FormControl>
So now you can use it with an asterisk <SearchUsers required /> or without <SearchUsers required={false} />
Add additional prop required to your Search component and then use it as a prop value for FormControl:
<FormControl required={props.required} />