如何使用 React Admin 从显示视图在对话框中添加表单输入?

How to add a form input on a Dialog from Show view with React Admin?

总结

我想在记录显示视图上添加一个快速操作按钮,该按钮将显示一个带有表单字段的对话框。

提交表单时,我希望提供的数据可以使用表单字段数据和一些选项发送更新操作。

我知道如何创建将在数据提供程序上发送更新操作的操作按钮,但我对如何处理对话框中的表单感到困惑。

尝试(和失败)

所以我开始创建一个按钮,显示一个带有 TextInput(来自 react-admin)的对话框:

const ValidateButton: FC<ButtonProps> = ({ record }) => {
  const [open, setOpen] = useState<boolean>(false);

  const handleInit = () => {
    setOpen(true)
  }

  const handleCancel = () => {
    setOpen(false);
  }

  const handleSubmit = () => {
    setOpen(false);
  }

  return (
    <>
      <Button
        label="Validate"
        onClick={handleInit}
      >
        <ValidateIcon />
      </Button>
      <Dialog
        open={open}
      >
        <DialogTitle>Validate</DialogTitle>
        <DialogContent>
          <TextInput
            source="comment"
            label="Validation comment"
          />
        </DialogContent>
        <DialogActions>
          <Button
            label="Cancel"
            onClick={handleCancel}
          />
          <Button
            label="Validate"
            onClick={handleSubmit}
          />
        </DialogActions>
      </Dialog>
    </>
  );
}

这给了我以下错误:

Error: useField must be used inside of a <Form> component

嗯。很清楚。所以我尝试将它包装在 SimpleForm 组件上:

<DialogContent>
  <SimpleForm
    form={createForm({ onSubmit: handleSubmit })}
    resource="rides"
    record={record}
  >
    <TextInput
      source="comment"
      label="Validation comment"
    />
  </SimpleForm>
</DialogContent>

然后我得到:

TypeError: can't access property "save", context is undefined
useSaveContext
node_modules/ra-core/esm/controller/details/SaveContext.js:23

  20 |  */
  21 | export var useSaveContext = function (props) {
  22 |     var context = useContext(SaveContext);
> 23 |     if (!context.save || !context.setOnFailure) {
  24 |         /**
  25 |          * The element isn't inside a <SaveContextProvider>
  26 |          * To avoid breakage in that case, fallback to props

ValidateButton在显示视图工具栏上实现:

const RideShowActions: FC<ShowActionsProps> = ({ basePath, data }) => {
  if (!data) {
    return null;
  }

  return (
    <TopToolbar>
      <ValidateButton />
      {[
        'created',
        'validated',
        'confirmed',
      ].includes(data?.status) && <CancelButton basePath={basePath} record={data} />}
      <EditButton basePath={basePath} record={data} />
    </TopToolbar>
  );
};

export const RideShow: FC<ShowProps> = (props) => (
  <Show
    title={<RideTitle />}
    actions={<RideShowActions />}
    {...props}
  >
    <SimpleShowLayout>
      // Show classic stuff.
    </SimpleShowLayout>
  </Show>
);

问题

我想最后一个错误是因为 SimpleForm 需要放在 <Edit> 或类似的地方。

我的目标只是让输入具有与对话框中的编辑表单相同的功能和 UX/UI 设计,并执行自定义操作。

我错过了什么?我怎样才能做到这一点?

注意:我尝试直接使用@material-ui/coreTextField组件。它有效,但我放弃了所有对我有用的反应管理功能,比如 ReferenceInput.

我终于通过创建自定义 DialogForm 组件找到了解决方案,结合了表单本身和操作按钮等通用逻辑:

import { FC, MouseEventHandler } from 'react';
import { Button } from 'react-admin';
import { Dialog, DialogProps, DialogTitle, DialogContent, DialogActions } from '@material-ui/core'
import { Form } from 'react-final-form';
import { Config } from 'final-form';

export interface DialogFormProps {
  open: DialogProps['open'],
  loading?: boolean;
  onSubmit: Config['onSubmit'],
  onCancel: MouseEventHandler,
  title?: string,
  submitLabel?: string;
  cancelLabel?: string;
}

export const DialogForm: FC<DialogFormProps> = ({
  open,
  loading,
  onSubmit,
  onCancel,
  title,
  cancelLabel,
  submitLabel,
  children,
}) => {
  return (
    <Dialog
      open={open}
    >
        <Form
          onSubmit={onSubmit}
          render={({handleSubmit}) => (
            <form onSubmit={handleSubmit}>
              {title && (
                <DialogTitle>
                  {title}
                </DialogTitle>
              )}
              <DialogContent>
                {children}
              </DialogContent>
              <DialogActions>
                <Button
                  label={cancelLabel || 'Cancel'}
                  onClick={onCancel}
                  disabled={loading}
                />
                <Button
                  label={submitLabel || 'Validate'}
                  type="submit"
                  disabled={loading}
                />
              </DialogActions>
            </form>
          )}
        />

    </Dialog>
  )
};

export default null;

您可以copy/paste上面的例子让它开箱即用。

这是一个带按钮的实现示例:

const ValidateButton: FC<ButtonProps> = ({ record }) => {
  const [open, setOpen] = useState<boolean>(false);
(undefined);
  const [mutate, { loading }] = useMutation();
  const notify = useNotify();
  const refresh = useRefresh();

  const handleInit = () => {
    setOpen(true)
  }

  const handleCancel = () => {
    setOpen(false);
  }

  const handleSubmit: DialogFormProps['onSubmit'] = (values) => {
    console.log(values);
    mutate(
      // Your mutation logic.
    );
  }

  return (
    <>
      <Button
        label="Validate"
        onClick={handleInit}
      >
        <ValidateIcon />
      </Button>
      <DialogForm
        open={open}
        loading={loading}
        onSubmit={handleSubmit}
        onCancel={handleCancel}
        title="Validate that thing"
        submitLabel="Let's go!"
      >
        <DialogContentText>
          Some text.
        </DialogContentText>
        <ReferenceInput
          source="referenceId"
          label="Reference"
          reference="service"
          filter={{ userId: record?.customerId }}
        >
          <SelectInput
            optionText="name"
            resettable
            fullWidth
          />
        </ReferenceInput>
      </DialogForm>
    </>
  );
}

希望对您有所帮助!