Conditionally render TextFields in react-admin [duplicate] - reactjs

Some fields I want to only show if they have a value. I would expect to do this like so:
<Show {...props} >
<SimpleShowLayout>
{ props.record.id ? <TextField source="id" />: null }
</SimpleShowLayout>
</Show>
But that doesn't work. I can make it somewhat work by making each field a higher order component, but I wanted to do something cleaner. Here's the HOC method I have:
const exists = WrappedComponent => props => props.record[props.source] ?
<WrappedComponent {...props} />: null;
const ExistsTextField = exists(TextField);
// then in the component:
<Show {...props} >
<SimpleShowLayout>
<ExistsTextField source="id" />
</SimpleShowLayout>
</Show>
This correctly shows the value, but strips the label.

We need to update our documentation about this. In the mean time, you can find informations about how to achieve that in the upgrade guide: https://github.com/marmelab/react-admin/blob/master/UPGRADE.md#aor-dependent-input-was-removed
Here's an example:
import { ShowController, ShowView, SimpleShowLayout, TextField } from 'react-admin';
const UserShow = props => (
<ShowController {...props}>
{controllerProps =>
<ShowView {...props} {...controllerProps}>
<SimpleShowLayout>
<TextField source="username" />
{controllerProps.record && controllerProps.record.hasEmail &&
<TextField source="email" />
}
</SimpleShowLayout>
</ShowView>
}
</ShowController>
);

Maybe this way can be useful
import { FormDataConsumer } from 'react-admin'
<FormDataConsumer>
{
({ formData, ...rest}) => formData.id &&
<>
<ExistsTextField source="id" />
</>
}
</FormDataConsumer>

Related

React Admin: How to run method before return <Edit> form view

Im making app in React-Admin Framework. Now I am creating the edit form:
<Resource name="order" edit={OrderEdit} />
And I want to decide whether to show Edit View based on a record.
E.g If record 'id' from data provider is equal to 5, display an alert and redirect to the main view '/orders':
const OrderEdit = (props) => {
const checkId = (id) => {
if(id==5){
alert('You cannot edit an order where ID Input is 5');
useRedirect('/orders');
}
}
//checkId(this.props.record.id); <=== HERE IS MY PROBLEM
/*
1. this.props.record.id = undefined;
2. props.record.id = undefined;
3. record.id = undefined;
*/
return (
<Edit {...props} >
<SimpleForm>
<TextInput
source="id"
label="ID Input"
/>
</SimpleForm>
</Edit>
);
};
export default OrderEdit;
But I don't know how to access record.id before return.
I know that I can do it with FunctionField:
<FunctionField
render={(record) =>{
if(record) checkId(record.id)
return null;
}}
label="test"
/>
But it doesn't look right.
You do not need this. since its a functional component where you pass props as an argument.
You could return null if record.id is not the correct, which is the same way as saying display nothing:
if (checkId(this.props.record.id)) return null
return (
<Edit {...props} >
<SimpleForm>
<TextInput
source="id"
label="ID Input"
/>
</SimpleForm>
</Edit>
);
Or more clean:
return checkId(this.props.record.id) ? null : (
<Edit {...props} >
<SimpleForm>
<TextInput
source="id"
label="ID Input"
/>
</SimpleForm>
</Edit>
);
I don't know if you can access the record in the Edit view component because it calls a getOne internally and forward the record to its child. So you can eventually wrap your form to access it like:
const WrapperForm = (props) => {
const {record} = props;
return (
<SimpleForm {...props}>
<TextInput source="id" label="ID Input"/>
</SimpleForm>
);
}

how to get data from form to redirect SimpleForm edit

I have a react component below. and I want to redirect with the dataset response from dataset_id. How to i get the data to put into the redirect field for simple form ?
export const qnaEdit = props => {
return (
<Edit {...props}>
<SimpleForm
redirect="data_sets">
<TextInput disabled source="id" />
<TextInput datasetid="dataset_id" />
<TextInput source="question" />
<TextInput multiline source="short_answer" />
{/* <RichTextField multiline source="url" /> */}
</SimpleForm>
</Edit>
);}
RedirectTo can be a function, not only string, so it should help.
if (typeof redirectTo === 'function') {
return redirectTo(basePath, id, data);
}
https://github.com/marmelab/react-admin/blob/master/packages/ra-core/src/util/resolveRedirectTo.ts
Documentation: https://marmelab.com/react-admin/CreateEdit.html#redirection-after-submission
You can also pass a custom route (e.g. “/home”) or a function as redirect prop value. For example, if you want to redirect to a page related to the current object:
// redirect to the related Author show page
const redirect = (basePath, id, data) => `/author/${data.author_id}/show`;
export const PostEdit = (props) => {
<Edit {...props}>
<SimpleForm redirect={redirect}>
...
</SimpleForm>
</Edit>
);
This affects both

Access the values of components in react-admin

I want to access values of components. Because I'll use it to access to another data. For example;
<ArrayInput source='services'>
<SimpleFormIterator>
<ReferenceInput label="Service Type"
source="serviceType"
reference="servicetypes"
validate={required()}>
<SelectInput optionText={GAMMA_CONSTANTS.SOURCE_ID} />
</ReferenceInput>
{(this.state.serviceTypes && this.state.serviceTypes.length > 0) ?
this.state.serviceTypes.filter(serviceType => {
return serviceType.id === (**source="serviceType"**)
})[0]["datapointtype"].map((feature, index) => {
return <TextInput source={index} label="deneme" />
})
: null}
</SimpleFormIterator>
</ArrayInput>
I want to access it in the filter method. Is there a way to do that?
You can use something like this:
import { FormDataConsumer } from 'react-admin';
const PostEdit = (props) => (
<Edit {...props}>
<SimpleForm>
<BooleanInput source="hasEmail" />
<FormDataConsumer>
{({ formData, ...rest }) => formData.hasEmail &&
<TextInput source="email" {...rest} />
}
</FormDataConsumer>
</SimpleForm>
</Edit>
);
Reference: https://marmelab.com/react-admin/Inputs.html#hiding-inputs-based-on-other-inputs
You're not limited to hide/show things. In your case, you should be able to avoid using state and directly access formData.serviceTypes

Cutomize Delete button react-admin

Is there any way to customise DeleteButton button in react-admin to add a confirmation message like 'Do you want to delete item?'. Currently on clicking on DeleteButton it directly deletes the item without asking for a confirmation. I tried adding title attribute to delete button but it does not get fired.
Here is my code
//This worked with admin-on-rest, but not working with react-admin
const CategoryDeleteTitle = translate(({ record, translate }) => <span>
{translate('Delete')}
{record && `${record.code} ${record.name}`}
</span>);
const EditActions = ({ basePath, data, resource }) => (
<CardActions>
<ShowButton basePath={basePath} record={data} />
<ListButton basePath={basePath} />
<DeleteButton title={<CategoryTitle />} basePath={basePath} record={data} resource={resource} />
</CardActions>
);
export const CategoryEdit = (props) => (
<Edit actions={<EditActions />} title={<CategoryTitle />} {...props}>
<SimpleForm>
<DisabledInput source="id" />
<TextInput source="name" />
</SimpleForm>
</Edit>
);
We now handle deletions in an optimistic way, providing an undo mechanism instead of a confirmation dialog.
If this doesn't suit you, you can follow the UPGRADE GUIDE which leads to this page of the documentation: https://marmelab.com/react-admin/CreateEdit.html#actions
Note that you'll have to create and handle your confirmation dialog using something like react-modals and dispatch the DELETE action yourself.
You can use this gist with custom actions like:
const UserEditActions = ({ basePath, data, resource }) => (
<CardActions>
<ListButton basePath={basePath} />
<DeleteButtonWithConfirmation basePath={basePath} record={data} resource={resource} undoable={false} />
<RefreshButton />
</CardActions>
);
export const UserEdit = ({ ...props }) => (
<Edit {...props} actions={<UserEditActions />} >
<CreateEditForm title={<EntityTitle label="User" />} />
</Edit>
);
In react-admin v3, there is now a DeleteWithConfirmButton :-)
According to the documentation "https://marmelab.com/react-admin/CreateEdit.html" create:
const CustomToolbar = props => (
<Toolbar {...props} classes={useStyles()}>
<SaveButton />
<DeleteButton undoable={false} />
</Toolbar>
);
import from react-admin button you need like this:
import {
Toolbar,
SaveButton,
DeleteWithConfirmButton
} from 'react-admin';
see all available names here https://github.com/marmelab/react-admin/tree/master/packages/ra-ui-materialui/src/button, and change DeleteButton on ImportedButton like this:
export const CustomToolbar = props => (
<Toolbar {...props} classes={useStyles()}>
<SaveButton/>
<DeleteWithConfirmButton/>
</Toolbar>
);
And change in the code, where you need <SimpleForm toolbar={<CustomToolbar />}>.

Is it possible to have conditional columns in a List/DataGrid in Admin on REST?

If I apply a filter in a List component in Admin on REST, I want to only show the filtered column (TextField) once this specific filter is selected, because the filter is fuzzy and I want to see the different results at a glance. But when no filter is set, the column is supposed to be hidden.
I implemented it like this so far.
const ConditionalFilterTextField = (props) => {
const show = ... // if filter is set in URL
return (show) ? <TextField {...props} /> : null
}
...
<List {...props} filters={<MyFilter />}>
<Datagrid>
...
<ConditionalFilterTextField sortable={false} source="filter_field" label="My Label" />
...
</Datagrid>
</List>
This does work in a way, but then again not really. It shows the label of the column and an empty space when I return null (when the filter is not set and the column is supposed to be hidden).
How can I control visibility of a TextField depending on whether a filter is set or not? How can I force a rerender of the List when I want to add a column?
Edit:
This is similar to your answer, #Gildas, but doesn't work for me. Some detail might be wrong?
in itemList.js
class ItemList extends Component {
render() {
const props = this.props
// Find out if current filter is source of field.
const filter = props.itemsFilters
const showFieldA = (filter && filter.fieldA) ? true : false
return <List {...props} filters={<ItemsFilter />} perPage={50}>
<Datagrid>
<ReferenceField sortable={false} source="id" reference="items" linkType="show">
<TextField source="fieldB" />
</ReferenceField>
<TextField sortable={false} source="fieldC" />
<TextField sortable={false} source="fieldD" />
<DateField sortable={false} source="fieldE" />
{ (showFieldA === true) ? <TextField sortable={false} source="fieldA" /> : null }
<ShowButton />
<EditButton />
</Datagrid>
</List>
}
}
const ItemsFilter = (props) => (
<Filter {...props}>
<TextInput label="FilterSearch" source="q" />
<TextInput label="FilterA" source="fieldA" />
</Filter>
)
function mapStateToProps(state) {
return {
itemsFilters: state.admin.resources.items.list.params.filter
}
}
export default connect(mapStateToProps)(ItemList)
...
in App.js
<Resource name="items" list={ItemList} show={ItemShow} edit={ItemEdit} />
const MyList = (props) => {
const show = ... // if filter is set in URL
return (
<List {...props} filters={<MyFilter />}>
<Datagrid>
...
{show ? <TextField {...props} /> : null}
...
</Datagrid>
</List>
);
}

Resources