UNPKG

react-admin

Version:

A frontend Framework for building admin applications on top of REST services, using ES6, React and Material UI

1,049 lines (779 loc) 37.2 kB
--- layout: default title: "Field Components" --- # Field Components A `Field` component displays a given property of a REST resource. Such components are used in the `List` view, but you can also use them in the `Edit` and `Create` views for read-only fields. The most usual of all field components is `<TextField>`: ```jsx // in src/posts.js import React from 'react'; import { List, Datagrid, TextField } from 'react-admin'; export const PostList = (props) => ( <List {...props}> <Datagrid> <TextField source="id" /> <TextField source="title" /> <TextField source="body" /> </Datagrid> </List> ); ``` All field components accept the following attributes: * `source`: Property name of your entity to view/edit. This attribute is required. * `label`: Used as a table header of an input label. Defaults to the `source` when omitted. * `sortable`: Should the list be sortable using `source` attribute? Defaults to `true`. * `className`: A class name (usually generated by JSS) to customize the look and feel of the field element itself * `cellClassName`: A class name (usually generated by JSS) to customize the look and feel of the field container (e.g. the `<td>` in a datagrid). * `headerClassName`: A class name (usually generated by JSS) to customize the look and feel of the field header (e.g. the `<th>` in a datagrid). * `addLabel`: Defines the visibility of the label when the field is not in a datagrid. Default value is ```true```. * `textAlign`: Defines the text alignment inside a cell. Supports `left` (the default) and `right`. {% raw %} ```jsx <TextField source="zb_title" label="Title" style={{ color: 'purple' }} /> ``` {% endraw %} **Tip**: You can use field components inside the `Edit` or `Show` views, too: ```jsx export const PostShow = ({ ...props }) => ( <Show {...props}> <SimpleShowLayout> <TextField source="title" /> </SimpleShowLayout> </Show> ); ``` **Tip**: If you display a record with a complex structure, you can use a path with dot separators as the `source` attribute. For instance, if the API returns the following 'book' record: ```jsx { id: 1234, title: 'War and Peace', author: { firstName: 'Leo', lastName: 'Tolstoi' } } ``` Then you can display the author first name as follows: ```jsx <TextField source="author.firstName" /> ``` **Tip**: If you want to format a field according to the value, use a higher-order component to do conditional formatting, as described in the [Theming documentation](./Theming.md#conditional-formatting). **Tip**: If your interface has to support multiple languages, don't use the `label` prop, and put the localized labels in a dictionary instead. See the [Translation documentation](./Translation.md#translating-resource-and-field-names) for details. ## `<ArrayField>` Display a collection using `<Field>` child components. Ideal for embedded arrays of objects, e.g. `tags` and `backlinks` in the following `post` object: ```js { id: 123 tags: [ { name: 'foo' }, { name: 'bar' } ], backlinks: [ { date: '2012-08-10T00:00:00.000Z', url: 'http://example.com/foo/bar.html', }, { date: '2012-08-14T00:00:00.000Z', url: 'https://blog.johndoe.com/2012/08/12/foobar.html', } ] } ``` The child must be an iterator component (like `<Datagrid>` or `<SingleFieldList>`). Here is how to display all the backlinks of the current post as a `<datagrid>` ```jsx <ArrayField source="backlinks"> <Datagrid> <DateField source="date" /> <UrlField source="url" /> </Datagrid> </ArrayField> ``` And here is how to display all the tags of the current post as `<Chip>` components: ```jsx <ArrayField source="tags"> <SingleFieldList> <ChipField source="name" /> </SingleFieldList> </ArrayField> ``` **Tip**: If you need to render a collection in a custom way, it's often simpler to write your own component: ```jsx const TagsField = ({ record }) => ( <ul> {record.tags.map(item => ( <li key={item.name}>{item.name}</li> ))} </ul> ) TagsField.defaultProps = { addLabel: true }; ``` ## `<BooleanField>` Displays a boolean value as a check. ```jsx import { BooleanField } from 'react-admin'; <BooleanField source="commentable" /> ``` ![BooleanField](./img/boolean-field.png) The `BooleanField` also includes an hidden text for accessibility (or to query in end to end tests). By default, it includes the translated label and the translated value, for example `Published: false`. If you need to override it, you can use the `valueLabelTrue` and `valueLabelFalse` props which both accept a string. Those strings may be translation keys: ```jsx // Simple texts <BooleanField source="published" valueLabelTrue="Has been published" valueLabelFalse="Has not been published yet" /> // Translation keys <BooleanField source="published" valueLabelTrue="myapp.published.true" valueLabelFalse="myapp.published.false" /> ``` ## `<ChipField>` Displays a value inside a ["Chip"](http://v1.material-ui.com/demos/chip), which is Material UI's term for a label. ```jsx import { ChipField } from 'react-admin'; <ChipField source="category" /> ``` ![ChipField](./img/chip-field.png) This field type is especially useful for one to many relationships, e.g. to display a list of books for a given author: ```jsx import { ChipField, SingleFieldList, ReferenceManyField } from 'react-admin'; <ReferenceManyField reference="books" target="author_id"> <SingleFieldList> <ChipField source="title" /> </SingleFieldList> </ReferenceManyField> ``` ## `<DateField>` Displays a date or datetime using the browser locale (thanks to `Date.toLocaleDateString()` and `Date.toLocaleString()`). ```jsx import { DateField } from 'react-admin'; <DateField source="publication_date" /> ``` This component accepts a `showTime` attribute (false by default) to force the display of time in addition to date. It uses `Intl.DateTimeFormat()` if available, passing the `locales` and `options` props as arguments. If Intl is not available, it ignores the `locales` and `options` props. {% raw %} ```jsx <DateField source="publication_date" /> // renders the record { id: 1234, publication_date: new Date('2017-04-23') } as <span>4/23/2017</span> <DateField source="publication_date" showTime /> // renders the record { id: 1234, publication_date: new Date('2017-04-23 23:05') } as <span>4/23/2017, 11:05:00 PM</span> <DateField source="publication_date" options={{ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }} /> // renders the record { id: 1234, publication_date: new Date('2017-04-23') } as <span>Sunday, April 23, 2017</span> <DateField source="publication_date" locales="fr-FR" /> // renders the record { id: 1234, publication_date: new Date('2017-04-23') } as <span>23/04/2017</span> <DateField source="publication_date" elStyle={{ color: 'red' }} /> // renders the record { id: 1234, publication_date: new Date('2017-04-23') } as <span style="color:red;">4/23/2017</span> ``` {% endraw %} See [Intl.DateTimeformat documentation](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/toLocaleDateString) for the `options` prop syntax. **Tip**: If you need more formatting options than what `Intl.DateTimeformat` can provide, build your own field component leveraging a third-party library like [moment.js](http://momentjs.com/). ## `<EmailField>` `<EmailField>` displays an email as a `<a href="mailto:" />` link. ```jsx import { EmailField } from 'react-admin'; <EmailField source="personal_email" /> ``` ## `<FunctionField>` If you need a special function to render a field, `<FunctionField>` is the perfect match. It passes the `record` to a `render` function supplied by the developer. For instance, to display the full name of a `user` record based on `first_name` and `last_name` properties: ```jsx import { FunctionField } from 'react-admin' <FunctionField label="Name" render={record => `${record.first_name} ${record.last_name}`} /> ``` **Tip**: Technically, you can omit the `source` and `sortBy` properties for the `<FunctionField>` since you provide the render function. However, providing a `source` or a `sortBy` will allow the datagrid to make the column sortable, since when a user clicks on a column, the datagrid uses these properties to sort. Should you provide both, `sortBy` will override `source` for sorting the column. ## `<ImageField>` If you need to display an image provided by your API, you can use the `<ImageField />` component: ```jsx import { ImageField } from 'react-admin'; <ImageField source="url" title="title" /> ``` This field is also generally used within an [<ImageInput />](./Inputs.md#imageinput) component to display preview. The optional `title` prop points to the picture title property, used for both `alt` and `title` attributes. It can either be an hard-written string, or a path within your JSON object: ```jsx // { picture: { url: 'cover.jpg', title: 'Larry Cover (French pun intended)' } } // Title would be "picture.title", hence "Larry Cover (French pun intended)" <ImageField source="picture.url" title="picture.title" /> // Title would be "Picture", as "Picture" is not a path in previous given object <ImageField source="picture.url" title="Picture" /> ``` If passed value is an existing path within your JSON object, then it uses the object attribute. Otherwise, it considers its value as an hard-written title. If the record actually contains an array of images in its property defined by the `source` prop, the `src` prop will be needed to determine the `src` value of the images, for example: ```js // This is the record { pictures: [ { url: 'image1.jpg', desc: 'First image' }, { url: 'image2.jpg', desc: 'Second image' }, ], } <ImageField source="pictures" src="url" title="desc" /> ``` ## `<FileField>` If you need to display a file provided by your API, you can use the `<FileField />` component: ```jsx import { FileField } from 'react-admin'; <FileField source="url" title="title" /> ``` This field is also generally used within an [<FileInput />](./Inputs.md#fileinput) component to display preview. The optional `title` prop points to the file title property, used for `title` attributes. It can either be an hard-written string, or a path within your JSON object: ```jsx // { file: { url: 'doc.pdf', title: 'Presentation' } } // Title would be "file.title", hence "Presentation" <FileField source="file.url" title="file.title" /> // Title would be "File", as "File" is not a path in previous given object <FileField source="file.url" title="File" /> ``` If passed value is an existing path within your JSON object, then it uses the object attribute. Otherwise, it considers its value as an hard-written title. If the record actually contains an array of files in its property defined by the `source` prop, the `src` prop will be needed to determine the `href` value of the links, for example: ```js // This is the record { files: [ { url: 'image1.jpg', desc: 'First image' }, { url: 'image2.jpg', desc: 'Second image' }, ], } <FileField source="files" src="url" title="desc" /> ``` You can optionally set the `target` prop to choose which window will the link try to open in. ```jsx // Will make the file open in new window <FileField source="file.url" target="_blank" /> ``` ## `<NumberField>` Displays a number formatted according to the browser locale, right aligned. Uses `Intl.NumberFormat()` if available, passing the `locales` and `options` props as arguments. This allows perfect display of decimals, currencies, percentage, etc. If Intl is not available, it outputs number as is (and ignores the `locales` and `options` props). {% raw %} ```jsx import { NumberField } from 'react-admin'; <NumberField source="score" /> // renders the record { id: 1234, score: 567 } as <span>567</span> <NumberField source="score" options={{ maximumFractionDigits: 2 }}/> // renders the record { id: 1234, score: 567.3567458569 } as <span>567.35</span> <NumberField source="share" options={{ style: 'percent' }} /> // renders the record { id: 1234, share: 0.2545 } as <span>25%</span> <NumberField source="price" options={{ style: 'currency', currency: 'USD' }} /> // renders the record { id: 1234, price: 25.99 } as <span>$25.99</span> <NumberField source="price" locales="fr-FR" options={{ style: 'currency', currency: 'USD' }} /> // renders the record { id: 1234, price: 25.99 } as <span>25,99 $US</span> <NumberField source="score" elStyle={{ color: 'red' }} /> // renders the record { id: 1234, score: 567 } as <span style="color:red;">567</span> ``` {% endraw %} See [Intl.Numberformat documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString) for the `options` prop syntax. **Tip**: If you need more formatting options than what `Intl.Numberformat` can provide, build your own field component leveraging a third-party library like [numeral.js](http://numeraljs.com/). **Tip**: When used in a `Show` view, the right alignment may look weird. Disable it by resetting the `style` attribute: {% raw %} ```jsx import { NumberField } from 'react-admin'; <NumberField source="score" style={{}} /> ``` {% endraw %} ## `<SelectField>` When you need to display an enumerated field, `<SelectField>` maps the value to a string. For instance, if the `gender` field can take values "M" and "F", here is how to display it as "Male" or "Female": ```jsx import { SelectField } from 'react-admin'; <SelectField source="gender" choices={[ { id: 'M', name: 'Male' }, { id: 'F', name: 'Female' }, ]} /> ``` By default, the text is built by - finding a choice where the 'id' property equals the field value - using the 'name' property an the option text You can also customize the properties to use for the lookup value and text, thanks to the 'optionValue' and 'optionText' attributes. ```jsx const choices = [ { _id: 123, full_name: 'Leo Tolstoi', sex: 'M' }, { _id: 456, full_name: 'Jane Austen', sex: 'F' }, ]; <SelectField source="author_id" choices={choices} optionText="full_name" optionValue="_id" /> ``` `optionText` also accepts a function, so you can shape the option text at will: ```jsx const choices = [ { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`; <SelectField source="author_id" choices={choices} optionText={optionRenderer} /> ``` `optionText` also accepts a React Element, that will be cloned and receive the related choice as the `record` prop. You can use Field components there. ```jsx const choices = [ { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; const FullNameField = ({ record }) => <Chip>{record.first_name} {record.last_name}</Chip>; <SelectField source="author_id" choices={choices} optionText={<FullNameField />}/> ``` The current choice is translated by default, so you can use translation identifiers as choices: ```jsx const choices = [ { id: 'M', name: 'myroot.gender.male' }, { id: 'F', name: 'myroot.gender.female' }, ]; ``` However, in some cases (e.g. inside a `<ReferenceField>`), you may not want the choice to be translated. In that case, set the `translateChoice` prop to false. ```jsx <SelectField source="gender" choices={choices} translateChoice={false}/> ``` **Tip**: <ReferenceField> sets `translateChoice` to false by default. ## `<ReferenceField>` This component fetches a single referenced record (using the `GET_MANY` REST method), and displays one field of this record. That's why a `<ReferenceField>` must always have a child `<Field>`. For instance, here is how to fetch the `post` related to `comment` records, and display the `title` for each: ```jsx import React from 'react'; import { List, Datagrid, ReferenceField, TextField } from 'react-admin'; export const PostList = (props) => ( <List {...props}> <Datagrid> <TextField source="id" /> <ReferenceField label="Author" source="user_id" reference="users"> <TextField source="name" /> </ReferenceField> </Datagrid> </List> ); ``` With this configuration, `<ReferenceField>` wraps the user's name in a link to the related user `<Edit>` page. ![ReferenceField](./img/reference-field.png) `<ReferenceField>` accepts a `reference` attribute, which specifies the resource to fetch for the related record. Also, you can use any `Field` component as child. **Note**: You **must** add a `<Resource>` for the reference resource - react-admin needs it to fetch the reference data. You *can* omit the `list` prop in this reference if you want to hide it in the sidebar menu. ```jsx <Admin dataProvider={myDataProvider}> <Resource name="comments" list={CommentList} /> <Resource name="posts" /> </Admin> ``` To change the link from the `<Edit>` page to the `<Show>` page, set the `linkType` prop to "show". ```jsx <ReferenceField label="User" source="userId" reference="users" linkType="show"> <TextField source="name" /> </ReferenceField> ``` By default, `<ReferenceField>` is sorted by its `source`. To specify another attribute to sort by, set the `sortBy` prop to the according attribute's name. ```jsx <ReferenceField label="User" source="userId" reference="users" sortBy="user.name"> <TextField source="name" /> </ReferenceField> ``` You can also prevent `<ReferenceField>` from adding link to children by setting `linkType` to `false`. ```jsx // No link <ReferenceField label="User" source="userId" reference="users" linkType={false}> <TextField source="name" /> </ReferenceField> ``` **Tip**: React-admin uses `CRUD_GET_ONE_REFERENCE` action to accumulate and deduplicate the ids of the referenced records to make *one* `GET_MANY` call for the entire list, instead of n `GET_ONE` calls. So for instance, if the API returns the following list of comments: ```jsx [ { id: 123, body: 'Totally agree', post_id: 789, }, { id: 124, title: 'You are right my friend', post_id: 789 }, { id: 125, title: 'Not sure about this one', post_id: 735 } ] ``` Then react-admin renders the `<CommentList>` with a loader for the `<ReferenceField>`, fetches the API for the related posts in one call (`GET http://path.to.my.api/posts?ids=[789,735]`), and re-renders the list once the data arrives. This accelerates the rendering, and minimizes network load. ## `<ReferenceManyField>` This component fetches a list of referenced records by reverse lookup of the current `record.id` in other resource (using the `GET_MANY_REFERENCE` REST method). You can specify the target field name, i.e. the field name of the current record's id in the other resource, using the required `target` field. The result is then passed to an iterator component (like `<SingleFieldList>` or `<Datagrid>`). The iterator component usually has one or more child `<Field>` components. For instance, here is how to fetch the `comments` related to a `post` record by matching `comment.post_id` to `post.id`, and then display the `author.name` for each, in a `<ChipField>`: ```jsx import React from 'react'; import { List, Datagrid, ChipField, ReferenceManyField, SingleFieldList, TextField } from 'react-admin'; export const PostList = (props) => ( <List {...props}> <Datagrid> <TextField source="id" /> <TextField source="title" type="email" /> <ReferenceManyField label="Comments by" reference="comments" target="post_id"> <SingleFieldList> <ChipField source="author.name" /> </SingleFieldList> </ReferenceManyField> <EditButton /> </Datagrid> </List> ); ``` ![ReferenceManyFieldSingleFieldList](./img/reference-many-field-single-field-list.png) `<ReferenceManyField>` accepts a `reference` attribute, which specifies the resource to fetch for the related record. It also accepts a `source` attribute which define the field containing the value to look for in the `target` field of the referenced resource. By default this is the `id` of the resource (`post.id` in the previous example). **Note**: You **must** add a `<Resource>` for the reference resource - react-admin needs it to fetch the reference data. You *can* omit the `list` prop in this reference if you want to hide it in the sidebar menu. You can use a `<Datagrid>` instead of a `<SingleFieldList>` - but not inside another `<Datagrid>`! This is useful if you want to display a read-only list of related records. For instance, if you want to show the `comments` related to a `post` in the post's `<Edit>` view: ```jsx import React from 'react'; import { Edit, Datagrid, SimpleForm, DisabledInput, DateField, EditButton, ReferenceManyField, TextField, TextInput } from 'react-admin'; export const PostEdit = (props) => ( <Edit {...props}> <SimpleForm> <DisabledInput label="Id" source="id" /> <TextInput source="title" /> <ReferenceManyField label="Comments" reference="comments" target="post_id" > <Datagrid> <DateField source="created_at" /> <TextField source="author.name" /> <TextField source="body" /> <EditButton /> </Datagrid> </ReferenceManyField> </SimpleForm> </Edit> ); ``` ![ReferenceManyFieldDatagrid](./img/reference-many-field-datagrid.png) By default, react-admin restricts the possible values to 25 and displays no pagination control. You can change the limit by setting the `perPage` prop: ```jsx <ReferenceManyField perPage={10} reference="comments" target="post_id"> ... </ReferenceManyField> ``` And if you want to allow users to paginate the list, pass a `<Pagination>` component as the `pagination` prop: ```jsx import { Pagination } from 'react-admin'; <ReferenceManyField pagination={<Pagination />} reference="comments" target="post_id"> ... </ReferenceManyField> ``` By default, it orders the possible values by id desc. You can change this order by setting the `sort` prop (an object with `field` and `order` properties). {% raw %} ```jsx <ReferenceManyField sort={{ field: 'created_at', order: 'DESC' }} reference="comments" target="post_id"> ... </ReferenceManyField> ``` {% endraw %} Also, you can filter the query used to populate the possible values. Use the `filter` prop for that. {% raw %} ```jsx <ReferenceManyField filter={{ is_published: true }} reference="comments" target="post_id"> ... </ReferenceManyField> ``` {% endraw %} ## `<ReferenceArrayField>` Use `<ReferenceArrayField>` to display an list of reference values based on an array of foreign keys. For instance, if a post has many tags, a post resource may look like: ```js { id: 1234, title: 'Lorem Ipsum', tag_ids: [1, 23, 4] } ``` Where `[1, 23, 4]` refer to ids of `tag` resources. `<ReferenceArrayField>` can fetch the `tag` resources related to this `post` resource by matching `post.tag_ids` to `tag.id`. `<ReferenceArrayField source="tags_ids" reference="tags">` would issue an HTTP request looking like: ``` http://myapi.com/tags?id=[1,23,4] ``` **Tip**: `<ReferenceArrayField>` fetches the related resources using the `GET_MANY` REST method, so the actual HTTP request depends on your REST client. Once it receives the related resources, `<ReferenceArrayField>` passes them to its child component using the `ids` and `data` props, so the child must be an iterator component (like `<SingleFieldList>` or `<Datagrid>`). The iterator component usually has one or more child `<Field>` components. Here is how to fetch the list of tags for each post in a `PostList`, and display the `name` for each `tag` in a `<ChipField>`: ```jsx import React from 'react'; import { List, Datagrid, ChipField, ReferenceArrayField, SingleFieldList, TextField } from 'react-admin'; export const PostList = (props) => ( <List {...props}> <Datagrid> <TextField source="id" /> <TextField source="title" /> <ReferenceArrayField label="Tags" reference="tags" source="tag_ids"> <SingleFieldList> <ChipField source="name" /> </SingleFieldList> </ReferenceArrayField> <EditButton /> </Datagrid> </List> ); ``` **Note**: You **must** add a `<Resource>` component for the reference resource to your `<Admin>` component, because react-admin needs it to fetch the reference data. You can omit the `list` prop in this Resource if you don't want to show an entry for it in the sidebar menu. ```jsx export const App = () => ( <Admin dataProvider={restProvider('http://path.to.my.api')}> <Resource name="posts" list={PostList} /> <Resource name="tags" /> // <= this one is compulsory </Admin> ); ``` In an Edit of Show view, you can combine `<ReferenceArrayField>` with `<Datagrid>` to display a related resources in a table. For instance, to display more details about the tags related to a post in the `PostShow` view: ```jsx import React from 'react'; import { Show, SimpleShowLayout, TextField, ReferenceArrayField, Datagrid, ShowButton } from 'react-admin'; export const PostShow = (props) => ( <Show {...props}> <SimpleShowLayout> <TextField source="id" /> <TextField source="title" /> <ReferenceArrayField label="Tags" reference="tags" source="tag_ids"> <Datagrid> <TextField source="id" /> <TextField source="name" /> <ShowButton /> </Datagrid> </ReferenceArrayField> <EditButton /> </SimpleShowLayout> </Show> ); ``` ## `<RichTextField>` This component displays some HTML content. The content is "rich" (i.e. unescaped) by default. ```jsx import { RichTextField } from 'react-admin'; <RichTextField source="body" /> ``` ![RichTextField](./img/rich-text-field.png) The `stripTags` attribute (`false` by default) allows you to remove any HTML markup, preventing some display glitches (which is especially useful in list views). ```jsx import { RichTextField } from 'react-admin'; <RichTextField source="body" stripTags /> ``` ## `<TextField>` The most simple as all fields, `<TextField>` simply displays the record property as plain text. ```jsx import { TextField } from 'react-admin'; <TextField label="Author Name" source="name" /> ``` ## `<UrlField>` `<UrlField>` displays an url in an `< a href="">` tag. ```jsx import { UrlField } from 'react-admin'; <UrlField source="site_url" /> ``` ## Styling Fields All field components accept a `className` prop, allowing you to customize their style to your liking. We advise you to use the Material UI styling solution, JSS, to generate those classes. See their [documentation](https://v1.material-ui.com/customization/css-in-js/#api) about that. {% raw %} ```jsx import { withStyles } from '@material-ui/core/styles'; const styles = { price: { color: 'purple' }, }; const PriceField = withStyles(styles)(({ classes, ...props }) => ( <TextField className={classes.price} {...props} /> )); export const ProductList = (props) => ( <List {...props}> <Datagrid> <PriceField source="price" /> </Datagrid> </List> ); // renders in the datagrid as <td><span class="[class name generated by JSS]">2</span></td> ``` {% endraw %} React-admin usually delegates the rendering of fields components to material-ui components. Refer to the material-ui documentation to see the default styles for elements. You may want to customize the cell style inside a `DataGrid`. You can use the `cellClassName` for that: {% raw %} ```jsx import { withStyles } from '@material-ui/core/styles'; const styles = { priceCell: { fontWeight: 'bold' }, }; const PriceField = withStyles(styles)(({ classes, ...props }) => ( <TextField cellClassName={classes.priceCell} {...props} /> )); export const ProductList = (props) => ( <List {...props}> <Datagrid> <PriceField source="price" /> </Datagrid> </List> ); // renders in the datagrid as <td class="[class name generated by JSS]"><span>2</span></td> ``` {% endraw %} You may want to override the field header (the `<th>` element in the datagrid). In that case, use the `headerClassName` prop: {% raw %} ```jsx import { withStyles } from '@material-ui/core/styles'; const styles = { priceHeader: { fontWeight: 'bold' }, }; const PriceField = withStyles(styles)(({ classes, ...props }) => ( <TextField headerClassName={classes.priceHeader} {...props} /> )); export const ProductList = (props) => ( <List {...props}> <Datagrid> <PriceField source="price" /> </Datagrid> </List> ); // renders in the table header as <th class="[class name generated by JSS]"><button>Price</button></td> ``` {% endraw %} Finally, sometimes, you just want to right align the text of a cell. Use the `textAlign` prop, which accepts either `left` or `right`: {% raw %} ```jsx const PriceField = props => ( <TextField {...props} /> ); PriceField.defaultProps = { textAlign: 'right', }; ``` {% endraw %} ## Writing Your Own Field Component If you don't find what you need in the list above, you can write your own Field component. It must be a regular React component, accepting not only a `source` attribute, but also a `record` attribute. React-admin will inject the `record` based on the API response data at render time. The field component only needs to find the `source` in the `record` and display it. For instance, here is an equivalent of react-admin's `<TextField>` component: ```jsx import React from 'react'; import PropTypes from 'prop-types'; const TextField = ({ source, record = {} }) => <span>{record[source]}</span>; TextField.propTypes = { label: PropTypes.string, record: PropTypes.object, source: PropTypes.string.isRequired, }; export default TextField; ``` **Tip**: The `label` attribute isn't used in the `render()` method, but react-admin uses it to display the table header. **Tip**: If you want to support deep field sources (e.g. source values like `author.name`), use [`lodash/get`](https://www.npmjs.com/package/lodash.get) to replace the simple object lookup: ```jsx import get from 'lodash/get'; const TextField = ({ source, record = {} }) => <span>{get(record, source)}</span>; ``` If you are not looking for reusability, you can create even simpler components, with no attributes. Let's say an API returns user records with `firstName` and `lastName` properties, and that you want to display a full name in a user list. ```jsx { id: 123, firstName: 'John', lastName: 'Doe' } ``` The component will be: ```jsx import React from 'react'; import { List, Datagrid, TextField } from 'react-admin'; const FullNameField = ({ record = {} }) => <span>{record.firstName} {record.lastName}</span>; FullNameField.defaultProps = { label: 'Name' }; export const UserList = (props) => ( <List {...props}> <Datagrid> <FullNameField source="lastName" /> </Datagrid> </List> ); ``` **Tip**: In such custom fields, the `source` is optional. React-admin uses it to determine which column to use for sorting when the column header is clicked. In case you use the `source` property for additional purposes, the sorting can be overridden by the `sortBy` property on any `Field` component. ## Adding Label To Custom Field Components In The Show View React-admin lets you use the same Field components in the List view and in the Show view. But if you use the `<FullNameField>` custom field component defined earlier in a Show view, something is missing: the Field label. Why do other fields have a label and not this custom Field? And how can you create a Field component that has a label in the Show view, but not in the List view? React-admin uses a trick: the Show view layouts (`<SimpleShowLayout>` and `<TabbedShowLayout>`) inspect their Field children, and whenever one has the `addLabel` prop set to `true`, the layout adds a label. That means that the only thing you need to add to a custom component to make it usable in a Show view is a `addLabel: true` default prop. ```jsx FullNameField.defaultProps = { addLabel: true, }; ``` ## Hiding A Field Based On The Value Of Another In a Show view, you may want to display or hide fields based on the value of another field - for instance, show an `email` field only if the `hasEmail` boolean field is `true`. For such cases, you can use the custom field approach: use the injected `record` prop, and render another Field based on the value. ```jsx import React from 'react'; import { EmailField } from 'react-admin'; const ConditionalEmailField = ({ record, ...rest }) => record && record.hasEmail ? <EmailField source="email" record={record} {...rest} /> : null; export default ConditionalEmailField; ``` **Tip**: Always check that the `record` is defined before inspecting its properties, as react-admin displays the Show view *before* fetching the record from the data provider. So the first time it renders the show view for a resource, the `record` is undefined. This `ConditionalEmailField` is properly hidden when `hasEmail` is false. But when `hasEmail` is true, the Show layout renders it... without label. And if you add a `addLabel` default prop, the Show layout will render the label regardless of the `hasEmail` value... One solution is to add the label manually in the custom component: ```jsx import React from 'react'; import { Labeled, EmailField } from 'react-admin'; const ConditionalEmailField = ({ record, ...rest }) => record && record.hasEmail ? ( <Labeled label="Email"> <EmailField source="email" record={record} {...rest} /> </Labeled> ) : null; export default ConditionalEmailField; ``` This comes with a drawback, though: the `<ConditionalEmailField>` cannot be used in a List view anymore, as it will always have a label. If you want to reuse the custom component in a List, this isn't the right solution. An alternative solution is to split the `<Show>` component. Under the hood, the `<Show>` component is composed of two sub components: the `<ShowController>` component, which fetches the record, and the `<ShowView>`, which is responsible for rendering the view title, actions, and children. `<ShowController>` uses the *render props* pattern: ```jsx // inside react-admin const Show = props => ( <ShowController {...props}> {controllerProps => <ShowView {...props} {...controllerProps} />} </ShowController> ); ``` The `<ShowController>` fetches the `record` from the data provider, and passes it to its child function when received (among the `controllerProps`). That means the following code: ```jsx import { Show, SimpleShowLayout, TextField } from 'react-admin'; const UserShow = props => ( <Show {...props}> <SimpleShowLayout> <TextField source="username" /> <TextField source="email" /> </SimpleShowLayout> </Show> ); ``` Is equivalent to: ```jsx import { ShowController, ShowView, SimpleShowLayout, TextField } from 'react-admin'; const UserShow = props => ( <ShowController {...props}> {controllerProps => <ShowView {...props} {...controllerProps}> <SimpleShowLayout> <TextField source="username" /> <TextField source="email" /> </SimpleShowLayout> </ShowView> } </ShowController> ); ``` If you want one field to be displayed based on the `record`, for instance to display the email field only if the `hasEmail` field is `true`, you just need to test the value from `controllerProps.record`, as follows: ```jsx 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> ); ``` And now you can use a regular Field component, and the label displays correctly in the Show view.