我有一个包含多个输入的表单。
一是“时间”,一是“价格”。
我正在使用 NextJS 和服务器端操作。 我现在所做的是当有人点击提交
时间必须在10到100之间。我正在处理的场景如下;
到目前为止,这有效。但随后用户填写 50 并转到下一个字段。我希望运行验证
onBlur
,以便错误消息在现在有效时消失。否则,当输入为 50 但错误消息显示小时数应在 10 到 100 之间时,会令人困惑。
如何做到这一点?我是否必须为每个字段创建一个单独的架构并将其称为 onBlur?
形式:
<form
ref={formRef}
action={async (formData) => {
const validatedFields = validateFields(formData)
const state = await createCard(validatedFields)
if (state.status === 'success') {
formRef.current?.reset()
setOpen(false)
setClientErrorMessage(undefined)
} else if (state.status === 'error') {
console.log(state)
setserverErrorMessage(state.message)
}
}}
>
<div className='mb-4'>
<Label htmlFor='client_id' className='mb-2'>
Client
</Label>
<Select name='client_id' required>
<SelectTrigger className='w-[240px]'>
<SelectValue placeholder='Select client' />
</SelectTrigger>
<SelectContent>
{clients?.map((client) => (
<SelectItem key={client.id} value={client.id}>
{client.name}
</SelectItem>
))}
</SelectContent>
</Select>
{clientErrorMessage?.client_id && (
<p className='py-2 text-xs text-red-500'>
{clientErrorMessage.client_id}
</p>
)}
</div>
<div className='mb-4'>
<Label htmlFor='hours' className='mb-2'>
Hours
</Label>
<Input type='number' name='hours' id='hours' required />
{clientErrorMessage?.hours && (
<p className='py-2 text-xs text-red-500'>
{clientErrorMessage.hours}
</p>
)}
</div>
<div className='mb-4'>
<Label htmlFor='price' className='mb-2'>
Price
</Label>
<div className='relative flex items-center max-w-2xl '>
<Euro className='absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 transform' />
<Input
type='number'
name='price'
id='price'
required
className='pl-6'
/>
{clientErrorMessage?.price && (
<p className='py-2 text-xs text-red-500'>
{clientErrorMessage.price}
</p>
)}
</div>
</div>
<div className='mb-4 flex flex-col'>
<div className='flex items-center'>
<Switch
checked={customEndDate}
className='mr-2'
onCheckedChange={handleSwitchChange}
/>
<span className=''>Set custom end date</span>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<InfoCircledIcon className='ml-1' />
</TooltipTrigger>
<TooltipContent>
<p>By default cards are valid for one year</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
{customEndDate ? (
<>
<Label htmlFor='ends_at' className='my-2 mr-2'>
Valid until
</Label>
<input
aria-label='Date'
type='date'
id='ends_at'
name='ends_at'
required
defaultValue={formattedDate}
/>
</>
) : (
<input
type='hidden'
aria-label='Date'
id='ends_at'
name='ends_at'
required
defaultValue={formattedDate}
/>
)}
</div>
<p aria-live='polite' className='sr-only'>
{state?.message}
</p>
<FormError errorMessage={serverErrorMessage} />
<DialogClose asChild>
<Button variant='outline' className='mr-2'>
Cancel
</Button>
</DialogClose>
<SubmitButton normal='Add card' going='Adding card...' />
</form>
验证字段:
const validateFields = (formData: FormData) => {
const validatedFields = createSchema.safeParse({
client_id: formData.get('client_id'),
hours: Number(formData.get('hours')),
hours_left: Number(formData.get('hours')),
price: Number(formData.get('price')),
ends_at: formData.get('ends_at'),
})
if (!validatedFields.success) {
return setClientErrorMessage(validatedFields.error.flatten().fieldErrors)
}
return validateFields
}
很想知道如何解决这个问题。
您可以像这样引用另一个模式中的模式:
const a = z.object({
client_id: z.number(),
hours: z.number(),
hours_left: z.number(),
price: z.number(),
ends_at: z.number()
});
const b = z.object({
data: a,
another_field: z.string(),
and_another: z.object({})
});
b
的最终形状将是:
{
a: {
client_id: number;
hours: number;
// etc...
},
another_field: string;
and_another: {}
}
.extend()
: 扩展现有模式
const a = z.object({
client_id: z.number(),
hours: z.number(),
hours_left: z.number(),
price: z.number(),
ends_at: z.number()
});
const extended_a = a.extend({
another_field: z.string()
});
extended_a
的最终形状将是:
{
client_id: number;
hours: number;
// etc...
another_field: string
}
现在您可以使用架构的子集验证选定的字段,然后使用完整的扩展架构再次验证。