BaptWaels
BaptWaels

Reputation: 1704

Add Nested resource using admin-on-rest

I'm using admin-on-rest. What a great framework ! It is working quite well for my flat resources.

I'm retrieving a list of projects /projects. I've implemented a ProjectEdit View to edit a given project /projects/:id

export const ProjectEdit = props => {
    const projectId = props.match.params.id;

    return (
        <Edit title={<ProjectTitle />} {...props}>
            <TabbedForm>
                <FormTab label="Project">
                    <DisabledInput source="id" />
                    <TextInput source="name" />
                </FormTab>
                <FormTab label="Companies">
                    // WHAT Should I put here to edit my companies list ???
                </FormTab>
            </TabbedForm>
        </Edit>
    );
};

I'd like to implement a new tab from which I'd be able to add / remove companies from my project.

GET /projects/:id/companies

{
    "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 2,
            "name": "My Company"
        }
    ]
}

POST /projects/:id/companies

DELETE /projects/:id/companies/:company_id

I've tried to use a ReferenceManyField but I'm unable to configure it since my API requires a custom GET. My projects don't have any reference to companies. I can't find any example on how to perform it.

Any clues please ? :)


EDIT1: I'm able to display a list of my project's companies using @malisbad answer. But I'm still not able to display it for edition (eg: Using a ReferenceArrayInput + SelectArrayInput)

Upvotes: 3

Views: 3581

Answers (2)

BaptWaels
BaptWaels

Reputation: 1704

I've found a solution to my problem. I'm not really happy with it but it works... I've created a Datagrid to remove the given company from the project. The Button gets as params record, resourceId.

I've also added a ReferenceInput to let me add one company at a time when the user hit Save

I've used a custom RestClient.js to handle GET_MANY_REFERENCE call and use the given url to retrieve my project's companies. I've done the same to handle UPDATE when the user hit save to add a new company.

EditProject.js :

<FormTab label="Entreprises">
    <ReferenceManyField label="Entreprises" reference="companies" target={{ resource: "projects", resourceId: projectId, subresource: "companies" }} allowEmpty>
        <Datagrid>
            <TextField source="id" />
            <TextField source="name" />
            <RemoveBtn resourceId={projectId} />
        </Datagrid>
    </ReferenceManyField>

    <ReferenceInput label="Company to Add" source="companyToAdd" reference="companies" allowEmpty>
        <SelectInput optionText="name" />
    </ReferenceInput>
</FormTab>

RemoveBtn.js :

export class RemoveBtn extends React.Component {
    handleRemove = () => {
        const {record, resourceId } = this.props;
        projectsService.removeCompanyFromProject(resourceId, record.id).then(() => window.location.reload());
    };

    render() {
        return <FlatButton label="Retirer" secondary onClick={this.handleRemove} />;
    }
}

Hope it helps...

Upvotes: 2

Malisbad
Malisbad

Reputation: 189

You have to use a custom restClient. I had the same problem, so if you haven't already, create it.

Then look at how the data comes through to each method that you want to have subresources for and modify the URL and request that you'll fire off from there.

For example, in GET_MANY_REFERENCE I have target contain an ENUM that specifies that I'm looking for a subresource.

let url = `${apiUrl}/${resource}`;
case GET_MANY_REFERENCE: {
    let target;
    try {
      target = JSON.parse(params.target);
    } catch (err) {
      target = {};
    }

    /*
      Reference fields do not support the format of
      /<resource>/<resource id>/<subresource> so the target parameter is used
      to instead indicate the subresource call. See
      https://marmelab.com/admin-on-rest/Fields.html#referencefield
      for details on reference fields
    */
    if (target && target[ENUMS.SUBRESOURCE_IDENTIFIER]) {

      /*
        If a resource ID is present, it's because the base resource doesn't match the
        host resource and needs to use its own ID, not the host resource ID.
      */
      if (target[ENUMS.RESOURCE_ID]) {
        url = `${url}/${target[ENUMS.RESOURCE_ID]}/${target.subresource}`;
      } else {
        url = `${url}/${params.id}/${target.subresource}`;
      }
    }

You can then extend this to other parts of your REST client

Upvotes: 3

Related Questions