React-admin ReferenceInput pulling document id instead source field - react-admin

Calling a ReferenceInput with an AutocompleteInput grabs the right document, and returns the "name" to the input but when you save it saves the document id to the source not the value of "name". Apparanetly React-admin does not allow anything but id to be saved natively, but the name is stored in the input until the document is saved. Is there any way to get the name from the input, and pass it to the format command maybe?
Expected result: input saves the value of the name from the document
<ReferenceInput label="Name" source="name" reference="profiles" filterToQuery={searchText => ({ name: searchText })} >
<AutocompleteInput optionText="name" source="name" resettable="true" shouldRenderSuggestions="true" />
</ReferenceInput>

This is correct - the default property of the object react-admin uses inside choice inputs (Select / Autocomplete) is the id. To override this behaviour you only have to pass one more prop to the <AutocompleteInput/> which is the optionValue e.g.:
<AutocompleteInput optionValue="name" ... />
And it should do exactly as you want it to.

Related

React Admin - how can I autofill (update) field values on change

I have a Google places autocomplete input and need to populate address related fields based on selected place as below:
<GPAutocompleteInput
source="address_full"
placeholder="Find address"
onPlaceSelected={onPlaceSelected}
gaOptions={{ types: ["address"] }}
/>
<TextInput source="address_street" disabled label="Ulica" />
<TextInput source="address_postcode" disabled label="Kod pocztowy" />
<TextInput source="address_city" disabled label="Miasto" />
Now I would like the 3 disabled inputs to be filled in with the address components from the selected place but can't figure out how to do this.
I've tried providing initialValues to the SimpleForm component and this works ok in the create view but not in the edit because those fields already have a value the values are not being updated.
react-final-form provides a simple hook allowing me to change values
import { useForm } from "react-final-form";
const form = useForm();
form.change("address_city", components?.address_city);
There may be a better way as this requires building a custom component to use form context required by the hook.

How to change the default text shown when no option is selected?

By default when the react-select is rendered i see "Select..." on the component. I want to see "Search ...".
Looking at the docs, I am assuming I need to use the noOptionsMessage prop, which is documented as:
noOptionsMessage function = () => undefined
Text to display when there are no options
({
inputValue string required
}) => One of<
react.Node,
null
>
I did not really understand that bit of the docs. E.g. this prop seems to be a function. So, I have been trying variations including following without success:
<AsyncSelect
cacheOptions
defaultOptions={[]}
loadOptions={handleLoadOptions}
onChange={handleChange}
noOptionsMessage={()=> "Search..."}
/>
So, what is the solution?
Add placeholder to the AsyncSelect.
<AsyncSelect
cacheOptions
defaultOptions={[]}
loadOptions={handleLoadOptions}
onChange={handleChange}
placeholder='Search...'
/>
Common props you may want to specify include:
autoFocus - focus the control when it mounts
className - apply a className to the control
classNamePrefix - apply classNames to inner elements with the given prefix
isDisabled - disable the control
isMulti - allow the user to select multiple values
isSearchable - allow the user to search for matching options
name - generate an HTML input with this name, containing the current value
onChange - subscribe to change events
options - specify the options the user can select from
placeholder - change the text displayed when no option is selected
value - control the current value

How can I prepopulate the structure of a complex nested form on the Create button in a List view in react-admin?

The List View of react-admin provides a "create (new record)" button out of the box when I specify a Create view in the Resource.
Since my record structure is nested up to three levels, containing objects with objects with arrays a.s.o., starting with an empty record (just {}) leads to a bunch of "undefined" errors in the validation function and when I test certain values with a FormDataConsumer to fold/unfold parts of the form based on other values.
I want my Create view to always start with a predefined record structure. How can I do that?
So it looks like you need default values for create form.
Documentation: https://marmelab.com/react-admin/CreateEdit.html#default-values
const postDefaultValue = { created_at: new Date(), nb_views: 0 };
export const PostCreate = (props) => (
<Create {...props}>
<SimpleForm initialValues={postDefaultValue}>
<TextInput source="title" />
<RichTextInput source="body" />
<NumberInput source="nb_views" />
</SimpleForm>
</Create>
);
You can flatten all the nested structure, and restore the input data back to the nested structure before submission. This document may help you: https://marmelab.com/react-admin/CreateEdit.html#altering-the-form-values-before-submitting

ArrayInput with SimpleFormIterator and conditional inputs

Let's say I have the following array in my create form
const CreateDefaults = {Inputs: [{id: "ID1", param: "a"},{id: "ID2", param: "b"}]};
and then I want to show extra TextInput only when id==="ID2"
export const MyCreate = withStyles(myStyles)(({ classes, ...props }) => (
<Create {...props}>
<SimpleForm defaultValue={CreateDefaults}>
<ArrayInput source="Inputs" {...props}>
<SimpleFormIterator {...props}>
<DisabledInput source="id" />
{/*this does not work*/}
{this.id === "ID2" ? (<TextInput source="ParamValue"/>) :null}
</SimpleFormIterator>
</ArrayInput>
</SimpleForm>
</Create>
));
How can I do something like that? I know that for the whole form, one can use FormDataConsumer. However, what can one do inside ArrayInput/SimpleFormIterator for each iteration?
How to access current object in iteration? I tried something like the answer given to the 'Custom Input do not receive record when inside ArrayInput' issue in the react-admin github page, but it still does not receive the record in custom input.
From the latest documentation here, you can see that if you use FormDataConsumer that is within an ArrayInput, you have a parameter called scopedFormData that you can use to get the current record and dereference that to get whatever fields you need. It usually also goes hand in hand with the getSource function you can use when setting the source within your FormDataConsumer.

Nested Reference Field

In order to retrieve the equipment type I am using a that will retrieve the equipment model and then another that references the equipment type using the equipment model's field "typeID" to retrieve the equipment type.
However it displays the following warning:
Warning: Failed prop type: Invalid prop translateChoice of type
boolean supplied to ReferenceField, expected function.
The image represents the data model (an equipment has an equipment model, and an equipment model has an equipment type)
I found a better solution is kinda of an hack but seems to be more efficient.
Taking the question example where in order to get equipmentType is only needed <ReferenceField>, it would be something like this:
const EquipList = ({...props}) => {
<List {...props}>
<Datagrid>
<ReferenceFieldController label="Equipment Type" reference="equipmentModel" source="modelID" linkType={false}>
{({referenceRecord, ...props}) => (
<ReferenceField basePath="/equipmentModel" resource="equipmentModel" reference="equipmentType" source="typeID" record={referenceRecord || {}} linkType="show">
<TextField source="name" />
</ReferenceField>
)}
</RefenceFieldController>
</Datagrid>
</List>
}
In the example above <ReferenceFieldController> fetches the equipmentModel of equipment, as like <ReferenceField>. Label is needed because RA uses the first <ReferenceField> to show the column header in <Datagrid>, if you use internationalization you should apply translate function to the correct resource on this prop.
<ReferenceController> fetches the record and passes it as referenceRecord to a child function that will render the component for field presentation. Instead of presenting the field component you render a <ReferenceField> to fetch the nested relation and next you show the field. Since <ReferenceFieldController> only passes controller props to its child and the props of field component don't do what you want in the nested relation, you have to explicit pass them to <ReferenceField>. You need to pass record of <ReferenceField> as referenceRecord || {} because the initially the referenceRecord is not fetched yet and <ReferenceField> doesn't work with record as null.
Setting the linkType of <ReferenceFieldController> to false makes it not render a <Link> component that would redirect the user to an incorrect route.
Not a perfect fix, but to get around the translateChoice issue, you can create a wrapper and pluck out that prop to prevent it from being passed.
const SubReference = ({ translateChoice, children, ...props }) => (
<ReferenceField {...props}>{children}</ReferenceField>
);
While troubleshooting this, I was also receiving an error about nested a tags. I was able to silence the error by setting the linkType prop to false in the parent ReferenceField
<ReferenceField source="item_id" reference="list" linkType={false}>
<SubReference source="id_to_reference_from_list" reference="second_list">
<TextField source="name" />
</SubReference>
</ReferenceField>
I have the same problem and I think this is an actual bug. I commented on the corresponding github issue https://github.com/marmelab/react-admin/issues/2140
I looked into the code for ReferenceField and as far as I understood this is an actual bug. ReferenceField expects a function for the translateChoice property, but internally hands a boolean to ReferenceFieldView.
If you nest one ReferenceField into another the inner one receives false as translateChoice property and rightfully complains that it is a boolean and not a function.