React-Admin: How to make <Filter> mandatory with "alwaysOn" using "list" - react-admin

I couldn't find a way to ONLY FETCH or LIST resources, when a user fills in the <SelectInput> (as rendered by <ReferenceInput>).
Due to a domain restriction,I need to ask the user to choose some Customer in order to query the Orders.
Is that possible? How can I pull it off?
To be clear, in their Demo project, the "Orders" can be listed using "Customer" as a <Filter>. (see image below)
A workaround could be also valid. Thanks.

Ok, this is an interesting scenario.
Let's closely look at the resource, and how it routes to various components.In this case, our interest is within the <list> and what exactly it renders:
// Set up the filter
const CustomerFilter = (props) => (
<Filter {...props}>
// setup your `ReferenceInput` and `SelectInput`
</Filter>
);
// Within your rendered component (OrderList)
<List {...props} filters={<CustomerFilter />}>
<Datagrid>
{console.log(props)} // Look out for the `match.params` prop
// ...
// Check for "customer_name" param within your props (verbose)
{match.params.customer &&
<TextField label="Customer" source="customer_name" />
}
</Datagrid>
</List>
Please note: don't just copy/paste this code. It's a verbose version.
The implication is that you can render customer name (or whatever) within your list depending on a passed parameter.

Related

How can I prepopulate the structure of a complex nested form on the Create button in a List view in react-admin?

The List View of react-admin provides a "create (new record)" button out of the box when I specify a Create view in the Resource.
Since my record structure is nested up to three levels, containing objects with objects with arrays a.s.o., starting with an empty record (just {}) leads to a bunch of "undefined" errors in the validation function and when I test certain values with a FormDataConsumer to fold/unfold parts of the form based on other values.
I want my Create view to always start with a predefined record structure. How can I do that?
So it looks like you need default values for create form.
Documentation: https://marmelab.com/react-admin/CreateEdit.html#default-values
const postDefaultValue = { created_at: new Date(), nb_views: 0 };
export const PostCreate = (props) => (
<Create {...props}>
<SimpleForm initialValues={postDefaultValue}>
<TextInput source="title" />
<RichTextInput source="body" />
<NumberInput source="nb_views" />
</SimpleForm>
</Create>
);
You can flatten all the nested structure, and restore the input data back to the nested structure before submission. This document may help you: https://marmelab.com/react-admin/CreateEdit.html#altering-the-form-values-before-submitting

How to make react-admin resource route default to show instead of edit?

The react-admin docs explain the default routing for resources as:
/posts/:id maps to PostEdit
/posts/:id/show maps to PostShow
I'd like my app to default to viewing, and only allow editing if the user clicks the edit icon. However the default for all the reference links is to point to /resource/:id.
Is there a way to swap the routing such that /resource/:id maps to Show and /resource/:id/edit is used for Edit? Or maybe change the reference links to /resource/:id/show?
This may not be exactly the thing you are looking for, but default actions can be switched, so click on item can lead to show instead of edit:
https://marmelab.com/react-admin/List.html#the-datagrid-component
export const PostList = (props) => (
<List {...props}>
<Datagrid rowClick="show">
...
</Datagrid>
</List>
);
Reference links can be set to reference "show" by add link="show" to the ReferenceField.
See https://github.com/marmelab/react-admin/issues/247#issuecomment-287819508
You might want to create some custom routes matching your needs redirecting to custom Create/Show components like
const customRoutes = [
<Route key="editPost" exact path="/posts/:id" component={PostEdit} />,
<Route key="showPost" exact path="/posts/:id/show" component={PostShow} />,
];
function App() {
return (
<Admin
...
customRoutes={customRoutes}
>
...
</Admin>
)
}
Source: https://marmelab.com/react-admin/Admin.html#customroutes

ArrayInput with SimpleFormIterator and conditional inputs

Let's say I have the following array in my create form
const CreateDefaults = {Inputs: [{id: "ID1", param: "a"},{id: "ID2", param: "b"}]};
and then I want to show extra TextInput only when id==="ID2"
export const MyCreate = withStyles(myStyles)(({ classes, ...props }) => (
<Create {...props}>
<SimpleForm defaultValue={CreateDefaults}>
<ArrayInput source="Inputs" {...props}>
<SimpleFormIterator {...props}>
<DisabledInput source="id" />
{/*this does not work*/}
{this.id === "ID2" ? (<TextInput source="ParamValue"/>) :null}
</SimpleFormIterator>
</ArrayInput>
</SimpleForm>
</Create>
));
How can I do something like that? I know that for the whole form, one can use FormDataConsumer. However, what can one do inside ArrayInput/SimpleFormIterator for each iteration?
How to access current object in iteration? I tried something like the answer given to the 'Custom Input do not receive record when inside ArrayInput' issue in the react-admin github page, but it still does not receive the record in custom input.
From the latest documentation here, you can see that if you use FormDataConsumer that is within an ArrayInput, you have a parameter called scopedFormData that you can use to get the current record and dereference that to get whatever fields you need. It usually also goes hand in hand with the getSource function you can use when setting the source within your FormDataConsumer.

Nested Stencil Routes/Components

I’ve been trying to implement nested routes/components. Can someone just please explain to me how to nest routes/components. The stencil route docs aren’t of much help.
Say In my component I have on the left a sidenav with a couple of stencil-route-link then on the right I should show the routed component.
As the Stencil team has still not released a wiki entry for this, here's a little update to this topic using #stencil/core: 1.3.2 and #stencil/router: 1.0.1.
The trick is to use the routeRender property in combination with slots and inline child routes:
<stencil-router>
<stencil-route-switch>
<stencil-route url="/"
exact={ true }
component="app-home"
/>
<stencil-route url="/me"
routeRender={ () => (
<app-me>
<stencil-route url="/me/login"
component="app-login"
/>
<stencil-route url="/me/profile"
component="app-profile"
/>
</app-me>
) }
/>
</stencil-route-switch>
</stencil-router>;
If you define the child routes inside your component (in this example app-me) the route may not be recovered after a reload or by navigating directly to it. Thus you have to define them inside your global stencil-route-switch.
StencilJS docs state:
You should have one single stencil-router component in your project. This component controls all interactions with the browser history and it aggregates updates through an event system.
So there's going to be one place to define your routes. There's also an example on their webpage:
<stencil-router>
<stencil-route url="/" component="landing-page" exact={true}/>
<stencil-route url="/demos" component="demos-page"/>
<stencil-route url="/demos/rendering" component="fiber-demo"/>
</stencil-router>
Entering /demos/rendering would render both demos-page and fiber-demo components. They won't be nested though.
The docs also mention the routeRender attribute, which can be used to do some sort of nesting. For example:
<stencil-route url="/" exact={true} routeRender={(props) => (
<app-root history={props.history}>
<app-sidebar />
<app-content />
</app-root>
) />
I know this is kind of old question but took me some time to figure this out, so I want to share here in case someone run into similar issues.
<stencil-router>
<stencil-route-switch scrollTopOffset={0}>
<stencil-route url="/" component="app-home" exact={true} />
<stencil-route url="/profile/:name" component="app-profile" />
<stencil-route>
<stencil-route-switch scrollTopOffset={0}>
<stencil-route url="/" component="app-home" exact={true} />
<stencil-route url="/profile/:name" component="app-profile" />
<stencil-route url="/docs" routeRender={()=>
<div>
<div>Document Heading</div>
<stencil-route-link url="/docs/setup">Setup</stencil-route-link>
<stencil-route-link url="/docs/todo">TODO</stencil-route-link>
<stencil-route-link url="/docs/profile">Profile</stencil-route-link>
<stencil-route url="/docs/setup" routeRender={()=>
<div>Set up document</div>}>
</stencil-route>
<stencil-route url="/docs/todo" routeRender={()=>
<div>TODO document</div>}>
</stencil-route>
<stencil-route url="/docs/profile" component="app-home" />
</div>
}>
</stencil-route>
</stencil-route-switch>
</stencil-router>
Some quick explanation:
As titsjmen shared, looks like it is important to have only one component within your app
You can, however nest "stencil-route" and hopefully with the code example above you can see it how it works. I tested it with both using routeRender as well as component approach (both works!)
Hopefully this will be useful to someone :)
EDIT (additional note):
In case this is not clear, this code block represents the area where you want to swap components based on the URL - back to original question, to achieve what you want, side nav can be just a matter of having bunch of "stencil-router-link" (which doesn't have to be placed in the same component - I have separate component called "sidenav-item" and seem to work well)

Nested Reference Field

In order to retrieve the equipment type I am using a that will retrieve the equipment model and then another that references the equipment type using the equipment model's field "typeID" to retrieve the equipment type.
However it displays the following warning:
Warning: Failed prop type: Invalid prop translateChoice of type
boolean supplied to ReferenceField, expected function.
The image represents the data model (an equipment has an equipment model, and an equipment model has an equipment type)
I found a better solution is kinda of an hack but seems to be more efficient.
Taking the question example where in order to get equipmentType is only needed <ReferenceField>, it would be something like this:
const EquipList = ({...props}) => {
<List {...props}>
<Datagrid>
<ReferenceFieldController label="Equipment Type" reference="equipmentModel" source="modelID" linkType={false}>
{({referenceRecord, ...props}) => (
<ReferenceField basePath="/equipmentModel" resource="equipmentModel" reference="equipmentType" source="typeID" record={referenceRecord || {}} linkType="show">
<TextField source="name" />
</ReferenceField>
)}
</RefenceFieldController>
</Datagrid>
</List>
}
In the example above <ReferenceFieldController> fetches the equipmentModel of equipment, as like <ReferenceField>. Label is needed because RA uses the first <ReferenceField> to show the column header in <Datagrid>, if you use internationalization you should apply translate function to the correct resource on this prop.
<ReferenceController> fetches the record and passes it as referenceRecord to a child function that will render the component for field presentation. Instead of presenting the field component you render a <ReferenceField> to fetch the nested relation and next you show the field. Since <ReferenceFieldController> only passes controller props to its child and the props of field component don't do what you want in the nested relation, you have to explicit pass them to <ReferenceField>. You need to pass record of <ReferenceField> as referenceRecord || {} because the initially the referenceRecord is not fetched yet and <ReferenceField> doesn't work with record as null.
Setting the linkType of <ReferenceFieldController> to false makes it not render a <Link> component that would redirect the user to an incorrect route.
Not a perfect fix, but to get around the translateChoice issue, you can create a wrapper and pluck out that prop to prevent it from being passed.
const SubReference = ({ translateChoice, children, ...props }) => (
<ReferenceField {...props}>{children}</ReferenceField>
);
While troubleshooting this, I was also receiving an error about nested a tags. I was able to silence the error by setting the linkType prop to false in the parent ReferenceField
<ReferenceField source="item_id" reference="list" linkType={false}>
<SubReference source="id_to_reference_from_list" reference="second_list">
<TextField source="name" />
</SubReference>
</ReferenceField>
I have the same problem and I think this is an actual bug. I commented on the corresponding github issue https://github.com/marmelab/react-admin/issues/2140
I looked into the code for ReferenceField and as far as I understood this is an actual bug. ReferenceField expects a function for the translateChoice property, but internally hands a boolean to ReferenceFieldView.
If you nest one ReferenceField into another the inner one receives false as translateChoice property and rightfully complains that it is a boolean and not a function.