Skip to content

Commit f5ce0b3

Browse files
authored
Merge pull request #8331 from marmelab/Fix-autocompletearrayinput
Fix AutocompleteArrayInput shows undefined on blur
2 parents d70958c + bf1e44e commit f5ce0b3

File tree

4 files changed

+88
-26
lines changed

4 files changed

+88
-26
lines changed

docs/AutocompleteArrayInput.md

+28-25
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ The list of choices must be an array of objects - one object for each possible c
8585
]} />
8686
```
8787

88-
You can also use an array of objects with different properties for the label and value, given you specify the `optionText` and `optionValue` props:
88+
You can also use an array of objects with different properties for the label and value, given you specify the [`optionText`](#optiontext) and [`optionValue`](#optionvalue) props:
8989

9090
```jsx
9191
<AutocompleteArrayInput source="roles" choices={[
@@ -129,60 +129,61 @@ const choices = possibleValues.map(value => ({ id: value, name: ucfirst(value) }
129129

130130
## `create`
131131

132-
To allow users to add new options, pass a React element as the `create` prop. `<AutocompleteArrayInput>` will then render a menu item at the bottom of the list, which will render the passed element when clicked.
132+
To allow users to add new options, pass a React element as the `create` prop. `<AutocompleteArrayInput>` will then render a "Create" option at the bottom of the choices list. When clicked, it will render the create element.
133+
134+
![create option](./img/autocomplete-array-input-create.gif)
133135

134136
{% raw %}
135137
```jsx
136-
import { CreateCategory } from './CreateCategory';
138+
import { CreateRole } from './CreateRole';
139+
140+
const choices = [
141+
{ id: 'admin', name: 'Admin' },
142+
{ id: 'u001', name: 'Editor' },
143+
{ id: 'u002', name: 'Moderator' },
144+
{ id: 'u003', name: 'Reviewer' },
145+
];
137146

138-
const PostCreate = () => (
147+
const UserCreate = () => (
139148
<Create>
140149
<SimpleForm>
141-
<TextInput source="title" />
142-
<ReferenceArrayInput source="category_ids" reference="categories">
143-
<AutocompleteArrayInput create={<CreateCategory />} />
144-
</ReferenceArrayInput>
150+
<SelectArrayInput
151+
source="roles"
152+
choices={choices}
153+
create={<CreateRole />}
154+
/>
145155
</SimpleForm>
146156
</Create>
147157
);
148158

149-
// in ./CreateCategory.js
150-
import { useCreate, useCreateSuggestionContext } from 'react-admin';
159+
// in ./CreateRole.js
160+
import { useCreateSuggestionContext } from 'react-admin';
151161
import {
152-
Box,
153-
BoxProps,
154162
Button,
155163
Dialog,
156164
DialogActions,
157165
DialogContent,
158166
TextField,
159167
} from '@mui/material';
160168

161-
const CreateCategory = () => {
169+
const CreateRole = () => {
162170
const { filter, onCancel, onCreate } = useCreateSuggestionContext();
163-
const [create] = useCreate();
164171
const [value, setValue] = React.useState(filter || '');
165172

166173
const handleSubmit = event => {
167174
event.preventDefault();
168-
create(
169-
'categories',
170-
{ data: { title: value } },
171-
{
172-
onSuccess: (data) => {
173-
setValue('');
174-
onCreate(data);
175-
},
176-
}
177-
);
175+
const newOption = { id: value, name: value };
176+
choices.push(newOption);
177+
setValue('');
178+
onCreate(newOption);
178179
};
179180

180181
return (
181182
<Dialog open onClose={onCancel}>
182183
<form onSubmit={handleSubmit}>
183184
<DialogContent>
184185
<TextField
185-
label="New category name"
186+
label="Role name"
186187
value={value}
187188
onChange={event => setValue(event.target.value)}
188189
autoFocus
@@ -201,6 +202,8 @@ const CreateCategory = () => {
201202

202203
If you just need to ask users for a single string to create the new option, you can use [the `onCreate` prop](#oncreate) instead.
203204

205+
If you're in a `<ReferenceArrayInput>` or `<ReferenceManyToManyInput>`, the `handleSubmit` will need to create a new record in the related resource. Check the [Creating New Choices](#creating-new-choices) for an example.
206+
204207
## `debounce`
205208

206209
When used inside a [`<ReferenceArrayInput>`](./ReferenceArrayInput.md), `<AutocompleteArrayInput>` will call `dataProvider.getList()` with the current input value as filter after a delay of 250ms. This is to avoid calling the API too often while users are typing their query.
292 KB
Loading

packages/ra-ui-materialui/src/input/AutocompleteArrayInput.stories.tsx

+58
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,64 @@ export const Basic = () => (
4646
</AdminContext>
4747
);
4848

49+
const choices = [
50+
{ id: 'admin', name: 'Admin' },
51+
{ id: 'u001', name: 'Editor' },
52+
{ id: 'u002', name: 'Moderator' },
53+
{ id: 'u003', name: 'Reviewer' },
54+
];
55+
56+
const CreateRole = () => {
57+
const { filter, onCancel, onCreate } = useCreateSuggestionContext();
58+
const [value, setValue] = React.useState(filter || '');
59+
60+
const handleSubmit = event => {
61+
event.preventDefault();
62+
const newOption = { id: value, name: value };
63+
choices.push(newOption);
64+
setValue('');
65+
onCreate(newOption);
66+
};
67+
68+
return (
69+
<Dialog open onClose={onCancel}>
70+
<form onSubmit={handleSubmit}>
71+
<DialogContent>
72+
<TextField
73+
label="Role name"
74+
value={value}
75+
onChange={event => setValue(event.target.value)}
76+
autoFocus
77+
/>
78+
</DialogContent>
79+
<DialogActions>
80+
<Button type="submit">Save</Button>
81+
<Button onClick={onCancel}>Cancel</Button>
82+
</DialogActions>
83+
</form>
84+
</Dialog>
85+
);
86+
};
87+
88+
export const CreateProp = () => (
89+
<AdminContext i18nProvider={i18nProvider}>
90+
<Create
91+
resource="users"
92+
record={{ roles: ['u001', 'u003'] }}
93+
sx={{ width: 600 }}
94+
>
95+
<SimpleForm>
96+
<AutocompleteArrayInput
97+
source="roles"
98+
choices={choices}
99+
sx={{ width: 400 }}
100+
create={<CreateRole />}
101+
/>
102+
</SimpleForm>
103+
</Create>
104+
</AdminContext>
105+
);
106+
49107
const dataProvider = {
50108
getOne: (resource, params) =>
51109
Promise.resolve({

packages/ra-ui-materialui/src/input/AutocompleteInput.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ If you provided a React element for the optionText prop, you must also provide t
407407
);
408408

409409
const finalOnBlur = useCallback((): void => {
410-
if (clearOnBlur) {
410+
if (clearOnBlur && !multiple) {
411411
const optionLabel = getOptionLabel(selectedChoice);
412412
if (!isEqual(optionLabel, filterValue)) {
413413
setFilterValue(optionLabel);
@@ -422,6 +422,7 @@ If you provided a React element for the optionText prop, you must also provide t
422422
selectedChoice,
423423
filterValue,
424424
debouncedSetFilter,
425+
multiple,
425426
]);
426427

427428
useEffect(() => {

0 commit comments

Comments
 (0)