Material-UI Autocomplete event.target - reactjs

I use Autocomplete component and I have code like this:
const countries = [
{country: 'Poland', code: 'PL'},
{country: 'Germany', code: 'DE'},
{country: 'Spain', code: 'ES'},
{country: 'France', code: 'FR'}
]
const Input = ({onCountryChange}) => (
<Autocomplete
id="combo-box"
options={countries}
getOptionLabel={option => option.country}
style={{ width: 300, marginTop: 10 }}
onChange={onCountryChange}
renderInput={params => (
<TextField {...params} label="Type a country" variant="outlined" fullWidth />
)}
/>
)
onCountryChange = (event) => {
this.setState({
countryCode: event.target.textContent
});
};
Now it sets as countryCode the country from countries options and I would like to use code instead (like PL). Any ideas how I can do this?

You have almost got the answer. I changed the onCountryChange function and look if this what you seek.
here is a working example: https://codesandbox.io/s/brave-mccarthy-kqx97
const countries = [
{ country: "Poland", code: "PL" },
{ country: "Germany", code: "DE" },
{ country: "Spain", code: "ES" },
{ country: "France", code: "FR" }
];
class App extends React.Component {
state = {
countryCode: ""
};
onCountryChange = (object, value) => {
this.setState({
countryCode: value.code
});
};
render() {
const { countryCode } = this.state;
return (
<div className="App">
{countryCode}
<Autocomplete
id="combo-box"
options={countries}
getOptionLabel={option => option.country}
style={{ width: 300, marginTop: 10 }}
onChange={this.onCountryChange}
renderInput={params => (
<TextField
{...params}
label="Type a country"
variant="outlined"
fullWidth
/>
)}
/>
</div>
);
}
}

From Material UI documentation:
function(event: React.SyntheticEvent, value: T | Array<T>, reason: string, details?: string) => void
event: The event source of the callback.
value: The new value of the
component. reason: One of "createOption", "selectOption",
"removeOption", "blur" or "clear".
This is my onChange function from Grouped template with TypeScript:
onChange={(event, value, reason, details) => selectChangeHandler(event, value as { title: string, year: number, firstLetter: string }, reason, details)}
const selectChangeHandler = (event: SyntheticEvent, value: { title: string, year: number, firstLetter: string }, reason: string, details: unknown) => {
event.preventDefault()
console.log('value', value)
console.log('reason', reason)
console.log('details', details)
}

Related

How to get dynamically selected value from material ui autocomplete

We can get TextField value dynamically with [evt.target.name]: evt.target.value but
How can I get Autocomplete selected value dynamically, Like bellow example:
import { useState } from 'react'
import Autocomplete from '#mui/material/Autocomplete'
import TextField from '#mui/material/TextField'
import Button from '#mui/material/Button'
const brands = ['niki', 'adidas', 'ramond', 'oliver', 'zara', 'casely']
const categories = ['pant', 'shirt']
const inputItems = [
{ label: 'Product Name', type: 'text', name: 'name' },
{ label: 'Brand', type: 'text', name: 'brand', options: brands },
{ label: 'Category', type: 'text', name: 'category', options: categories },
{ label: 'Price', type: 'number', name: 'price' },
{ label: 'Description', type: 'text', name: 'description' },
{ label: 'Image', type: 'text', name: 'image' },
]
const inputItemsObj = {}
inputItems.forEach(item => inputItemsObj[item.name] = '')
const Products = () => {
const [ fields, setFields ] = useState(inputItemsObj)
const [ fieldsError, setFieldsError ] = useState(inputItemsObj)
const changeHandler = (evt, newValue, action, option ) => {
setFields({ ...fields,[evt.target.name]: evt.target.value })
}
const submitHandler = (evt) => {
evt.preventDefault()
console.log(fields)
}
return (
<form noValidate onSubmit={submitHandler}>
{inputItems.map(({label, type, name, options}, key) => (name === 'category' || name === 'brand') ? (
<Autocomplete key={key}
options={options}
getOptionLabel={option => option}
renderInput={ params => <TextField {...params}
label={label}
placeholder={label}
type={type}
fullWidth
margin='dense'
InputLabelProps={{ shrink: true }}
name={name}
error={!fields[name] || !!fieldsError[name]}
helperText={fieldsError[name]}
/>}
name={name}
value={options[0]}
onChange={changeHandler}
/>
) : (
<TextField key={key}
label={label}
placeholder={label}
type={type}
fullWidth
autoFocus={key === 0}
margin='dense'
InputLabelProps={{ shrink: true }}
name={name}
value={fields[name]}
onChange={changeHandler}
multiline
rows={name === 'description' ? 3 : 1}
error={!fields[name] || !!fieldsError[name]}
helperText={fieldsError[name]}
/>
))}
<Button type='submit' variant='contained' >Submit</Button>
</form>
)
}
export default Products
The second value of onChange will give you a list of all the selected values.
function(event: React.SyntheticEvent, value: T | Array<T>, reason: string, details?: string) => void
So in your example use newValue rather than evt in your changeHandler
More info found here: https://mui.com/api/autocomplete/
Edit: I don't think you will be able to get the name from evt.target.name so some solution like this should work.
const changeHandler = (name) => (evt, newValue, action, option ) => {
// Also as you are updating/adding to state a callback should be used in setFields
setFields((prevState) => ({ ...prevState, [name]: newValue }))
}
Then in the Autocomplete:
<Autocomplete key={key}
//...other props
onChange={changeHandler(name)}
/>

How to set default value of react-select after getting data from API

I have a react select component I want to update data so when I fetch data from my API i want to set my data to react select I use this code.
I have create state and use useEffect to fetch data from an API
const [currency, setCurrency] = useState([]);
useEffect(() => {
if (sessionStorage.getItem('AccountId')) {
axios.get(process.env.REACT_APP_SQL_API_URL + 'Organisation/' + query.search.split('=')[1])
.then(res => {
setCurrency({ label: res.data[0].currency, value: res.data[0].currency });
})
.catch(error => console.log(error.response))
}
else {
history.push('/login')
}
}, [])
This is the react select code
<Controller
rules={{ required: true }}
name="currency"
control={control}
render={({ field: { onChange } }) =>
<Select
onChange={(e) => {
onChange(e)
//handleSelect(e)
}}
options={[{ value: 'USD', label: 'USD $' }, { value: 'TZS', label: 'TZS' }]}
defaultValue={[currency]}
/>
}
/>
you need to have a state :
const [currency, setCurrency] = useState([]);
<Controller
rules={{
required: true }}
name="currency"
control={control}
render={({ field: { onChange } }) =>
<Select
onChange={(e) => {
onChange(e)
//handleSelect(e)
console.log(e.target);
console.log(e.target.value);
setCurrency(state=>[ //change state ])
}}
options={[{ value: 'USD', label: 'USD $' }, { value: 'TZS', label: 'TZS' }]}
defaultValue={currency}
value={currency}
/>
}
/>

My react-dropdown-select item is undefined

I am attempting to parse the dropdown item that is selected into a Prop. I get an error of
TypeError: Cannot read property 'name' of undefined
I have attempted to make a new Prop and parse it through there and that did not work either. I have also tried to add all array into the formData to no avail.
When I do not add the dropdown menu the Format and Location are parsed no problem, as soon as they change to the dropdown's it does not get read. I think the dropdowns change them into another format and not a string. .
The addFixture.js code is below:
const AddFixture = ({ addFixture, history }) => {
const [formData, setFormData] = useState({
teams: '',
opposition: '',
date: '',
format: '',
location: '',
});
const {
teams,
opposition,
date,
format,
location
} = formData;
const overs = [
{ value: '40 Overs', label: '40 Overs' },
{ value: '20 Overs', label: '20 Overs' },
]
const hOrA = [
{ value: 'Home', label: 'Home' },
{ value: 'Away', label: 'Away' },
]
const onChange = (e) =>
setFormData({ ...formData, [e.target.name]: e.target.value });
<form
className="form"
onSubmit={(e) => {
e.preventDefault();
addFixture(formData, history);
}}
>
<div className="form-group">
<Select
type="text"
placeholder="* Location"
name="location"
value={location}
options={hOrA}
onChange={onChange}
required
/>
</div>
<div className="form-group">
<Select
type="text"
placeholder="* format"
name="format"
value={format}
onChange={onChange}
options={overs}
required
/>
</div>
</form>
);
};
AddFixture.propTypes = {
addFixture: PropTypes.func.isRequired
};
React select expects the value to match one of the dropdown values - So
value={location} //needs to match the object {value:string, label:string}
React select passes in the full value of the selected option to the onChange so
onChange={onChange} //onChange={(val:{value:label}) => {}}
You can pass in the name of the dropdown like so
onChange={(val) => onChange(val,nameOfDropdown )}
Then use the name passed in like so
const onChange = (selected, name) =>{
setFormData({ ...formData, [name]: selected.value });
}
With all these changes your code should look something like this
const AddFixture = () => {
const [formData, setFormData] = useState({
format: '',
location: '',
});
const {
format,
location
} = formData;
const overs = [
{ value: '40 Overs', label: '40 Overs' },
{ value: '20 Overs', label: '20 Overs' },
]
const hOrA = [
{ value: 'Home', label: 'Home' },
{ value: 'Away', label: 'Away' },
]
/** onChange will get passed value from the dropdown {value:string, label:string} */
const onChange = (selected, name) =>{
setFormData({ ...formData, [name]: selected.value });
}
return(
<>
<div className="form-group">
<Select
type="text"
placeholder="* Location"
name="location"
/** Need to set this to one of the dropdown values {value:string, label:string} */
value={hOrA.find((val) => val.value === location)}
options={hOrA}
onChange={(val) => onChange(val,"location" )}
required
/>
</div>
<div className="form-group">
<Select
type="text"
placeholder="* format"
name="format"
/** Need to set this to one of the dropdown values {value:string, label:string} */
value={hOrA.find((val) => val.value === format)}
onChange={(val) => onChange(val,"format" )}
options={overs}
required
/>
</div>
</>
);
};

How can i validate multiple array controller with select option in react js?

I am doing simple an validation using react hook form but how can i take name after errors.name because i have an too complex name and i can't change that name cause of backend
Here is an Code sandbox link of my example
can anyone help me.
Here is my code:-
const App = () => {
const { control, handleSubmit, errors } = useForm();
const [target, setTarget] = useState([0]);
const addEmptyTargetFields = () => {
setTarget((previousTarget: any) => [...previousTarget, target.length + 1]);
};
const removeTarget = (index: number) => {
setTarget([...target.slice(0, index), ...target.slice(index + 1)]);
};
const onSubmit = () => {
console.log("form submitted");
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>Ice Cream Preference</label>
{target.map((e: any, i: number) => {
return (
<div key={i}>
<Controller
as={Select}
control={control}
name={`mediationRuleTargetList[${i}].networkId`}
placeholder="Bidder"
options={[
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
]}
rule={{ required: true }}
/>
{errors.mediationRuleTargetList[${i}].networkId &&
errors.mediationRuleTargetList[${i}].networkId ===
"required" && <span>Bidders is required</span>}
<Controller
as={Select}
control={control}
name={`mediationRuleTargetList[${i}].ecpmLookBackPeriod`}
placeholder="eCPM look back period"
options={[
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
]}
rule={{ required: true }}
/>
{errors.mediationRuleTargetList[${i}].ecpmLookBackPeriod &&
errors.mediationRuleTargetList[${i}].ecpmLookBackPeriod.type ===
"required" && <span>Bidders is required</span>}
{i > 0 && <button onClick={() => removeTarget(i)} />}
</div>
);
})}
<button onClick={() => addEmptyTargetFields()}>Add More</button>
<input type="submit" />
</form>
);
};

Handle change on Autocomplete Component from material ui

I want to use Autocomplete component for input tags. I'm trying to get the tags and save them on a state so I can later save them on the database. I'm using functions instead of classes in react. I did try with onChange, but I didn't get any result.
<div style={{ width: 500 }}>
<Autocomplete
multiple
options={autoComplete}
filterSelectedOptions
getOptionLabel={(option) => option.tags}
renderInput={(params) => (
<TextField
className={classes.input}
{...params}
variant="outlined"
placeholder="Favorites"
margin="normal"
fullWidth
/>
)}
/>
</div>;
As Yuki already mentioned, make sure you did use the onChange function properly. It receives two parameters. According to the documentation:
Signature: function(event: object, value: any) => void.
event: The event source of the callback
value: null (The value/values within the Autocomplete component).
Here's an example:
import React from 'react';
import Chip from '#material-ui/core/Chip';
import Autocomplete from '#material-ui/lab/Autocomplete';
import TextField from '#material-ui/core/TextField';
export default class Tags extends React.Component {
constructor(props) {
super(props);
this.state = {
tags: []
};
this.onTagsChange = this.onTagsChange.bind(this);
}
onTagsChange = (event, values) => {
this.setState({
tags: values
}, () => {
// This will output an array of objects
// given by Autocompelte options property.
console.log(this.state.tags);
});
}
render() {
return (
<div style={{ width: 500 }}>
<Autocomplete
multiple
options={top100Films}
getOptionLabel={option => option.title}
defaultValue={[top100Films[13]]}
onChange={this.onTagsChange}
renderInput={params => (
<TextField
{...params}
variant="standard"
label="Multiple values"
placeholder="Favorites"
margin="normal"
fullWidth
/>
)}
/>
</div>
);
}
}
const top100Films = [
{ title: 'The Shawshank Redemption', year: 1994 },
{ title: 'The Godfather', year: 1972 },
{ title: 'The Godfather: Part II', year: 1974 },
{ title: 'The Dark Knight', year: 2008 },
{ title: '12 Angry Men', year: 1957 },
{ title: "Schindler's List", year: 1993 },
{ title: 'Pulp Fiction', year: 1994 },
{ title: 'The Lord of the Rings: The Return of the King', year: 2003 },
{ title: 'The Good, the Bad and the Ugly', year: 1966 },
{ title: 'Fight Club', year: 1999 },
{ title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 },
{ title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 },
{ title: 'Forrest Gump', year: 1994 },
{ title: 'Inception', year: 2010 },
];
I needed to hit my api on every input change to get my tags from backend!
Use Material-ui onInputChange if you want to get your suggested tags on every input change!
this.state = {
// labels are temp, will change every time on auto complete
labels: [],
// these are the ones which will be send with content
selectedTags: [],
}
}
//to get the value on every input change
onInputChange(event,value){
console.log(value)
//response from api
.then((res) => {
this.setState({
labels: res
})
})
}
//to select input tags
onSelectTag(e, value) {
this.setState({
selectedTags: value
})
}
<Autocomplete
multiple
options={top100Films}
getOptionLabel={option => option.title}
onChange={this.onSelectTag} // click on the show tags
onInputChange={this.onInputChange} //** on every input change hitting my api**
filterSelectedOptions
renderInput={(params) => (
<TextField
{...params}
variant="standard"
label="Multiple values"
placeholder="Favorites"
margin="normal"
fullWidth
/>
Are you sure you used onChange correctly?
onChange signature: function(event: object, value: any) => void
#Dworo
If someone has a problem with displaying a selected item from the dropdown in the Input field,
I found a workaround, basically, you have to bind an inputValue at onChage for both Autocomplete and TextField.
const [input, setInput] = useState('');
<Autocomplete
options={suggestions}
getOptionLabel={(option) => option}
inputValue={input}
onChange={(e,v) => setInput(v)}
style={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="Combo box" onChange={({ target }) => setInput(target.value)} variant="outlined" fullWidth />
)}
/>
I wanted to update my state when I select an option from the autocomplete. I had a global onChange handler that manages for all inputs
const {name, value } = event.target;
setTukio({
...tukio,
[name]: value,
});
That updates the object dynamically based on the name of the field. But on the Autocomplete the name returns blank. So I changed the handler from onChange to onSelect. Then either create a separate function to handle the change or as in my case added an if statement to check if the name is not passed.
// This one will set state for my onSelect handler of the autocomplete
if (!name) {
setTukio({
...tukio,
tags: value,
});
} else {
setTukio({
...tukio,
[name]: value,
});
}
The above approach works if u have a single autocomplete. If you have multiple u can pass a custom function like below
<Autocomplete
options={tags}
getOptionLabel={option => option.tagName}
id="tags"
name="tags"
autoComplete
includeInputInList
onSelect={(event) => handleTag(event, 'tags')}
renderInput={(params) => <TextField {...params} hint="koo, ndama nyonya" label="Tags" margin="normal" />}
/>
// The handler
const handleTag = ({ target }, fieldName) => {
const { value } = target;
switch (fieldName) {
case 'tags':
console.log('Value ', value)
// Do your stuff here
break;
default:
}
};
This worked for me:
I have an array of objects of type:
{
id: someId,
label: someLabel,
}
let's call this array "items". And use a formState object, like this:
const [formState, setFormState] = useState({
idPuntoVenta: '',
nomPuntoVenta: '',
selected: items[0],
});
And defined the Autocomplete component like this:
<Autocomplete
disablePortal
id="salesPoint"
options={sortedSalesPoints}
value={selected}
onChange={(event, newValue) => {
setFormState({
...formState,
selected: newValue,
idPuntoVenta: newValue?.id,
});
}}
inputValue={nomPuntoVenta}
onInputChange={(event, newInputValue) => {
setFormState({
...formState,
nomPuntoVenta: newInputValue,
});
}}
renderInput={(params) => (
<TextField
{...params}
className="form-control"
size="small"
label="Punto de venta"
required
/>
)}
/>;
The important thing here (and the one that took me a while) is to understand that the {value} property of the Autocomplete component is an object of the same type of the ones stored in the array "items"

Resources