跳至主要内容

如何實作表單驗證

本指南說明如何使用框架內建的 schema 驗證器搭配 react-hook-form 實作表單驗證。

前置條件

react-hook-form 已包含在 @appfuse/appfuse-web 的 peer dependencies 中,框架內建的驗證器無需額外安裝。

禁止使用 Zod

AppFuse Web 提供輕量級驗證器(~5KB),禁止使用 Zod(~60KB)。前端僅驗證類型和必填,格式驗證由後端處理。

基本驗證

步驟 1:定義 Schema

import { schema } from '@appfuse/appfuse-web/form/validator';

const customerSchema = schema.object({
name: schema.string().required(),
email: schema.string().required(),
phone: schema.string().required(),
});

步驟 2:建立表單

import { useForm } from 'react-hook-form';
import { validatorResolver } from '@appfuse/appfuse-web/form/validator';
import { Input } from '@appfuse/appfuse-web/form';

function CustomerForm() {
const { control, handleSubmit } = useForm({
resolver: validatorResolver(customerSchema),
defaultValues: {
name: '',
email: '',
phone: '',
},
});

const onSubmit = (data) => {
console.log('Valid data:', data);
};

return (
<form onSubmit={handleSubmit(onSubmit)}>
<Input name="name" control={control} label="Name" />
<Input name="email" control={control} label="Email" type="email" />
<Input name="phone" control={control} label="Phone" />
<button type="submit" className="btn btn-primary">Save</button>
</form>
);
}

常見驗證規則

必填欄位

const mySchema = schema.object({
// 字串必填
name: schema.string().required(),

// 選擇欄位必填
category: schema.string().required(),

// 數字必填
price: schema.number().required(),
});

選填欄位(nullable)

const mySchema = schema.object({
// 選填字串
note: schema.string(),

// 選填數字
discount: schema.number().nullable(),

// 選填陣列
tags: schema.array(),
});

預設值設計

// ✅ 正確的預設值
const defaultValues = {
name: '', // 字串欄位用空字串
category: null, // 選擇欄位用 null
price: null, // 數字欄位用 null
date: null, // 日期欄位用 null
};

// ❌ 錯誤
const defaultValues = {
category: '', // Select 不應使用空字串
price: 0, // 0 是有意義的值,不是空值
};

後端違例處理

後端回傳的驗證錯誤(violations)可自動映射到表單欄位:

import { createServerErrorHandler } from '@appfuse/appfuse-web/form/validator';
import { prompt } from '@appfuse/appfuse-web/messaging';

function CustomerForm() {
const { control, handleSubmit, setError } = useForm({
resolver: validatorResolver(customerSchema),
});

const onSubmit = async (data) => {
try {
await customerService.create(data);
prompt.success('Customer created');
} catch (error) {
// 自動將後端 violations 映射到表單欄位錯誤
const handler = createServerErrorHandler(setError);
handler(error);
}
};

return <form onSubmit={handleSubmit(onSubmit)}>...</form>;
}

錯誤訊息翻譯

錯誤訊息會自動翻譯,${field} 會被替換為欄位名稱:

// nls/message/zh-TW.ts
export default {
'${field} is required': '${field}為必填',
};

驗證策略

AppFuse Web 採用前後端分工的驗證策略:

驗證類型前端後端
類型檢查schema.string()
必填.required()@NotBlank
長度/格式@Length, @Email
唯一性✅ Database
跨欄位驗證✅ Custom Validator

下一步