material ui always return 0 onchange - reactjs

when i try to change the value in autocomplete of material-ui, i always get its value 0, here i have uploaded my whole code, can anyone please check my code and help me to resolve this issue ?
any help will be really appreciated.
export default function CreatePIC() {
const classes = useStyles();
const Department_list = [
{ label: 'Department1', id: 1 },
{ label: 'Department2', id: 2 },
{ label: 'Department3', id: 3 },
{ label: 'Department4', id: 4},
{ label: 'Department5', id: 5 }
]
const [department, setDepartment] = React.useState('');
const handleChangeDepartment = (event) => {
console.log(event.target.value);
setDepartment(event.target.value);
};
return (
<Autocomplete
id="Department"
value={department}
helperText={error.department}
options={Department_list}
getOptionLabel={option => typeof option === 'string' ? option : option.label}
onChange = {handleChangeDepartment}
renderInput={(params) => <TextField {...params} label="Search Department" variant="outlined" placeholder="Add Department" />}
/>
)
}

Ciao, in Autocomplete component event.target.value will be always 0. If you want to get the selected department you could use value in handleChangeDepartment. So your code becomes:
const handleChangeDepartment = (event, values) => {
console.log(event.target.value); // print always 0
console.log(values); // print values selected like { label: 'Department1', id: 1 }
setDepartment(values.label); // set department with values.label
};
Here a codesandbox example.

Rather than using:
event.target.value
try using:
event.target.innerText
or, to find the option index, use:
event.target.dataset.optionIndex

Related

Disable options based on currently selected option with Material UI Autocomplete component

I am creating an Autocomplete element using Material UI, React Hook Form, and Yup that allows a user to choose multiple inputs for the days of the week.
If a user chooses the "Every day" option, I want to disable being able to choose Sunday, Monday, Tues... etc. How would I do that using getOptionDisabled?
Here is my current code...
const daysOfWeekSuggestions = [
{label: "Every day"},
{label: "Sunday"},
{label: "Monday"},
{label: "Tuesday"},
{label: "Wednesday"},
{label: "Thursday"},
{label: "Friday"},
{label: "Saturday"}
];
<Autocomplete
disableClearable
disablePortal
filterSelectedOptions
multiple
getOptionLabel={(option) => option.label}
id="days-autocomplete"
options={daysOfWeekSuggestions}
renderInput={(params) => <TextField
required
error={errors.daysOfWeek ? true : false}
id="daysOfWeek"
label="Days of the week"
name="daysOfWeek"
type="search"
{...params}
{...register("daysOfWeek")}
/>}
/>
What you need to do is use the getOptionDisabled prop and then check to see if the user has already selected Every Day. Im guessing you are saving it into state similar to how I am using it here so you can then just use the .some function to see if the Every day object is in the values array.
const daysOfWeekSuggestions = [
{ label: "Every day" },
{ label: "Sunday" },
{ label: "Monday" },
{ label: "Tuesday" },
{ label: "Wednesday" },
{ label: "Thursday" },
{ label: "Friday" },
{ label: "Saturday" }
];
export default function DaysOfTheWeekSelect() {
const [value, setValue] = React.useState([]);
const handleOnChange = (e, value) => {
setValue(value);
};
return (
<Autocomplete
disableClearable
disablePortal
multiple
getOptionLabel={(option) => option.label}
id="days-autocomplete"
onChange={handleOnChange}
options={daysOfWeekSuggestions}
getOptionDisabled={(option) => {
if (value.some((day) => day.label === "Every day")) {
return true;
}
if (value.some((day) => day.label === option.label)) {
return true;
}
return false;
}}
value={value}
renderInput={(params) => (
<TextField
required
error={errors.daysOfWeek ? true : false}
id="daysOfWeek"
label="Days of the week"
name="daysOfWeek"
type="search"
{...params}
{...register("daysOfWeek")}
/>
)}
/>
);
}
I also made it that if you have already selected a value then it would be disabled as well. Just seems a little bit better than just filtering out the selected values
I think you need more than just getOptionDisabled option.
I have done similar thing in my prev app, for that you need to add one more property called disabled to each option and onChange to Every day, manually disable remaining options.
You can check working demo here - https://codesandbox.io/s/disable-mui-autocomplete-options-on-condition-gurzb
Try using the isOptionEqualToValue instead
<Autocomplete
...
isOptionEqualToValue={(option, value) => option.id === value.id}
...
/>
supply the identifying property, in this case 'id'

AntDesign Cascader: error Not found value in options

I am wanting to use the "Cascader" component of "Ant Design" but I am having trouble filling it with data. This is my code, which I am doing wrong, sorry I am still a newbie and I need your support please.
function CascaderEmpCliUn(props) {
const optionLists = { a: []}
const [state, setState] = useState(optionLists);
useEffect(() => {
async function asyncFunction(){
const empresas = await props.loginReducer.data.empresas;
const options = [
empresas.map(empresa => ({
value: empresa.id,
label: empresa.siglas,
children: [
empresa.cli_perm.map(cliente => ({
value: cliente.id,
label: cliente.siglas,
children: [
cliente.uunn_perm.map(un => ({
value: un.id,
label: un.nombre,
}))
]
}))
]})
)
];
setState({a : options})
}
asyncFunction();
}, [])
return (
<Cascader options={state.a} placeholder="Please select" />
)
}
ERROR
Not found value in options
I was able to reproduce your error with dummy data whenever I had an empty array of children at any level. I'm not sure why this should be a problem, but it is. So you need to modify your mapping function to check the length of the child arrays. It seems to be fine if passing undefined instead of an empty array if there are no children.
General Suggestions
You don't need to store the options in component state when you are getting them from redux. It can just be a derived variable. You can use useMemo to prevent unnecessary recalculation.
You are passing the entire loginReducer state in your props which is not ideal because it could cause useless re-renders if values change that you aren't actually using. So you want to minimize the amount of data that you select from redux. Just select the empresas.
Revised Code
function CascaderEmpCliUn() {
// you could do this with connect instead
const empresas = useSelector(
(state) => state.loginReducer.data?.empresas || []
);
// mapping the data to options
const options = React.useMemo(() => {
return empresas.map((empresa) => ({
value: empresa.id,
label: empresa.siglas,
children:
empresa.cli_perm.length === 0
? undefined
: empresa.cli_perm.map((cliente) => ({
value: cliente.id,
label: cliente.siglas,
children:
cliente.uunn_perm.length === 0
? undefined
: cliente.uunn_perm.map((un) => ({
value: un.id,
label: un.nombre
}))
}))
}));
}, [empresas]);
return <Cascader options={options} placeholder="Please select" />;
}
The final code of "options" object:
const options = useMemo(() => {
return empresas.map((empresa) => ({
value: empresa.id,
label: empresa.siglas,
children:
empresa.cli_perm.length === 0
? console.log("undefined")
:
empresa.cli_perm.map((cliente) => ({
value: cliente.id,
label: cliente.siglas,
children:
cliente.uunn_perm.length === 0
? console.log("undefined")
:
cliente.uunn_perm.map((un) => ({
value: un.id,
label: un.nombre
}))
}))
}));
}, [empresas]);

React select set value in select

As I understood, in react-select (latest) you need to send an object to the value props, to select/preselect an option. I am also using react-hook-form for my form management.
Is it possible to set the value through react-select or do I need to use react-hook-form's setValue()
As I click on an Item, I send this to my states:
const handleSectionOnClick = (id) => {
setTravelRoute({
'LocationEnd': { value: +travelRoutes[id].LocationIdEnd, label: travelRoutes[id].LocationCityEnd },
'LocationStart': { value: +travelRoutes[id].LocationIdStart, label: travelRoutes[id].LocationCityStart },
'startDate': new Date(),
'endDate': new Date()
})
}
My Demo object looks like this:
{
'LocationCityEnd': "Berlin, Ger",
'LocationCityStart': "Hattingen, Ger",
'LocationIdEnd': 2,
'LocationIdStart': 1,
'startDate': new Date(),
'endDate': new Date()
}
My (LocationStart) select component looks like this:
<Controller
render={({ onChange }) => (
<Select
styles={customStyles}
onChange={handleOnChangeStartLocation}
name="startLocation"
value={travelRoute?.LocationStart}
options={selectOptions}
placeholder="Choose..."
/>
)}
name="startLocation"
className={`${errors.startLocation ? 'inputSelectError' : ''}`}
control={control}
rules={{ required: true }}
/>
Nothing gets selected, not even the value/label.
What Am I missing? Thank you!
EDIT:
I added the handleOnChangeStartLocation function:
const handleOnChangeStartLocation = e => {
const { value, label } = e;
setTravelRoute(prevState => ({
...prevState,
LocationIdStart: value,
LocationCityStart: label
}))
}
The problem seems to be that you are not updating LocationStart with the new value/label. You should do something like:
const handleOnChangeStartLocation = e => {
const { value, label } = e;
setTravelRoute(prevState => ({
...prevState,
LocationStart: e, // add this line of code
LocationIdStart: value,
LocationCityStart: label
}))
}

React: OnChange for drop down return me the entire <select></select> tag

I am working with react and I have a dropdown menu which is generated like that:
Select a user : <select defaultValue={'DEFAULT'} onChange={this.handleFilter}><option value="DEFAULT" disabled>-- select an gangster --</option>{ddUsers}</select>
ddUsers:
let ddUsers = this.state.users.map(user => <option key={uuid.v4()} value={user}>{user.name}</option>)
And here is my users array:
users : [
{ name: 'All', key : uuid.v4()},
{ name: 'Koko', key : uuid.v4()},
{ name: 'Krikri', key : uuid.v4()},
{ name: 'Kéké', key : uuid.v4()}
]
My problem is that when I want to use the event.target it return me the entire select tag (here the output of console.log(event.target):
<select>
​<option value=​"DEFAULT" disabled selected>​-- select an gangster --​</option>
​<option value=​"[object Object]​">​All​</option>​
<option value=​"[object Object]​">​Koko​</option>​
<option value=​"[object Object]​">​Krikri​</option>​
<option value=​"[object Object]​">​Kéké​</option>​
</select>​
where it should normally return me only the user.
here is my handleUser (which displays me select tag above):
handleFilter = (event) => {
console.log(event.target);
}
I am lost on what I am missing. I have something similar and it's working perfectly.
What you want is a key/value at select value, but unfortunately, it does not work.
Maybe you'll need to use another component like React-select or use JSON.stringfy() and JSON.parse()
I have made an abstraction code using JSON methods.
const uuid = {
v4() {
return Math.random();
}
};
const users = [
{ name: "All", key: uuid.v4() },
{ name: "Koko", key: uuid.v4() },
{ name: "Krikri", key: uuid.v4() },
{ name: "Kéké", key: uuid.v4() }
];
function App() {
const [selected, setSelected] = React.useState("");
function parseSelected(event) {
const valueToParse = event.target.value;
const itemSelected = JSON.parse(valueToParse);
setSelected(itemSelected);
return;
}
return (
<div>
<select name="any" id="any" onChange={parseSelected}>
{users.map(user => (
<option key={user.key} value={JSON.stringify(user)}>
{user.name}
</option>
))}
</select>
<p>Selected name: {selected.name}</p>
<p>Selected key: {selected.key}</p>
</div>
);
}
the reason is Option HTML interface accepts only DOMString as a value.
You can see the docs here
You could do something like this.. This allows for flexibility so you can use this <select> component anywhere, using any object with any property.. (using #CarlosQuerioz answer, you'd always have to use an object with a name key)
Parameter meanings:
data: array you are wanting to use as options for select
value: the key from your data property that you want to display
placeholder: placeholder value (not selectable)
defaultOption: default selected option (more on this below)
onSelectChange: event that is fired when selection changes
How we handle defaultOption:
If you pass in an array of objects to defaultOption we will select the first object in that array, and use the specified value key to determine the value to be shown
If you pass in an object, we will show the specified value key
To demonstrate the defaultOption, uncomment each of these values in the example below, you'll see what I mean.
const users = [ // If this was your `data` object
{ name: "Karkar", key: 1 },
{ name: "Koko", key: 2 },
{ name: "Krikri", key: 3 },
{ name: "Kéké", key: 4 }
];
<MySelect
data={users}
value="name" // Must be a valid key that your objects have
onSelectChange={handle}
defaultOption={users[0].name}
//defaultOption={users[1]} // Would display 'Koko' by default
//defaultOption={users} // Would display 'Karkar' by default
/>
EXAMPLE:
const { useState, useEffect } = React;
const { render } = ReactDOM;
function MySelect({ data, value, defaultOption = "", onSelectChange, placeholder = "Select an option" }) {
const [selected, setSelected] = useState(defaultOption);
const handleOnSelectChange = selection => {
onSelectChange && onSelectChange(selection);
}
const handleChange = event => {
let sel = data.find(d => d[value] === event.target.value);
setSelected(sel);
handleOnSelectChange(sel);
};
useEffect(() => {
if (typeof selected === "object") {
let val = selected.length > 0 ? selected[0] : selected;
setSelected(val);
handleOnSelectChange(val);
}
}, []);
return (
<select value={selected[value]} onChange={handleChange}>
<option value="" disabled>
{placeholder}
</option>
{data && data.map((itm, indx) => (
<option key={indx} value={itm[value]}>
{itm[value]}
</option>
))}
</select>
);
}
const users = [
{ name: "Karkar", key: 1 },
{ name: "Koko", key: 2 },
{ name: "Krikri", key: 3 },
{ name: "Kéké", key: 4 }
];
function App() {
const [user, setUser] = useState();
const handle = selectedUser => {
setUser(selectedUser);
};
return (
<div>
<MySelect
data={users}
value="name"
placeholder="Please make a selection"
onSelectChange={handle}
//defaultOption={users[0].name}
//defaultOption={users[0]}
defaultOption={users}
/>
{user && <pre>{JSON.stringify(user, null, 2)}</pre>}
</div>
);
}
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>

react-select AsyncSelect loadOptions through React.cloneElement

I have a config file with some fields for generating input elements inside a component.
I'm trying to generate an AsyncSelect input field and assigning it loadOptions prop through the config. Problem is that the function never gets called.
Here's the object in the configuration for generating the AsyncSelect input:
{
key: 'cityCode',
value: (customer, borrower) => ({
label: borrower.cityName,
value: borrower.cityCode
}),
text: 'city',
disabled: falseFn,
input: REACT_ASYNC_SELECT_INPUT,
props: (borrower, component) => ({
inputValue: component.state.cityName,
loadOptions: () => {
return CitiesService.find(component.state.cityName || '')
.then(records => records.map(record => ({
label: record.name,
value: record.code
})));
},
onInputChange: cityName => {
component.setState({
cityName
});
},
onChange: ({label, value}, {action}) => {
if (action === 'clear') {
component.updateCustomer(component.props.fieldKey + 'cityName', '');
component.updateCustomer(component.props.fieldKey + 'cityCode', -1);
} else {
component.updateCustomer(component.props.fieldKey + 'cityName', label);
component.updateCustomer(component.props.fieldKey + 'cityCode', value);
}
}
}),
render: trueFn
},
Here's the part of the component render utilizing the config file to render different inputs:
<div className="values">
{
BorrowerInfoConfig().map(field => {
if (field.render(borrower)) {
const kebabCaseKey = _.kebabCase(field.key);
const fieldElement = React.cloneElement(field.input, {
className: `${kebabCaseKey}-input`,
value: field.value ? field.value(customer, borrower) : _.get(borrower, field.key),
onChange: e => {
let value = e.target.value;
if (field.options) {
value = Number(value);
}
this.updateCustomer(fieldKey + field.key, value);
},
disabled: field.disabled(borrower),
...field.props(borrower, this)
}, field.options ? Object.keys(field.options).map(option => <option
key={option}
className={`${kebabCaseKey}-option`}
value={option}>
{field.options[option]}
</option>) : null);
return <div key={field.key} className={`value ${kebabCaseKey}`}>
<span>{field.text}</span>
{fieldElement}
</div>;
}
return null;
})
}
</div>
As you can see I use React.cloneElement to clone the input from the config file and assign new properties to it depends on what I get from the config file, in this case 4 custom props:
inputValue
loadOptions
onInputChange
onChange
My problem is that loadOptions is never called, any ideas why? On the other hand inputValue is assign correctly to the cityName state of the component.
My Problem was that REACT_ASYNC_SELECT_INPUT references normal Select and not Select.Async.

Resources