如何使用 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/core
的TextField
组件。它有效,但我放弃了所有对我有用的反应管理功能,比如 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>
</>
);
}
希望对您有所帮助!
总结
我想在记录显示视图上添加一个快速操作按钮,该按钮将显示一个带有表单字段的对话框。
提交表单时,我希望提供的数据可以使用表单字段数据和一些选项发送更新操作。
我知道如何创建将在数据提供程序上发送更新操作的操作按钮,但我对如何处理对话框中的表单感到困惑。
尝试(和失败)
所以我开始创建一个按钮,显示一个带有 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/core
的TextField
组件。它有效,但我放弃了所有对我有用的反应管理功能,比如 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>
</>
);
}
希望对您有所帮助!