0

The use case is - I wanted to do CRUD operation on a child resource of a parent.

Parent: /home/:id/device/:id

Child: /home/:id/device/:id/file/:id

Currently I have the parent CRUD working. Was thinking to have an action button/menu (on the parent List component) to take the flow into Child CRUD components. Is this the right approach or any other better solution available?

3 Answers3

1

If this is something like the customers button for segments in the demo (http://localhost:3000/#/Segment), check the source: https://github.com/marmelab/admin-on-rest-demo/blob/master/src/segments/LinkToRelatedCustomers.js

Gildas Garcia
  • 6,966
  • 3
  • 15
  • 29
  • Thanks @Gildas. But this use case is different. The Customer & Segments can live independent of each other. And that customer references a segment in which he/she falling into as an association, not as in parent/child. Whereas, in the use case that I am having - Device (vs) Files, the file resource cannot exist without a device resource. In other words, a file resource is always manipulated in the context of a device resource (file resource path in the Router having a device id in the path as a prefix - representing the context of the file). – Sundar Rajan Muthuraj Jul 01 '17 at 11:10
  • Do you have a resource declared for `File` too or is it embedded inside `Device`? – Gildas Garcia Jul 02 '17 at 11:56
  • Yes, file is a declared resource within `` (sorry about delayed response). – Sundar Rajan Muthuraj Jul 06 '17 at 06:36
0

List accepts actions so you can theoretically do anything.

https://marmelab.com/admin-on-rest/List.html#actions

But your use case is a bit unclear. More details will help

kunal pareek
  • 1,285
  • 10
  • 21
  • Thanks for the pointer. More clarity - While I am in the List component of the parent - there will be a button like 'File' (Similar to Edit or Delete) and on click of the File button, I am currently pushing the parent context (/device:id) into local storage and pushing the router path to /file that shows the File Component. I was not sure if this is the right way to handle a child resource (relative to a parent resource) with AOR. – Sundar Rajan Muthuraj Jun 28 '17 at 10:18
  • I think it should be fine. I do not think there is any canonical way to do this right now. Admin-On-Rest isnt really that opinionated a framework. You can think of it as a Redux app with a lot of support for basic stuff built in. As long as you are using Redux and Sagas I think you should be fine. However the guys who maintain aor are pretty active here. So they may have a different opinion about this. – kunal pareek Jun 28 '17 at 10:24
0

I wanted to the same thing and managed (more or less). I'm not entirely happy with the solution mainly because I need specify an initial state for <Admin />. This is needed, because if I access the child URL directly, the AOR/DECLARE_RESOURCES action doesn't finish on time and I have an empty state for the parent and the child. (any pointers would be much appreciated why is this happening)

I have a model called providers and one can upload multiple images for a provider.

You can find descriptions on the how and why in the code sample. I hope this helps you.

// ProviderPhoto Components

export const ProviderPhotoList = (props) => (
  // have to pass in the resource here otherwise this would be done by <CrudRoute /> but in our case
  // the <Create> and the <List> is not the child of <Resource />
  // also hasCreate=true because we want to show the new image button and filter is hardcoded with the providerId coming from the route (see below)
  <List {...props} resource="provider-photos" perPage={25} hasCreate={true} filter={{ provider: props.providerId }}>
    <ProviderPhotoGrid />
  </List>
)

export const ProviderPhotoCreate = (props) => (
  // have to pass in the resource here otherwise this would be done by <CrudRoute /> but in our case
  // the <Create> and the <List> is not the child of <Resource />
  <Create resource="provider-photos" {...props}>
    <SimpleForm>
      <ReferenceInput source="provider" allowEmpty reference="providers" validate={ required } defaultValue={props.providerId}> 
        <SelectInput optionText="name" />
      </ReferenceInput>
      <ImageInput source="photos" accept="image/*" maxSize={10*1024*1024} multiple>
        <ImageField source="src" title="" validate={ required }/>
      </ImageInput>
    </SimpleForm>
  </Create>
)

// customRoutes.js
import { Route } from 'react-router-dom';

export default [
  // few things to note here:
  //  - using render instead of component, otherwise the component gets created too early
  //  - passing in the provider (parent) ID from the route params
  //  - passing in ...routeProps (List Create Edit needs it)
  <Route exact path="/providers/:providerId/provider-photos" render={(routeProps) => <ProviderPhotoList providerId={routeProps.match.params.providerId} {...routeProps}/>} />,
  <Route exact path="/providers/:providerId/provider-photos/create" render={(routeProps) => <ProviderPhotoCreate providerId={routeProps.match.params.providerId} {...routeProps}/>} />,
];

// App.js

// hacky :(
const initialState = {
  admin: {
    resources: {
      'providers': { props: { name: 'providers' }, data: {}, list: { ids: [], params: { sort: null, order: null, page: 1, perPage: null, filter: {} }, total: 0 } },
      'provider-photos': { props: { name: 'provider-photos' }, data: {}, list: { ids: [], params: { sort: null, order: null, page: 1, perPage: null, filter: {} }, total: 0 } }
    }
  }
};

const App = () => (
  <Admin customRoutes={customRoutes} restClient={restClient} initialState={initialState}>
    <Resource name="providers" list={ProviderList} create={ProviderCreate} edit={ProviderEdit} remove={Delete} />
    {/* no list,create,edit etc... specified, we just register the resource */}
    <Resource name="provider-photos"/>
  </Admin>
);
jkrnak
  • 956
  • 6
  • 9