How do you conditionally show fields in "Show" component in react-admin? - conditional-statements

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

How to remove "No results found" label from the list when DataGrid has empty component

const TabbedDatagrid = (props: TabbedDatagridProps) => (
<Datagrid empty={<Empty />}>
<TextField source="id" />
<TextField source="createDate" />
</Datagrid>
);
const Empty = () => {
const { basePath } = useListContext();
return (
<Box textAlign="center" m={1}>
<Typography variant="h4" paragraph>
No products available
</Typography>
<Typography variant="body1">
Create one or import from a file
</Typography>
<CreateButton basePath={basePath} />
<Button>Import</Button>
</Box>
);
};
const MyList = (props: ListProps) => (
<List
{...props}
sort={{ field: 'createDate', order: 'DESC' }}
perPage={10}
filters={orderFilters}
pagination={<PostPagination />}
>
<TabbedDatagrid />
</List>
);
In the result both are displayed, 'No results found" and Empty component
How to display only Empty component?
The <Pagination> component takes a limit prop.
From the React-Admin docs
limit: An element that is displayed if there is no data to show (default: <PaginationLimit>)
You could try passing null <PostPagination limit={null} /> to hide that default "No results found" message.
You can replace these messages with your: Translating The Empty Page

Passing state via route.params with React Navigation returning undefined

I'm attempting to pass a 'passcode' state as params in my React Native app.
I'm doing this by passing params into a 'navigation.navigate' call.
However, every time I navigate to the next screen, it's returning 'undefined' for 'route.params'.
For reference, here is my component I'm passing data FROM:
const SignUpPasscodeScreen = ({ navigation }) => {
const [passcode, setPasscode] = useState(0)
return (
<View>
<View style={styles.mainView}>
<SubLogo />
<Heading title="Set passcode" />
<SubHeading content="You'll need this anytime you need to access your account." />
<Input inputText={ text => setPasscode(text) } inputValue={passcode} />
</View>
<View style={styles.subView}>
<CtaButton text="Continue" onPressFunction={ () => navigation.navigate({ routeName: 'SignUpLegalName', params: { passcode } } ) } />
</View>
</View>
)
}
And here's the component I'm passing data to, and where the error occurs upon navigation:
const SignUpLegalName = ({ route, navigation }) => {
const { passcode } = route.params
return (
<View>
<View style={styles.mainView}>
<SubLogo />
<Heading title="Tell us your name" />
<SubHeading content="This needs to be the same as what's on your passport, or any other form of recognised ID." />
<Input />
<Input />
</View>
<View style={styles.subView}>
<CtaButton text="Continue" onPressFunction={ () => navigation.navigate('SignUpLink')} />
</View>
</View>
)
}
I've tried two forms of passing the props through:
Passing it in as a second argument
Passing it in as a 'params' object as shown above
Both should work according to the documentation - link here
For reference, this is my route structure:
const switchNavigator = createSwitchNavigator({
loginFlow: createStackNavigator({
SignUpPasscode: SignUpPasscodeScreen,
SignUpLegalName: SignUpLegalName,
})
});
The above structure doesn't say to me that it's a nested structure which therefore requires any additional work to pass it through...
Can someone help? It'd be appreciated as it's giving me a headache!
Have a try with below code in the button press event:
<CtaButton
text="Continue"
onPressFunction={() => navigation.navigate('SignUpLegalName',{ passcode })}
/>

How to scope field components based on record properties

In react-admin, how do I access the record properties to show only certain components based on a record property value?
In the following code I want to only show the Bearer Token if the record name is not "All"; if the record name is "All", then only show if the user's role is "admin".
I'm getting "Uncaught TypeError: Cannot read property 'name' of undefined" in the OrgShow component, shown below, which I believe is saying that there is no "props". I've also tried "record" and "name", none of which are available.
export const OrgShow = ({ permissions, ...props }) => (
<Show permissions={permissions} title={<Title />} {...props}>
<SimpleShowLayout permissions={permissions}>
<TextField source="id" />
<TextField source="name" />
{
(props.record.name === 'All' && permissions === 'admin') || (props.record.name !== 'All' && permissions === 'user')
? <TextField source="bearer_token" />
: [
<FunctionField label='Bearer Token' render={record => obfuscator(record.bearer_token)} />,
<Alert severity="warning">You are not authorized for this org.</Alert>
]
}
</SimpleShowLayout>
</Show>
);
UPDATE:
I ended up solving this by using a FunctionField component, which I think is similar to user striped's solution; main difference being that FunctionField seems to be great for one-off function, whereas creating a new component seems appropriate if you're going to use that logic for more than one source. Anyway, here's the solution I came up with:
export const OrgShow = ({ permissions, ...props }) => (
<Show permissions={permissions} title={<Title />} {...props}>
<SimpleShowLayout permissions={permissions}>
<TextField source="id" />
<TextField source="name" />
<FunctionField permissions={permissions} label='Bearer Token' render={record => {
if ((record.name === 'All' && permissions === 'admin') || record.name !== 'All') {
return record.bearer_token
} else {
return obfuscator(record.bearer_token)
}
}} />
</SimpleShowLayout>
</Show>
);
I think that you need to create a specific component. The record should be automatically propagated to the child:
const MyComponent = (props) => {
return (props.record.name === 'All' && props.permissions === 'admin') || (props.record.name !== 'All' && props.permissions === 'user')
? <TextField source="bearer_token" record={props.record}/>
: [
<FunctionField label='Bearer Token' record={props.record} render={record => obfuscator(record.bearer_token)} />,
<Alert severity="warning">You are not authorized for this org.</Alert>
]
}
export const OrgShow = ({ permissions, ...props }) => (
<Show permissions={permissions} title={<Title />} {...props}>
<SimpleShowLayout permissions={permissions}>
<TextField source="id" />
<TextField source="name" />
<MyComponent permission={permissions} />
</SimpleShowLayout>
</Show>
);

Display Field in Edit Form

Currently, if I try to place a Field into an Edit form, the field doesn't display at all. There is no errors in the console or the terminal about why it wont.
Example:
<Edit undoable={false} {...props}>
<SimpleForm>
<FormRow>
<TextField source="id"/>
<TextField source="name"/>
</FormRow>
</SimpleForm>
</Edit>
will not display either of these on the page load, it will simply be blank.
Is there any way to use fields in the Edit form?
You need to pass in the record prop (and basePath if its a reference).
The Edit component does not get the record prop so create a form component and it will get passed the record as a prop
eg.
const ProjectEdit: FC<EditComponentProps> = props => {
const classes = useStyles();
return (
<RA.Edit {...props} title={<ProjectTitle />}>
<RA.SimpleForm>
<ProjectForm />
</RA.SimpleForm>
</RA.Edit>
);
};
export const ProjectForm = (props: any) => {
return (
<Box flex={1} mr={{ md: 0, lg: '1em' }}>
<RA.TextInput source="name" fullWidth={true} />
<Typography variant="h6" gutterBottom>
Tasks
</Typography>
<RA.TextField
source="name"
fullWidth={true}
record={props.record}
/>
<RA.ReferenceManyField
label="Tasks"
reference="Task"
target="projectId"
fullWidth={true}
record={props.record}
basePath="/Task"
>
<RA.SingleFieldList fullWidth={true}>
<RA.ChipField source="name" fullWidth={true} />
</RA.SingleFieldList>
</RA.ReferenceManyField>
</Box>
);
};

React-Admin - How to log record data to the console?

I'd like to know what are the data I'm working with, when using react-admin. (see them in the console to help debug)
For instance, when I'm working with a List:
import React from 'react';
import { Datagrid, List, TextField } from 'react-admin';
import { ListProps } from '../../types/admin/ListProps';
const ProductList = (props: ListProps): JSX.Element => {
console.log('ProductList.props', props);
return (
<List
{...props}
sort={{
field: 'titleEN',
order: 'DESC',
}}
>
<Datagrid rowClick="edit">
<TextField source="title" />
<TextField source="titleEN" label={'Title (EN)'} />
<TextField source="titleFR" label={'Title (FR)'} />
{/*<ArrayField source="images"><SingleFieldList><ChipField source="id" /></SingleFieldList></ArrayField>*/}
{/*<ReferenceArrayField source="imagesIds" reference="images"><TextField source="id" /></ReferenceArrayField>*/}
<TextField source="customer.label" label={'Customer'} />
<TextField source="price" />
</Datagrid>
</List>
);
};
export default ProductList;
How can I do that? I haven't found anything in the official doc https://marmelab.com/react-admin/List.html
This kind of logging can be displayed through the Data Provider. See https://marmelab.com/react-admin/DataProviders.html
For instance, with https://github.com/marcantoine/ra-data-graphql-prisma data provider, one such implementation could be https://github.com/UnlyEd/ra-data-graphql-prisma/commit/4031c5c3f2e97c479a9714df56da06653a908444