I have the following component which I'm trying to use inside a "Drinks Edit View". In "drinks" I have nested "ingredients", but the ingredients are also a related resource.
I want to select the ingredient from the related resource and set the nested fields (in drink) "record.ingredients[idx].name", "record.ingredients.[idx]._id", "record.ingredients.[idx].unit".
But I can't change the nested, even if I use an index [0] to test, it doesn't work and I don't know how to get the index too.
The way it is now, for example if I change the ingredient, the name of the Drink will be changed because the component is inside the Drink Edit View .
I tried FormDataConsumer, RecordContext without success.
import {
AutocompleteInput, ArrayInput, SimpleFormIterator, ReferenceInput, TextInput, NumberInput, BooleanInput,
AutocompleteInputProps, FormDataConsumer,
} from 'react-admin';
import { useFormContext } from 'react-hook-form';
export const ArrayInputForIngredients = () => {
const { setValue } = useFormContext();
const handleIngredientChange: AutocompleteInputProps['onChange'] = (
value,
record
) => {
setValue('name', record?.name); // NOT WORKING, CHANGES THE NAME OF THE DRINK
setValue('unit', record?.unit);
setValue('_id', record?._id);
};
return (
<ArrayInput source="ingredients">
<SimpleFormIterator inline >
<ReferenceInput source="_id" reference="ingredients" >
<AutocompleteInput label="Ingrediente"
source="_id"
optionText={"name"}
onChange={handleIngredientChange} />
</ReferenceInput>
<TextInput source="name" />
<TextInput source="unit" />
<BooleanInput source="decorative" />
<BooleanInput source="optional" />
<NumberInput source="quantity" />
</SimpleFormIterator>
</ArrayInput>
)
}
The component is inside something (simplified) like this:
export const DrinksEdit = () => {
return (
<Edit>
<SimpleForm>
<TextInput source= "name" />
<ArrayInputForIngredients />
< /SimpleForm>
< /Edit>
)
};
The name and other data of the each ingredient I select in the AutoCompleteInput
to change and not the name of the drink.
If I understand correctly, you need to pre-fill the values of name
and unit
based on the referenced ingredient selected, right?
If so, calling setValue('name', record?.name)
directly can't work because you need to use FormDataConsumer
's getSource
to compute the source path for the input nested inside ArrayInput
.
One way to achieve that is to use a HOF like this:
const TagsInput = () => {
const { setValue } = useFormContext();
const onChange = (source) => (_id, record) => {
setValue(source, record?.name?.en);
};
return (
<ArrayInput source="tags">
<SimpleFormIterator inline>
<FormDataConsumer>
{({ getSource }) => (
<ReferenceInput source={getSource("id")} reference="tags">
<AutocompleteInput
label="Tags"
source="id"
optionText={"name.en"}
onChange={onChange(getSource("name"))}
/>
</ReferenceInput>
)}
</FormDataConsumer>
<TextInput source="name" />
</SimpleFormIterator>
</ArrayInput>
);
};
And then
export const PostEdit = () => {
return (
<Edit>
<SimpleForm>
<TextInput source= "name" />
<TagsInput />
</SimpleForm>
</Edit>
)
};
Working example in the following sandbox (with tags input, in PostEdit, miscellanous tab): https://codesandbox.io/p/devbox/young-platform-z7x5lf
https://stackblitz.com/edit/github-vakown?file=src%2Fposts%2FPostEdit.tsx
Relevant code:
const TagAutocompleteInput = () => {
const sourceContext = useSourceContext();
const nameInputSource = sourceContext.getSource('name');
const { setValue } = useFormContext();
const onChange = (_id, record) => {
setValue(nameInputSource, record?.name?.en);
};
return (
<ReferenceInput source="id" reference="tags">
<AutocompleteInput
label="Tags"
source="id"
optionText={'name.en'}
onChange={onChange}
/>
</ReferenceInput>
);
};
const TagsInput = () => (
<ArrayInput source="tags">
<SimpleFormIterator inline>
<TagAutocompleteInput />
<TextInput source="name" />
</SimpleFormIterator>
</ArrayInput>
);
This leverages useSourceContext
from react-admin.