Access Property of a Nested Record

Viewed 941

I'm using React-Admin 3.5.0 with api-platform/admin 2.1.1.

I have a custom list for one of my resources.
The resource has a title and an author. The author has several properties, one of which is username.

Following the documentation, I tried to display the username of the author in my list like this:

export const MyList = (props) =>
    <List {...props}>
        <Datagrid>
            <TextField source="title" />
            <TextField source="author.username" />
        </Datagrid>
    </List>

This does not work. The field for author.username is empty.

I investigated a bit and found that the record only contains the ID of the author:

record:
    ...
    title: "abcde"
    author: "/users/1"

The response from the server however does contain the username property:

0: {
    ...,
    "title": "abcde",
    "author": {
        "@id": "/users/1",
        "username": "Test"
    }
},
...

Is there any nice way to make it work?

3 Answers

Found this on api-platform admin source code:

Replace embedded objects by their IRIs, and store the object itself in the cache to reuse without issuing new HTTP requests.

And right below:

document[key] = document[key]['@id'];

https://github.com/api-platform/admin/blob/ba0630083f28eaa8806bc1da4613677924604a52/src/hydra/dataProvider.js#L75

This behavior came from this pull request:

https://github.com/api-platform/admin/pull/96

To display author fields, you will need to use a ReferenceField for each one of them:

export const MyList = (props) =>
    <List {...props}>
        <Datagrid>
            <TextField source="title" />
            <ReferenceField source="author" reference="users">
                <TextField source="username"/>
            </ReferenceField>

            <ReferenceField source="author" reference="users" link={false}>
                <TextField source="email"/>
            </ReferenceField>
        </Datagrid>
    </List>

Doing it will make the author fields link to the author page. You can disable this passing link={false} on the ReferenceField, like on the second author field above

React admin will pack all those author reference requests into a single one, so even if api-platform data provider woun't do it's cache promise, you will endup with only one http request.

Note: You must add a <Resource> for the reference resource - react-admin needs it to fetch the reference data. You can omit the list prop in this reference if you want to hide it in the sidebar menu.

version 2.2 should support nested/embedded objects:

See https://api-platform.com/docs/admin/handling-relations/#display-a-field-of-an-embedded-relation

I tried it. Unfortunately, it didn't work for me...

import {
    fetchHydra
    HydraAdmin,
    hydraDataProvider as baseHydraDataProvider,
} from "@api-platform/admin";

const dataProvider = baseHydraDataProvider(
    entrypoint,
    fetchHydra,
    apiDocumentationParser,
    true // use embedded!
);

export default () => (
    <HydraAdmin
        dataProvider={dataProvider} authProvider={authProvider} entrypoint={entrypoint} i18nProvider={i18nProvider}>
        <ResourceGuesser name={"users"} list={UserList} icon={UserIcon}/>
         ....
    </HydraAdmin>

record input v.s. transmitted input

How to debug

Log the converted record data into your console (this causes some error, but at least you see what really happens).

const TagsField = ({record, ...props}) => (
    <ArrayField source="images">
        {console.log("Record:")}
        {console.log(record)}
        <TextField source="image"/>
    </ArrayField>
)
... 
export const HouseList = (props) => (
<ListGuesser title="houses" {...props}>
    ....
    <NumberField source="price" label="Price (€)"/>
    <TagsField label="Images" source="images" sortable={false}>
    </TagsField>
    ....

Has someone an idea, what I did wrong over here?

Even more details

https://github.com/api-platform/admin/pull/299

Related