表单
表单由两层组成:
Form包住整张表,负责提交与整体校验;FormItem包住一个字段,负责标签、必填标记与说明文字。
字段本身的校验由控件负责:给 Input、Select、Checkbox 等组件传 validator,它们就会显示自己的 错误状态与错误文案。这样控件在单独使用时也能校验,不必依赖 Form。
vue
<script setup lang="ts">
import { ref } from "vue";
import { Button, Form, FormItem, Input } from "@sharpdotnut/dotsharp";
const email = ref("");
const emailValidator = (value: string | number) =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value)) ? false : "邮箱格式不正确";
</script>
<template>
<Form @submit="(valid) => console.log(valid)">
<FormItem label="邮箱" required>
<Input v-model="email" type="email" :validator="emailValidator" />
</FormItem>
<Button type="submit">提交</Button>
</Form>
</template>验证函数
所有支持校验的控件接收同一个 validator:参数是当前值,返回值有三种含义。
| 返回值 | 含义 | 外观 |
|---|---|---|
false | 通过 | 样式不变(本库没有“正确 / 成功”样式) |
true | 不通过,但不带文案 | 显示错误状态,例如红色描边、aria-invalid |
string | 不通过,字符串是错误提示 | 显示错误状态与这段文字 |
注意:false 才是通过,true 是不通过。 这与一些常见组件库相反,写验证函数时请以此为准。
ts
// 通过:返回 false
const required = (value: string | number) => (String(value).trim() ? false : "请填写");
// 不通过、不带文案:返回 true
const adult = (value: string | number) => (Number(value) >= 18 ? false : true);
// 不通过、带文案:返回字符串
const email = (value: string | number) =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value)) ? false : "邮箱格式不正确";验证函数是同步的。它抛出的异常按不通过处理,异常信息会当作错误文案,避免错误被静默吞掉。
触发时机
默认在两种时机校验:
input:用户改变值时(输入、选择、拖动、开关切换);blur:控件失焦时。
用 validateOn 收窄,传单个值或数组:
vue
<!-- 只在失焦时自动校验 -->
<Input v-model="email" :validator="emailValidator" validate-on="blur" />传空数组可以关闭全部自动校验,改为只由手动调用或表单提交触发:
vue
<Input v-model="email" :validator="emailValidator" :validate-on="[]" />手动校验
控件与 Form 都暴露两个方法:
| 方法 | 签名 | 说明 |
|---|---|---|
validate | () => boolean | 立即校验,返回是否通过 |
clearValidate | () => void | 清空校验结果,不修改值 |
vue
<script setup lang="ts">
import { ref } from "vue";
import { Button, Form, FormItem, Input } from "@sharpdotnut/dotsharp";
const formRef = ref<{ validate: () => boolean } | null>(null);
function validateForm(): void {
formRef.value?.validate(); // true = 全部通过
}
function clearForm(): void {
formRef.value?.clearValidate();
}
</script>
<template>
<Form ref="formRef">
<FormItem label="邮箱">
<Input v-model="email" type="email" :validator="emailValidator" :validate-on="[]" />
</FormItem>
<Button @click="validateForm">校验</Button>
</Form>
</template>Form.validate() 会跑完所有字段再返回,保证每个字段的错误状态都被更新。单个控件也可以单独调用:
ts
const inputRef = ref<{ validate: () => boolean } | null>(null);
inputRef.value?.validate();提交
Form 监听原生提交事件并阻止页面刷新:先校验全部字段,再触发 submit,参数 valid 表示整体结果。
vue
<Form @submit="(valid) => (valid ? save() : null)">
...
<Button type="submit">提交</Button>
</Form>即使字段设置了 :validate-on="[]",提交时也会校验;submit 只负责把结果交给你,不会自动拦截业务逻辑。
FormItem
FormItem 负责字段排版:
label:字段标签;required:标签末尾的必填标记,仅视觉提示(同时在支持的控件上输出aria-required);description:字段下方的说明文字。
是否必填、校验规则都由控件的 validator 决定,required 不参与校验。控件没有自己的 label 时, FormItem 的标签会通过 aria-labelledby 成为控件的可访问名称。
vue
<FormItem label="用户名" required description="2–20 个字符">
<Input v-model="name" :validator="nameValidator" />
</FormItem>什么时候不用 FormItem
FormItem 不是必需的。如果表单很简单、只有自带 label 的控件(Input、Select),并且不需要必填标记和 字段说明,直接用控件自己的 label 更好:
vue
<Form @submit="onSubmit">
<Input v-model="email" label="邮箱" :validator="emailValidator" />
<Select v-model:value="plan" label="套餐" :validator="planValidator">
<Option label="基础版" value="basic" />
<Option label="专业版" value="pro" />
</Select>
<Button type="submit">提交</Button>
</Form>控件自己的标签会与控件关联成可访问名称,validator、Form 的整体校验、validate() / clearValidate() 都不受影响,只是少了一层包裹。
需要下面这些能力时再用 FormItem:
- 标签末尾的必填标记(
required); - 字段下方的说明文字(
description); - 控件本身没有
label(Checkbox、RadioGroup、Slider等):这时FormItem的标签既负责排版,也作为 控件的可访问名称。
同一个字段不要同时给 FormItem 和控件写标签,会渲染出两份。
支持的控件
以下控件都使用同一套 validator / validateOn / validate() / clearValidate():
| 控件 | 验证函数收到的值 |
|---|---|
Input(含多行 textarea) | string | number |
Select | string | number |
Checkbox / Switch | boolean |
RadioGroup | string | number |
Slider | number |
RangeSlider | [number, number] |
OptInputs | string |
DateInput | Date | null |
TimeInput | TimeValue | null |
错误文案由控件自己渲染在字段下方,FormItem 不会重复显示。控件只有“常规”和“不通过”两种状态, 全部由验证函数驱动,没有独立的 success / error 属性;不通过时文案优先使用验证函数返回的字符串。
完整字段组合见 Form 表单 · 完整示例。
完整属性与示例见 Form 表单。