I have a Select that I'm using from Ant Design used with react-final-form. So far it looks like this:
const SelectInput = (props) => (
<AntForm.Item label={props.label}>
<Select {...props.input}>
{props.options.map((option) => (
<Select.Option key={option.id} value={option.id}>
{option.name}
</Select.Option>
))}
</Select>
</AntForm.Item>
);
// ....
<AntForm layout="vertical">
<Field
label="Select an option"
name="option"
options={options}
component={SelectInput}
/>
</AntForm>
The data looks like this from the server:
const data = [
{id: 1, label: 'option1'},
{id: 2, label: 'option2'},
{id: 3, label: 'option3'},
{id: 4, label: 'option4'},
]
However as you can you the data has label in it. in my select, I'm rendering name.
I want to be able to pass a prop that modifies the array to use label. I've seen it used in some select components in other libraries, but I interested in how this is done.
i want to do something like this:
<AntForm layout="vertical">
<Field
label="Select an option"
name="option"
label={options => option.label} // <-- modify to use label here
options={data}
component={SelectInput}
/>
</AntForm>
How do I achieve something like this? If there's a way to do this with Ant Design, I would love to know as well.
Just pass a new props nameKey
const SelectInput = (props) => (
<AntForm.Item label={props.label}>
<Select {...props.input}>
{props.options.map((option) => (
<Select.Option key={option.id} value={option.id}>
{option[props.nameKey || "name"]}
</Select.Option>
))}
</Select>
</AntForm.Item>
);
<Field
label="Select an option"
name="option"
nameKey="label"
options={data}
component={SelectInput}
/>
Related
I created a Select using React that allows the user to select multiple options.
The problem is that the Select displays the ID of the selected item, instead of their name.
How can I change the code in a way that the Select display the names separated by commas (now shows the IDs separated by commas), while keeping the array of ids for later processing.
Any idea how to fix it? Here is the code to CodeSanbox
I have the following array in a Material UI Select:
const names = [
{ id: "1", value: "Oliver Hansen" },
{ id: "2", value: "Van Henry" },
{ id: "3", value: "Omar Alexander" }
];
This is the code that renders the Multiple Select:
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
name="first"
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.join(", ")}
>
{names.map((name) => (
<MenuItem key={name.id} value={name.id}>
<Checkbox checked={personName.indexOf(name.id) > -1} />
<ListItemText primary={name.value} />
</MenuItem>
))}
</Select>
I found one possible solution for your issue.
check if it works for you.
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import { Input, OutlinedInput } from "#material-ui/core";
import InputLabel from "#material-ui/core/InputLabel";
import FormControl from "#material-ui/core/FormControl";
import Select from "#material-ui/core/Select";
import MenuItem from "#material-ui/core/MenuItem";
import ListItemText from "#material-ui/core/ListItemText";
import Checkbox from "#material-ui/core/Checkbox";
const useStyles = makeStyles((theme) => ({
formControl: {
margin: theme.spacing(1),
minWidth: 300
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
const names = [
{ id: "1", value: "Oliver Hansen" },
{ id: "2", value: "Van Henry" },
{ id: "3", value: "Van Henry" }
];
export default function NativeSelects() {
const classes = useStyles();
const [personName, setPersonName] = React.useState([]);
const handleChange = (event) => {
const {
target: { value }
} = event;
setPersonName(
// On autofill we get a the stringified value.
typeof value === "string" ? value.split(",") : value
);
};
return (
<div>
<FormControl className={classes.formControl}>
<InputLabel htmlFor="age-native-simple">
Names here to select from
</InputLabel>
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
name="first"
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.map(obj=> names[obj - 1].value).join(", ")}
>
{names.map((name) => (
<MenuItem key={name.id} value={name.id}>
<Checkbox checked={personName.indexOf(name.id) > -1} />
<ListItemText primary={name.value} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
Updated Code
just you can do this:
renderValue={
(selected) =>
names.filter( name => selected.includes(name.id) )
.map( record => record.name )
.join(", ")
}
To show the selected user's names, you can update the map part, where currently, you're using id.
You can update this to use the name.value to store/show the person's names.
{names.map((name) => (
<MenuItem key={name.value} value={name.value}>
<Checkbox checked={personName.indexOf(name.value) > -1} />
<ListItemText primary={name.value} />
</MenuItem>
))}
Updated Sandbox
the simple steps I did, just focus on the renderValue property, on the Select component:
renderValue={(selected) => names.find((val) => val.id === selected).value}
the logic I use, find the value in the 'names' array, where the id is the selected id, then take the 'value' value in the 'names' array, to display.
Update the Menu Items' value to be a object instead of an id.
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
name="first"
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.map((item) => item.value)?.join(",")}
>
{names.map((name) => (
<MenuItem key={name.id} value={name}>
<Checkbox
checked={personName.find((p) => p.id === name.id) !== undefined}
/>
<ListItemText primary={name.value} />
</MenuItem>
))}
</Select>
I am using reactjs, ant design and ant design pro for my project, I have a form like the code below. Option's data is loaded from api. How can I choose that each time I select an option ,the information of that option will appear down to ProDescriptions
props.onInsertObjectFinish(value)} >
<Form.Item name="TopicId" label="ID chủ đề ">
<Input id="TopicId" type="hidden" value={currentRow?.id}/>{currentRow?.id}
</Form.Item>
<Form.Item name='ExhibitId' label="Tên hiện vật ">
<Select placeholder="Chọn hiện vật muốn thêm" style={{ width: 120 }} onSelect={(value) => props.getvalue(value)}>
{props.listObj.map((obj: any) => {
return <Option value={obj.id} >{obj.name}</Option>
})}
</Select>
<ProDescriptions
/>
</Form.Item>
</Form>
well, create a state
const [proDesc, setProDesc] = useState({});
create a function
const handleSelectChange = (value) =>{
props.getvalue(value)
let index = props.listObj.findIndex(item => item.id == value)
if(index > -1) setProDesc(props.listObj[index])
}
call function onSelectChange
onSelect={(value) => handleSelectChange(value)}
and pass this state as prop to ProDescriptions
<ProDescriptions proDesc={proDesc} />
now you have a full object of selected option in your ProDescription component, use it how ever you want
Here is the code of ProDescription that i can't using proDesc
props.onInsertObjectFinish(value)}>
<Form.Item name="TopicId" label="ID chủ đề ">
<Input id="TopicId" type="hidden" value={currentRow?.id}/>{currentRow?.id}
</Form.Item>
<Form.Item name='ExhibitId' label="Tên hiện vật ">
<Select
placeholder="Chọn hiện vật muốn thêm"
style={{ width: 120 }}
onSelect={(value) => handleSelectChange(value)}
>
{props.listObj.map((obj: any) => {
return <Option key={obj.id} value={obj.id} >{obj.name}</Option>
})}
</Select>
<ProDescriptions
column={1}
request={async () => ({
data: proDesc || {},
})}
columns={columns as ProDescriptionsItemProps<TableListItem>[]}
/>
</Form.Item>
</Form>
And here is the sample code running of ProDescription
const [currentRow, setCurrentRow] = useState();
<ProDescriptions<TableListItem>
column={1}
title={currentRow?.name}
request={async () => ({
data: currentRow || {},
})}
params={{
id: currentRow?.name,
}}
columns={columns as ProDescriptionsItemProps<TableListItem>[]}
/>
I tried to map an array so that I will not code a 200 codes in one particular select. This is my code.
const location = [ {id: 'A'}, ...... up to 200 id: values]
<FormControl fullWidth={true}>
<Field name='location'
component={renderSelectField}
option={location.map((location, index) => (<option value={location.id}>{location.id}</option>))}
props={{size: 'small',
type: 'text',
variant: 'outlined'
}}
/>
</FormControl>
const renderSelectField = ({input, label, meta: {touched, error}, children}) => (
<Select
floatinglabeltext={label}
errortext={touched && error ? 1 : 0}
{...input}
onChange={(value) => input.onChange(value)}
children={children}/>
);
instead of coding these:
{/* <option/>*/}
{/* <option value='A'>A</option>*/}
{/* <option value='B'>B</option>*/}
..... up to 200
I tried also put a return in it but does not work.
You have to map it using MenuItem instead of Option in Field element
{Location.map((location, index) => (
<MenuItem key={index} value={location.id}>
{location.id}
</MenuItem>
))}
I am using material ui in my react application.
And I am using select component.
I want to bind some initial values to the select control.
I had tried with the useState hook. It's not working.
Below is the code i am using.
const userContributors=[{
firstName: "user101",
id: "1",
role: "Users"
},{
firstName: "user102",
id: "2",
role: "Users"
},{
firstName: "user103",
id: "3",
role: "Users"
}]
const [ApplicationAdminUsers, SetApplicationAdminUsers] = useState([
{
firstName: "user101",
id: "1",
role: "Users"
}
]);
<FormControl
variant="outlined"
margin="normal"
InputLabelProps={{
shrink: true
}}
validators={["required"]}
errorMessages={["Select Application Admin"]}
className={[
classes.formControl,
"fullWidthControl",
"multiselect-checkbox"
].join(" ")}
>
<InputLabel
ref={inputLabel}
htmlFor="select-multiple-checkbox"
className="multi-label-top"
>
Select Users...
</InputLabel>
<Select
multiple
value={ApplicationAdminUsers}
onChange={handleChangeUserContributores}
className="multi-input-padding"
validators={["required"]}
errorMessages={["Select Application Admin"]}
input={
<OutlinedInput
labelWidth={labelWidth}
id="select-multiple-checkbox"
/>
}
MenuProps={MenuProps}
>
{userContributors.map((item, index) => (
<MenuItem key={"aa" + item.id} value={item}>
{item.firstName}
</MenuItem>
))}
</Select>
</FormControl>
And here I'm using this as multi select control.
Any solutions to solve this.
Thanks
When you're using object as material-ui Select value, you need to provide 'renderValue' prop:
renderValue={selected => selected.map(item => item.firstName).join(', ')}
You can refer to this CodeSandbox example
You can't use an object as the value for the input, it needs to be a string.
Also you haven't handled the onChange to update the array to add/remove items.
As a simple example.
const userContributors=["joe", "bob", "tim"]
...
const [ApplicationAdminUsers, SetApplicationAdminUsers] = useState(["joe"]);
...
<Select
multiple
value={ApplicationAdminUsers}
onChange={(event)=> SetApplicationAdminUsers(event.target.value)}
className="multi-input-padding"
validators={["required"]}
errorMessages={["Select Application Admin"]}
input={
<OutlinedInput
labelWidth={labelWidth}
id="select-multiple-checkbox"
/>
}
MenuProps={MenuProps}
>
I'm trying to use React-final-form with a DropDown in a Child Component.
Can't get this to work.
All of my text fields are already in a Child Component and this works like a charm.
The field in the parent looks like this:
<Field
name="lastName"
placeholder="Last Name"
validate={required}
>
{({input, meta, placeholder}) => (
<MyField meta={meta} input={input} placeholder={placeholder}/>
)}
</Field>
The Child Component looks like this:
export const MyField = (props) => {
return (
<Form.Field className={props.meta.active ? 'active' : ''}>
<Label>{props.label ? props.label : props.placeholder}</Label>
<Form.Input
{...props.input}
placeholder={props.placeholder}
className={(props.meta.error && props.meta.touched ? 'error' : '')}
/>
</Form.Field>
)
};
The "Form.Field" and "Label" are coming from semantic-ui-react
But now I want to do the same with a DropDown.
The standard DropDown, taken from an example on the React-Final-Form site, looks like this:
<Field name="toppingsA" component="select">
<option value="chicken">Chicken</option>
<option value="ham">Ham</option>
<option value="mushrooms">Mushrooms</option>
<option value="cheese">Cheese</option>
<option value="tuna">Tuna</option>
<option value="pineapple">Pineapple</option>
</Field>
And it works in a sense that I'm getting the value in my react-final-form values onSubmit.
then I'm trying to offload the Dropdown itself to the Child Component (with the intention to use the semantic-ui-react version of a Dropdown, but first things first and get the Dropdown to work :-) )
Parent Component:
const eatOptions = [
{key: 'c', text: 'Chicken', value: 'chicken'},
{key: 'h', text: 'Ham', value: 'ham'},
{key: 'm', text: 'Mushrooms', value: 'mushrooms'},
{key: 't', text: 'Tuna', value: 'tuna'}
];
// And in the Form:
<Field name="toppingsB" component="select" options={eatOptions}>
{ ({input, meta, options}) => {
return (
<Opts options={options} name={input.name}/>
)
}}
</Field>
And in the Child Component:
export const Opts = (props) => {
return (
<select name={props.name}>
{props.options.map((x) => {
return (
<option key={x.key} value={x.value}>{x.text}</option>
)
})}
</select>
)
};
Result is that the HTML looks the same (which does not say that much I guess), ToppingsA is picked up in the values (on onSubmit) and ToppingsB is not.
I can't figure out what am I missing here and your help would be very much appreciated.
Thanks in advance,
Bert
If you are using render-props for toppingsB then the Field component prop should not be type "select" as the children of Field will be a function and not multiple tags.
It also looks like you are not letting your form know of any changes that occur inside the child component. Try passing the Opts component an onChange function as a prop:
<Opts
options={options}
name={input.name}
onChange={ (value:string) => input.onChange(value)}
/>
#S.Taylor, Thanks for your help!! It works.
As a reference the working code:
The Field in Parent Component:
<Field name="toppingsB" options={eatOptions} >
{ ({input, meta, options}) => {
return (
<Opts
options={options}
name={input.name}
onChange={ (value) => input.onChange(value)}
/>
)
}}
</Field>
And the code in the Child Component:
export const Opts = (props) => {
return (
<select name={props.name} onChange={props.onChange}>
{props.options.map((x) => {
return (
<option key={x.key} value={x.value}>{x.text}</option>
)
})}
</select>
)
};