-
-
Notifications
You must be signed in to change notification settings - Fork 605
Open
Labels
Description
Description
When building a form stepper, like so:
It's common for each step to have its own form. However, this complicates the form submission and validation process by requiring you to add complex logic.
Ideally, it would be nice to have a FormGroup where you could validate the group, but not the form itself - submit the value and move on to the next step.
API Proposal
// ...
const formOpts = formOptions({
defaultValues: {
step1: {
name: "",
},
step2: {
name: "",
},
},
})
const Step2Form = withForm({
...formOpts,
render: function Render({ form }) {
return (
<form.FormGroup
name="step2"
onGroupSubmit={({ value: _value }) => {
form.handleSubmit();
}}
>
{(formGroup) => (
<form onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
formGroup.handleSubmit();
}}>
<form.AppField
name="step2.name"
>
{field => (
<field.TextField />
)}
</form.AppField>
<button type="submit">Next</button>
</form>
)}
</form.FormGroup>
)
},
})
export const StepperForm = () => {
const [step, setStep] = useState(0);
const form = useAppForm({
...formOpts,
validationLogic: revalidateLogic(),
validators: {
onDynamic: z.object({
step1: z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
}),
// Will run when `step2` group is submitted or the whole form is submitted.
// When `step2` group is submitted, it will run the form's validators, then throw aways errors on `step1`
step2: z.object({
name: z.string().min(3, "Name must be at least 3 characters"),
}),
})
},
onSubmit: ({ value }) => {
console.log("Form submitted:", value);
}
});
return (
<div>
{step === 1 && (
// FormGroup internally provides a sub-form context for its children including a `doNotValidate` flag to disable the parent form's validation on field changes
<form.FormGroup
name="step1"
validators={{
// If `validators` are defined on the FormGroup, they will disable the parent form's validators for this group's `onGroupSubmit`
// Only required for async or for performance optimizations on sync validations
onDynamic: z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
})
}}
onGroupSubmit={({ value: _value }) => {
setStep(step + 1);
}}
onGroupSubmitInvalid={() => { }}
>
{(formGroup) => (
<form onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
formGroup.handleSubmit();
}}>
{/* Then, Field component consumes `sub-form` context and enables us to pass options to `FieldApi` */}
<form.AppField
name="step1.name"
>
{field => (
<field.TextField />
)}
</form.AppField>
<button type="submit">Next</button>
{/* formGroup contains errorMaps and errors, just like forms and fields */}
<pre>{JSON.stringify(formGroup.state.errorMap, null, 2)}</pre>
</form>
)}
</form.FormGroup>
)}
{/* Can even extract it using `formGroup` */}
{step === 2 && (
<Step2Form form={form} />
)}
</div>
);
};Reactions are currently unavailable
