Back to React Admin

CreateInDialogButton

docs/CreateInDialogButton.md

5.14.614.0 KB
Original Source

<CreateInDialogButton>

This Enterprise Edition component offers a way to open a <Create> view inside a dialog, hence allowing to create a new record without leaving the current view.

<video controls autoplay playsinline muted loop> <source src="https://react-admin-ee.marmelab.com/assets/ra-form-layout/latest/CreateInDialogButton.mp4" type="video/mp4" /> Your browser does not support the video tag. </video>

It can be useful in case you want the ability to create a record linked by a reference to the currently edited record, or if you have a nested <DataTable> inside a <Show> or an <Edit> view.

Note that this component doesn't use routing, so it doesn't change the URL. It's therefore not possible to bookmark the creation dialog, or to link to it from another page. If you need that functionality, use <CreateDialog> instead.

Usage

First, install the @react-admin/ra-form-layout package:

sh
npm install --save @react-admin/ra-form-layout
# or
yarn add @react-admin/ra-form-layout

Tip: ra-form-layout is hosted in a private npm registry. You need to subscribe to one of the Enterprise Edition plans to access this package.

Then, put <CreateInDialogButton> wherever you would put a <CreateButton>, and use the same children as you would for a <Create> component (e.g. a <SimpleForm>):

{% raw %}

jsx
import {
  DataTable,
  ReferenceManyField,
  Show,
  SimpleForm,
  SimpleShowLayout,
  TextField,
  TextInput,
  WithRecord,
} from "react-admin";
import { CreateInDialogButton } from "@react-admin/ra-form-layout";

const CompanyShow = () => (
    <Show>
        <SimpleShowLayout>
            <TextField source="name" />
            <TextField source="address" />
            <TextField source="city" />
            <ReferenceManyField target="company_id" reference="employees">
                <WithRecord render={record => (
                    <CreateInDialogButton record={{ company_id: record.id }}>
                        <SimpleForm>
                            <TextInput source="first_name" />
                            <TextInput source="last_name" />
                        </SimpleForm>
                    </CreateInDialogButton>
                )} />
                <DataTable>
                    <DataTable.Col source="first_name" />
                    <DataTable.Col source="last_name" />
                </DataTable>
            </ReferenceManyField>
        </SimpleShowLayout>
    </Show>
);

{% endraw %}

In the above example, <CreateInDialogButton> is used to create a new employee for the current company. The <WithRecord> component helps to set the new employee company id by default.

Props

<CreateInDialogButton> accepts the following props:

PropRequiredTypeDefaultDescription
ButtonPropsOptionalobjectObject containing props to pass to Material UI's <Button>
childrenRequiredReactNodeThe content of the dialog
fullWidthOptionalbooleanfalseIf true, the dialog stretches to the full width of the screen
iconOptionalReactElementAllows to override the default icon
inlineOptionalbooleanSet to true to display only a Material UI <IconButton> instead of the full <Button>
labelOptionalstringAllows to override the default button label. I18N is supported
maxWidthOptionalstringsmThe max width of the dialog
mutation OptionsOptionalobjectThe options to pass to the useMutation hook
resourceOptionalstringThe resource name, e.g. posts
sxOptionalobjectOverride the styles applied to the dialog component
titleOptionalReactNodeThe title of the dialog

ButtonProps

The ButtonProps prop allows you to pass props to the MUI <Button> component. For instance, to change the color and size of the button:

{% raw %}

jsx
const CreateButton = () => (
  <CreateInDialogButton ButtonProps={{ color: 'primary', fullWidth: true }}>
      <SimpleForm>
          ...
      </SimpleForm>
  </CreateInDialogButton>
);

{% endraw %}

children

<CreateInDialogButton> doesn't render any field by default - it delegates this to its children, usually a Form component.

React-admin provides several built-in form layout components:

  • SimpleForm for a single-column layout
  • TabbedForm for a tabbed layout
  • AccordionForm for long forms with collapsible sections
  • LongForm for long forms with a navigation sidebar
  • WizardForm for multi-step forms
  • and Form, a headless component to use as a base for your custom layouts

To use an alternative form layout, switch the <CreateInDialogButton> child component:

diff
const CreateButton = () => (
    <CreateInDialogButton fullWidth maxWidth="md">
-       <SimpleForm>
+       <TabbedForm>
+           <TabbedForm.Tab label="Identity">
                <TextInput source="first_name" fullWidth />
                <TextInput source="last_name" fullWidth />
+           </TabbedForm.Tab>
+           <TabbedForm.Tab label="Informations">
                <DateInput source="dob" label="born" fullWidth />
                <SelectInput source="sex" choices={sexChoices} fullWidth />
+           </TabbedForm.Tab>
-       </SimpleForm>
+       </TabbedForm>
    </CreateInDialogButton>
);

fullWidth

By default, <CreateInDialogButton> renders a Material UI <Dialog> component that takes the width of its content.

You can make the dialog full width by setting the fullWidth prop to true:

jsx
const CreateButton = () => (
  <CreateInDialogButton fullWidth>
      ...
  </CreateInDialogButton>
);

In addition, you can set a dialog maximum width by using the maxWidth enumerable in combination with the fullWidth boolean. When the fullWidth prop is true, the dialog will adapt based on the maxWidth value.

jsx
const CreateButton = () => (
  <CreateInDialogButton fullWidth maxWidth="sm">
      ...
  </CreateInDialogButton>
);

icon

The icon prop allows you to pass an icon to the button. It can be a MUI icon component, or a custom icon component.

jsx
import { Create } from '@mui/icons-material';

const CreateButton = () => (
  <CreateInDialogButton icon={<Create />}>
      ...
  </CreateInDialogButton>
);

inline

By default, <CreateInDialogButton> renders a <Button> component. If you want to display only an <IconButton>, set the inline prop to true:

jsx
const CreateButton = () => (
  <CreateInDialogButton inline>
      ...
  </CreateInDialogButton>
);

label

The label prop allows you to pass a custom label to the button, instead of the default ("Create"). It can be a string, or a React element.

jsx
const CreateButton = () => (
  <CreateInDialogButton label="Edit details">
      ...
  </CreateInDialogButton>
);

maxWidth

The maxWidth prop allows you to set the max width of the dialog. It can be one of the following values: xs, sm, md, lg, xl, false. The default is sm.

For example, you can use that prop to make the dialog full width:

jsx
const CreateButton = () => (
  <CreateInDialogButton fullWidth maxWidth={false}>
      ...
  </CreateInDialogButton>
);

mutationOptions

The mutationOptions prop allows you to pass options to the useMutation hook.

This can be useful e.g. to pass a custom meta to the dataProvider.create() call.

{% raw %}

jsx
const CreateButton = () => (
  <CreateInDialogButton mutationOptions={{ meta: { fetch: 'author' } }}>
      ...
  </CreateInDialogButton>
);

{% endraw %}

resource

The resource prop allows you to pass the resource name to the <CreateInDialogButton> component. If not provided, it will be deduced from the resource context.

This is useful to link to a related record. For instance, the following button lets you create the author of a book:

jsx
const CreateAuthorButton = () => {
  return (
    <CreateInDialogButton resource="authors">
        ...
    </CreateInDialogButton>
  );
};

sx

Customize the styles applied to the Material UI <Dialog> component:

{% raw %}

jsx
const CreateButton = () => (
  <CreateInDialogButton sx={{ backgroundColor: 'paper' }}>
      ...
  </CreateInDialogButton>
);

{% endraw %}

title

Unlike the <Create> components, with Dialog components the title will be displayed in the <Dialog>, not in the <AppBar>. Here is an example:

tsx
const CreateButton = () => (
  <CreateInDialogButton title="Create a new customer">
      <SimpleForm>
          ...
      </SimpleForm>
  </CreateInDialogButton>
);

You can also hide the title by passing null:

tsx
<CreateInDialogButton title={null}>
    <SimpleForm>
        ...
    </SimpleForm>
</CreateInDialogButton>

Warn When Unsaved Changes

If you'd like to trigger a warning when the user tries to close the dialog with unsaved changes, using the warnWhenUnsavedChanges prop directly on the form won't work, because this feature relies on the router's location, but both components do not use routing.

Instead, you can use the <WarnWhenUnsavedChangesInDialog> component provided by ra-form-layout.

You can add this component to your form like so:

tsx
import { TextInput, SimpleForm } from 'react-admin';
import { CreateInDialogButton, WarnWhenUnsavedChangesInDialog } from '@react-admin/ra-form-layout';

const EmployerEditButton = () => (
    <CreateInDialogButton>
        <SimpleForm>
            <TextInput source="name" />
            <TextInput source="address" />
            <TextInput source="city" />
            <WarnWhenUnsavedChangesInDialog />
        </SimpleForm>
    </CreateInDialogButton>
);

Combining With <EditInDialogButton>

Below is an example of an <Edit> view, inside which is a nested <DataTable>, offering the ability to create, edit and show the rows thanks to <CreateInDialogButton>, <EditInDialogButton> and <ShowInDialogButton>:

{% raw %}

jsx
import React from "react";
import {
  DataTable,
  DateField,
  DateInput,
  Edit,
  ReferenceManyField,
  required,
  SelectField,
  SelectInput,
  SimpleForm,
  SimpleShowLayout,
  TextField,
  TextInput,
  useRecordContext,
} from "react-admin";
import {
  CreateInDialogButton,
  EditInDialogButton,
  ShowInDialogButton,
} from "@react-admin/ra-form-layout";

const sexChoices = [
  { id: "male", name: "Male" },
  { id: "female", name: "Female" },
];

const CustomerForm = () => (
  <SimpleForm defaultValues={{ firstname: "John", name: "Doe" }}>
    <TextInput source="first_name" validate={required()} fullWidth />
    <TextInput source="last_name" validate={required()} fullWidth />
    <DateInput source="dob" label="born" validate={required()} fullWidth />
    <SelectInput source="sex" choices={sexChoices} fullWidth />
  </SimpleForm>
);

const CustomerLayout = () => (
  <SimpleShowLayout>
    <TextField source="first_name" fullWidth />
    <TextField source="last_name" fullWidth />
    <DateField source="dob" label="born" fullWidth />
    <SelectField source="sex" choices={sexChoices} fullWidth />
  </SimpleShowLayout>
);

const NestedCustomersDataTable = () => {
  const record = useRecordContext();

  const createButton = (
    <CreateInDialogButton
      inline
      fullWidth
      maxWidth="md"
      record={{ employer_id: record?.id }} // pre-populates the employer_id to link the new customer to the current employer
    >
      <CustomerForm />
    </CreateInDialogButton>
  );

  const editButton = (
    <EditInDialogButton fullWidth maxWidth="md">
      <CustomerForm />
    </EditInDialogButton>
  );

  const showButton = (
    <ShowInDialogButton fullWidth maxWidth="md">
      <CustomerLayout />
    </ShowInDialogButton>
  );

  return (
    <ReferenceManyField
      label="Customers"
      reference="customers"
      target="employer_id"
    >
      <DataTable>
        <DataTable.Col source="id" />
        <DataTable.Col source="first_name" />
        <DataTable.Col source="last_name" />
        <DataTable.Col source="dob" label="born" field={DateField} />
        <DataTable.Col source="sex">
          <SelectField source="sex" choices={sexChoices} />
        </DataTable.Col>
        <DataTable.Col label={createButton}>
          {editButton}
          {showButton}
        </DataTable.Col>
      </DataTable>
    </ReferenceManyField>
  );
};

const EmployerEdit = () => (
  <Edit>
    <SimpleForm>
      <TextInput source="name" validate={required()} />
      <TextInput source="address" validate={required()} />
      <TextInput source="city" validate={required()} />
      <NestedCustomersDataTable />
    </SimpleForm>
  </Edit>
);

{% endraw %}