У меня есть следующий компонент, который я пытаюсь использовать в «Представлении редактирования напитков». В «напитки» я вложил «ингредиенты», но ингредиенты также являются связанным ресурсом.
Я хочу выбрать ингредиент из связанного ресурса и установить вложенные поля (в напитке) «record.ingredients[idx].name», «record.ingredients.[idx]._id», «record.ingredients.[idx]». .единица".
Но я не могу изменить вложенность, даже если я использую индекс [0] для проверки, это не работает, и я тоже не знаю, как получить индекс.
Как сейчас, например, если я изменю ингредиент, название напитка будет изменено, потому что компонент находится внутри представления редактирования напитка.
Я попробовал FormDataConsumer, RecordContext, но безуспешно.
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>
)
}
Компонент находится внутри чего-то (упрощенного) вроде этого:
export const DrinksEdit = () => {
return (
<Edit>
<SimpleForm>
<TextInput source= "name" />
<ArrayInputForIngredients />
< /SimpleForm>
< /Edit>
)
};
Название и другие данные каждого ингредиента, которые я выбираю в AutoCompleteInput для изменения, а не название напитка.





Если я правильно понимаю, вам нужно предварительно заполнить значения name и unit на основе выбранного ингредиента, на который указывает ссылка, верно?
Если это так, прямой вызов setValue('name', record?.name) не может работать, потому что вам нужно использовать getSource FormDataConsumer для вычисления исходного пути для входных данных, вложенных внутри ArrayInput.
Один из способов добиться этого — использовать HOF следующим образом:
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>
);
};
А потом
export const PostEdit = () => {
return (
<Edit>
<SimpleForm>
<TextInput source= "name" />
<TagsInput />
</SimpleForm>
</Edit>
)
};
Рабочий пример в следующей песочнице (с вводом тегов, в PostEdit, вкладка «Разное»): https://codesandbox.io/p/devbox/young-platform-z7x5lf
https://stackblitz.com/edit/github-vakown?file=src%2Fposts%2FPostEdit.tsx
Соответствующий код:
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>
);
Это использует useSourceContext из реакции-администратора.
Благодаря @ slax57 мне было трудно заставить его работать (я попробовал FormDataConsumer, но не смог заставить его работать правильно). Я помещу свой ответ здесь, потому что я также включил решение по изменению входной «единицы». Теперь onChange принимает 2 источника.
export const ArrayInputForIngredients = () => {
const { setValue } = useFormContext();
const onChange = (sourceOne, sourceTwo) => (_id, record) => {
setValue(sourceOne, record?.name);
setValue(sourceTwo, record?.unit);
};
return (
<ArrayInput source = "ingredients">
<SimpleFormIterator inline>
<FormDataConsumer>
{({ getSource }) => (
<ReferenceInput source = {getSource("_id")} reference = "ingredients">
<AutocompleteInput
label = "Ingrediente"
source = "_id"
optionText = {"name"}
onChange = {onChange(getSource("name"), getSource("unit"))}
/>
</ReferenceInput>
)}
</FormDataConsumer>
<TextInput source = "name" />
<TextInput source = "unit" />
</SimpleFormIterator>
</ArrayInput>
);
};
Теперь вставляю поля, причем без GetSource (убрано на V5)
export const ArrayInputForIngredients = () => {
const { data, isLoading } = useGetList('ingredients');
const IngredientAutocompleteInput = () => {
const sourceContext = useSourceContext();
const nameInputSource = sourceContext.getSource('name');
const unitInputSource = sourceContext.getSource('unit');
const abvInputSource = sourceContext.getSource('abv');
const { setValue } = useFormContext();
const onChangeIngredient = (_id, record) => {
setValue(nameInputSource, record?.name);
setValue(unitInputSource, record?.unit);
setValue(abvInputSource, record?.abv);
};
return (
<>
<ReferenceInput source = {"_id"} reference = "ingredients"
sort = {{ field: 'name', order: 'ASC' }} >
<Grid item xs = {12} md = {6} xl = {3} >
<AutocompleteInput
label = "Ingrediente"
source = "_id"
validate = {required("Nome necessário")}
optionText = "name"
isLoading = {isLoading}
onChange = {onChangeIngredient}
filterToQuery = {searchText => ({ name: searchText })}
fullWidth
/>
</Grid>
</ReferenceInput>
<TextInput source = "name" sx = {{
display: "none",
}} />
<NumberInput source = "abv" sx = {{
display: "none",
}} />
</>
)
}
А затем используйте его как компонент:
<SimpleFormIterator inline >
<IngredientAutocompleteInput />
</SimpleFormIterator>
Спасибо! Я пробовал много вариантов, но безуспешно, но с таким источником аргументов, как вы, все проблемы были решены. Я приведу свой ответ со своим кодом ниже только потому, что я также включил модификацию для изменения входной «единицы измерения».