Datagrid for nested endpoints in react-admin

2019-08-28 06:22发布

问题:

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

回答1:

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.