Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Doc] Add ReferenceOneInput #8783

Merged
merged 1 commit into from
Mar 30, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/EditTutorial.md
Original file line number Diff line number Diff line change
@@ -889,6 +889,7 @@ This affects both the submit button, and the form submission when the user press
Users often need to edit data from several resources in the same form. React-admin doesn't support nested forms, but provides ways to edit related data in a user-friendly way:

- [`<EditInDialogButton>`](./EditInDialogButton.md) lets users open a modal to edit a related record
- [`<ReferenceOneInput>`](./ReferenceOneInput.md) lets users edit one related record
- [`<ReferenceManyInput>`](./ReferenceManyInput.md) lets users edit a list of related records
- [`<ReferenceManyToManyInput>`](./ReferenceManyToManyInput.md) lets users edit a list of related records via an associative table

5 changes: 3 additions & 2 deletions docs/Features.md
Original file line number Diff line number Diff line change
@@ -233,6 +233,7 @@ React-admin supports **one-to-many**, **many-to-one**, **one-to-one**, and **man
- [`ReferenceArrayInput`](./ReferenceArrayInput.md)
- [`ReferenceManyInput`](./ReferenceManyInput.md)
- [`ReferenceManyToManyInput`](./ReferenceManyToManyInput.md)
- [`ReferenceOneInput`](./ReferenceOneInput.md)

The [Fields For Relationships](./FieldsForRelationships.md) page lists all reference fields together with their common usage.

@@ -481,8 +482,8 @@ Inside forms, you can use [react-admin input components](./Inputs.md), designed
| Array of objects | `[{ item: 'jeans', qty: 3 }, { item: 'shirt', qty: 1 }]` | [`<ArrayInput>`](./ArrayInput.md) |
| Array of Enums | `['foo', 'bar']` | [`<SelectArrayInput>`](./SelectArrayInput.md), [`<AutocompleteArrayInput>`](./AutocompleteArrayInput.md), [`<CheckboxGroupInput>`](./CheckboxGroupInput.md), [`<DualListInput>`](./DualListInput.md) |
| Array of foreign keys | `[42, 43]` | [`<ReferenceArrayInput>`](./ReferenceArrayInput.md) |
| Translations | `{ en: 'Hello', fr: 'Bonjour' }` | [`<TranslatableInputs>`](./TranslatableInputs.md) |
| Related records | `[{ id: 42, title: 'Hello' }, { id: 43, title: 'World' }]` | [`<ReferenceManyInput>`](./ReferenceManyInput.md), [`<ReferenceManyToManyInput>`](./ReferenceManyToManyInput.md) |
| Translations | `{ en: 'Hello', fr: 'Bonjour' }` | [`<TranslatableInputs>`](./TranslatableInputs.md) |
| Related records | `[{ id: 42, title: 'Hello' }, { id: 43, title: 'World' }]` | [`<ReferenceManyInput>`](./ReferenceManyInput.md), [`<ReferenceManyToManyInput>`](./ReferenceManyToManyInput.md), [`<ReferenceOneInput>`](./ReferenceOneInput.md) |

You can build **dependent inputs**, using the [react-hook-form's `useWatch` hook](https://react-hook-form.com/api/usewatch). For instance, here is a `CityInput` that displays the cities of the selected country:

4 changes: 2 additions & 2 deletions docs/Inputs.md
Original file line number Diff line number Diff line change
@@ -79,8 +79,8 @@ React-admin provides a set of Input components, each one designed for a specific
| Array of objects | `[{ item: 'jeans', qty: 3 }, { item: 'shirt', qty: 1 }]` | [`<ArrayInput>`](./ArrayInput.md) |
| Array of Enums | `['foo', 'bar']` | [`<SelectArrayInput>`](./SelectArrayInput.md), [`<AutocompleteArrayInput>`](./AutocompleteArrayInput.md), [`<CheckboxGroupInput>`](./CheckboxGroupInput.md), [`<DualListInput>`](./DualListInput.md) |
| Array of foreign keys | `[42, 43]` | [`<ReferenceArrayInput>`](./ReferenceArrayInput.md) |
| Translations | `{ en: 'Hello', fr: 'Bonjour' }` | [`<TranslatableInputs>`](./TranslatableInputs.md) |
| Related records | `[{ id: 42, title: 'Hello' }, { id: 43, title: 'World' }]` | [`<ReferenceManyInput>`](./ReferenceManyInput.md), [`<ReferenceManyToManyInput>`](./ReferenceManyToManyInput.md) |
| Translations | `{ en: 'Hello', fr: 'Bonjour' }` | [`<TranslatableInputs>`](./TranslatableInputs.md) |
| Related records | `[{ id: 42, title: 'Hello' }, { id: 43, title: 'World' }]` | [`<ReferenceManyInput>`](./ReferenceManyInput.md), [`<ReferenceManyToManyInput>`](./ReferenceManyToManyInput.md), [`<ReferenceOneInput>`](./ReferenceOneInput.md) |



1 change: 1 addition & 0 deletions docs/Reference.md
Original file line number Diff line number Diff line change
@@ -134,6 +134,7 @@ title: "Index"
* [`<ReferenceManyToManyField>`](./ReferenceManyToManyField.md)<img class="icon" src="./img/premium.svg" />
* [`<ReferenceManyToManyInput>`](./ReferenceManyToManyInput.md)<img class="icon" src="./img/premium.svg" />
* [`<ReferenceOneField>`](./ReferenceOneField.md)
* [`<ReferenceOneInput>`](./ReferenceOneInput.md)
* [`<Resource>`](./Resource.md)
* [`<RichTextField>`](./RichTextField.md)
* [`<RichTextInput>`](./RichTextInput.md)
2 changes: 2 additions & 0 deletions docs/ReferenceOneField.md
Original file line number Diff line number Diff line change
@@ -26,6 +26,8 @@ This field fetches a one-to-one relationship, e.g. the details of a book, when u

For the inverse relationships (the book linked to a book_detail), you can use a [`<ReferenceField>`](./ReferenceField.md).

**Tip**: To edit the records of a one-to-one relationship, use [the `<ReferenceOneInput>` component](./ReferenceOneInput.md).

## Usage

Here is how to render a field of the `book_details` resource inside a Show view for the `books` resource:
237 changes: 237 additions & 0 deletions docs/ReferenceOneInput.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
---
layout: default
title: "The ReferenceOneInput Component"
---

# `<ReferenceOneInput>`

Use `<ReferenceOneInput>` in an `<Edit>` or `<Create>` view to edit a record linked to the current record via a one-to-one relationship, e.g. to edit the details of a book in the book edition view. It's an [Enterprise Edition](https://marmelab.com/ra-enterprise)<img class="icon" src="./img/premium.svg" /> component, part of the `@react-admin/ra-relationships` package.

<video controls autoplay muted loop width="100%">
<source src="./img/reference-one-input.webm" type="video/webm">
Your browser does not support the video tag.
</video>

`<ReferenceOneInput>` renders the inputs provided as its children, and fetches the related record to populate them. When users change the related record fields, the `<ReferenceOneInput>` component stores these changes locally. Then, when users actually submit the form, `<ReferenceOneInput>` will update both the base record and the related record.

## Usage

Here is an example one-to-one relationship: a `book` has at most one `book_details` row associated to it.

```
┌─────────────┐ ┌──────────────┐
│ book │ │ book_details │
│-------------│ │--------------│
│ id │───┐ │ id │
│ title │ └──╼│ book_id │
└─────────────┘ │ year │
│ author │
│ country │
│ genre │
│ pages │
└──────────────┘
```

You probably want to let users edit the book details directly from the book Edition view (instead of having to go to the book details Edition view). `<ReferenceOneInput>` allows to do that.

```jsx
import {
Edit,
SimpleForm,
TextInput,
NumberInput,
} from 'react-admin';
import { ReferenceOneInput } from '@react-admin/ra-relationships';

const BookEdit = () => (
<Edit mutationMode="optimistic">
<SimpleForm>
<TextInput source="title" />
<ReferenceOneInput reference="book_details" target="book_id">
<NumberInput source="year" />
<TextInput source="author" />
<TextInput source="country" />
<TextInput source="genre" />
<NumberInput source="pages" />
</ReferenceOneInput>
</SimpleForm>
</Edit>
);
```

`<ReferenceOneInput>` requires a `reference` and a `target` prop to know which entity to fetch, and one or more inputs as its `children` to edit the related record.

`<ReferenceOneInput>` persists the changes in the referenced record (book details in the above example) after persisting the changes in the main resource (book in the above example). This means that you can also use `<ReferenceOneInput>` in `<Create>` views.

**Tip**: `<ReferenceOneInput>` cannot be used with `undoable` mutations. You have to set `mutationMode="optimistic"` or `mutationMode="pessimistic"` in the parent `<Edit>` or `<Create>`, as in the example above.

## Props

| Prop | Required | Type | Default | Description |
| -------------- | -------- | ------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `target` | Required | `string` | - | Target field carrying the relationship on the referenced resource, e.g. 'book_id' |
| `reference` | Required | `string` | - | The name of the resource for the referenced records, e.g. 'book_details' |
| `children` | Required | `Element` | - | One or several input elements that accept a `source` prop |
| `label` | Optional | `string` | `reference` | Input label. In i18n apps, the label is passed to the `translate` function. Defaults to the humanized `reference` when omitted. Set `label={false}` to hide the label. |
| `source` | Optional | `string` | `id` | Name of the field that carries the identity of the current record, used as origin for the relationship |
| `filter` | Optional | `Object` | - | Filters to use when fetching the related record, passed to `getManyReference()` |
| `sort` | Optional | `{ field, order }` | `{ field: 'id', order: 'ASC' }` | Sort order to use when fetching the related record, passed to `getManyReference()` |
| `defaultValue` | Optional | `Object` | - | Default value for the related record (in case it does not yet exist) |
| `sx` | Optional | `SxProps` | - | MUI shortcut for defining custom styles |

Additional props are passed to the MUI `<Stack>` component.

## `children`

`<ReferenceOneInput>` expects input components as its children (like `<TextInput>`, `<NumberInput>`, etc.), which will allow to edit the related record. The inputs will be rendered inside an [MUI `<Stack>`](https://mui.com/material-ui/react-stack/).

```jsx
<ReferenceOneInput reference="book_details" target="book_id">
<NumberInput source="year" />
<TextInput source="author" />
<TextInput source="country" />
<TextInput source="genre" />
<NumberInput source="pages" />
</ReferenceOneInput>
```

**Important note**: `<ReferenceOneInput>` works by cloning its children and overriding their `source` prop, to add a temporary field name prefix. This means that, if you need to nest your inputs inside another component, you need to propagate the `source` prop to them.

## `defaultValue`

`<ReferenceOneInput>` allows to specify a default value for the related record. This is useful when the current record does not yet have a related record, and you want to pre-fill the related record with some default values.

{% raw %}

```jsx
<ReferenceOneInput
reference="book_details"
target="book_id"
defaultValue={{ author: 'Gustave Flaubert', year: 1857 }}
>
<NumberInput source="year" />
<TextInput source="author" />
<TextInput source="country" />
<TextInput source="genre" />
<NumberInput source="pages" />
</ReferenceOneInput>
```

{% endraw %}

## `filter`

`<ReferenceOneInput>` allows to specify filters to use when fetching the related record. This can be useful when you need additional filters to select the related record.

{% raw %}

```jsx
<ReferenceOneInput
reference="book_details"
target="book_id"
filter={{ reviewed: true }}
>
...
</ReferenceOneInput>
```

{% endraw %}

## `label`

By default, `<ReferenceOneInput>` humanizes the `reference` name to build a label. You can customize the label by passing the `label` prop.

```jsx
<ReferenceOneInput
reference="book_details"
target="book_id"
label="Detailed information about the book"
>
...
</ReferenceOneInput>
```

React-admin uses [the i18n system](https://marmelab.com/react-admin/Translation.html) to translate the label, so you can use translation keys to have one label for each language supported by the interface:

```jsx
<ReferenceOneInput
reference="book_details"
target="book_id"
label="resource.books.fields.details"
>
...
</ReferenceOneInput>
```

## `reference`

The name of the resource to fetch for the related records.

For instance, if you want to display the `book_details` of a given `book`, the `reference` name should be `book_details`:

```jsx
<ReferenceOneInput reference="book_details" target="book_id">
...
</ReferenceOneInput>
```

## `sort`

`<ReferenceOneInput>` allows to specify the sort options used when fetching the related record. This can be useful when the relation table does not have an `id` column.

{% raw %}

```jsx
<ReferenceOneInput
reference="book_details"
target="book_id"
sort={{ field: '_id', order: 'DESC' }}
>
...
</ReferenceOneInput>
```

{% endraw %}

## `source`

By default, `<ReferenceManyInput>` fetches the `reference` for which the `target` field equals the current record `id`. You can customize the field that carries the identity of the current record by setting the `source` prop.

```jsx
<ReferenceOneInput reference="book_details" target="book_id" source="_id">
...
</ReferenceOneInput>
```

## `sx`

You can override the style of the root component (a MUI [`<FormControl>`](https://mui.com/material-ui/api/form-control/#main-content)) and its child components by setting the `sx` prop.

{% raw %}

```jsx
<ReferenceOneInput
reference="book_details"
target="book_id"
sx={{ border: '1px solid red' }}
>
...
</ReferenceOneInput>
```

{% endraw %}

## `target`

Name of the field carrying the relationship on the referenced resource. For instance, if each `book` is linked to a record in `book_details`, and each `book_details` exposes a `book_id` field linking to the `book`, the `target` would be `book_id`.

```jsx
<ReferenceOneInput reference="book_details" target="book_id">
...
</ReferenceOneInput>
```

## Limitations

- `<ReferenceOneInput>` cannot be used inside an `<ArrayInput>` or a `<ReferenceManyInput>`.
- `<ReferenceOneInput>` cannot have a `<ReferenceManyInput>` as one of its children.
Binary file added docs/img/reference-one-input.webm
Binary file not shown.
1 change: 1 addition & 0 deletions docs/navigation.html
Original file line number Diff line number Diff line change
@@ -187,6 +187,7 @@
<li {% if page.path == 'ReferenceArrayInput.md' %} class="active" {% endif %}><a class="nav-link" href="./ReferenceArrayInput.html"><code>&lt;ReferenceArrayInput&gt;</code></a></li>
<li {% if page.path == 'ReferenceManyInput.md' %} class="active" {% endif %}><a class="nav-link" href="./ReferenceManyInput.html"><code>&lt;ReferenceManyInput&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
<li {% if page.path == 'ReferenceManyToManyInput.md' %} class="active" {% endif %}><a class="nav-link" href="./ReferenceManyToManyInput.html"><code>&lt;ReferenceManyToManyInput&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
<li {% if page.path == 'ReferenceOneInput.md' %} class="active" {% endif %}><a class="nav-link" href="./ReferenceOneInput.html"><code>&lt;ReferenceOneInput&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
<li {% if page.path == 'RichTextInput.md' %} class="active" {% endif %}><a class="nav-link" href="./RichTextInput.html"><code>&lt;RichTextInput&gt;</code></a></li>
<li {% if page.path == 'SelectInput.md' %} class="active" {% endif %}><a class="nav-link" href="./SelectInput.html"><code>&lt;SelectInput&gt;</code></a></li>
<li {% if page.path == 'SelectArrayInput.md' %} class="active" {% endif %}><a class="nav-link" href="./SelectArrayInput.html"><code>&lt;SelectArrayInput&gt;</code></a></li>