Skip to content

表单 ​

表单由两层组成:

  • 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
Selectstring | number
Checkbox / Switchboolean
RadioGroupstring | number
Slidernumber
RangeSlider[number, number]
OptInputsstring
DateInputDate | null
TimeInputTimeValue | null

错误文案由控件自己渲染在字段下方,FormItem 不会重复显示。控件只有“常规”和“不通过”两种状态, 全部由验证函数驱动,没有独立的 success / error 属性;不通过时文案优先使用验证函数返回的字符串。

完整字段组合见 Form 表单 · 完整示例。

完整属性与示例见 Form 表单。