Datagrid for nested endpoints in react-admin
Asked Answered
M

2

1

I am trying to understand what the right approach would be to address nested endpoints, let's assume I have a many to many books and authors relationship, and an API which exposes api/authors, api/books, and api/authors/{id}/books. This is a common design pattern.

The CRUD on api/authors works beautifully in react-admin. However, under the authors <Show> I want to show a <Datagrid> of all the books with pagination and sorting, which my api makes available under api/authors/{id}/books.

What is the right approach to make a datagrid of such a nested endpoint?

I've looked into the <ReferenceManyField> which works well in the one to many context but doesn't allow accessing nested endpoints, only filtering an endpoint.

Ideally I would want something that is along the lines of:

<Show {...props}>
    <TabbedShowLayout>
        <Tab label="Books">
            <NestedResourceField reference="books" nestedResource={`authors/${props.record.id}/books`} pagination={<Pagination/>} >
                <Datagrid>
                    <TextField source="name" />
                </Datagrid>
            </NestedResourceField>
        </Tab>
    </TabbedShowLayout>
</Show>

Note that <NestedResourceField> is a hypothetical component which would have a behavior very similar to <ReferenceManyField> but would accept a nested endpoint under nestedResource instead of target.

I am struggling to understand what the design strategy should be for the hypothetical <NestedResourceField> in order to re-use as much of the react-admin framework as possible.

It would be straightforward to "manually" do the fetch myself and list the content but then I would lose all the pagination, filtering, sorting, etc... that comes with react-admin and the fact that books is an already defined resource.

My question is similar to these unanswered questions:

custom routes in react-admin

custom path for resource route in react-admin

Edit

Turns out an almost identical question which I had not found previously was posted here: Support for resource nesting

Merat answered 5/7, 2019 at 18:24 Comment(0)
M
3

So I decided to solve this with a hack in the dataProvider.

Keeping with the above example and using the stock ReferenceManyField:

<Show {...props}>
    <TabbedShowLayout>
        <Tab label="Books">
            <ReferenceManyField reference="books" target="_nested_authors_id" pagination={<Pagination/>} >
                <Datagrid>
                    <TextField source="name" />
                </Datagrid>
            </ReferenceManyField>
        </Tab>
    </TabbedShowLayout>
</Show>

I then modified my dataProvider, which is a fork of ra-jsonapi-client. I changed index.js under the case GET_MANY_REFERENCE from this:

      // Add the reference id to the filter params.
      query[`filter[${params.target}]`] = params.id;

      url = `${apiUrl}/${resource}?${stringify(query)}`;

to this:

      // Add the reference id to the filter params.
      let refResource;
      const match = /_nested_(.*)_id/g.exec(params.target);
      if (match != null) {
        refResource = `${match[1]}/${params.id}/${resource}`;
      } else {
        query[`filter[${params.target}]`] = params.id;
        refResource = resource;
      }

      url = `${apiUrl}/${refResource}?${stringify(query)}`;

So basically I just remap the parameters to the url for the special case where the target matches a hard coded regex.

ReferenceManyField would normally have caused the dataProvider to call api/books?filter[_nested_authors_id]=1 and this modification makes the dataProvider call api/authors/1/books instead. It is transparent to react-admin.

Not elegant but it works and doesn't seem to break anything on the front end.

Merat answered 5/7, 2019 at 21:2 Comment(1)
Nice solution @vchev005. Is this still your preferred way to handle nested resources such as api/authors/{id}/books? ThanksSpectator
S
-1

Try using ArrayField component:

<Show {...props}>
    <TabbedShowLayout>
        <Tab label="Books">
            <ArrayField source="books" label="">
              <Datagrid>
                 <TextField source="name" />
              </Datagrid>
            </ArrayField>
        </Tab>
    </TabbedShowLayout>
</Show>

Where books is your nested resource field. In your "author" end point you should include books.

Straw answered 10/2, 2020 at 3:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.