表单与输入
表单是业务系统里最重的部分。Nuxt UI 把输入框、下拉、开关等控件,连同「字段标签、校验、错误提示」一起打包成 UForm + UFormField + 各种输入组件,写起来比裸表单省很多事。
本篇基于 /453.html 接好的环境,重点讲怎么搭一个带校验的表单。
整体结构
UForm 是表单容器,UFormField 包住单个字段(负责标签和错误信息),里面再放具体输入组件(如 UInput)。
<template>
<UForm :state="state" :validate="validate" @submit="onSubmit">
<UFormField name="email" label="邮箱">
<UInput v-model="state.email" placeholder="you@example.com" />
</UFormField>
<UButton type="submit" class="mt-4">提交</UButton>
</UForm>
</template>
<script setup>
const state = reactive({
email: ''
})
function validate(state) {
const errors = []
if (!state.email) {
errors.push({ name: 'email', message: '邮箱不能为空' })
}
return errors
}
function onSubmit() {
console.log('提交的数据', state)
}
</script>
几个要点。
:state是表单数据对象,字段用v-model双向绑定。:validate是一个函数,返回「错误数组」,每项含name(对应UFormField的name)和message。@submit在「校验通过」后触发;校验没过不会提交。UButton的type="submit"会让它成为提交按钮(按钮 UButton 里讲过)。
常用输入组件
除了 UInput,Nuxt UI 还有一组输入控件,用法都类似,都是 v-model 绑定值。
<template>
<UForm :state="state" @submit="onSubmit">
<UFormField name="name" label="姓名">
<UInput v-model="state.name" />
</UFormField>
<UFormField name="bio" label="简介">
<UTextarea v-model="state.bio" />
</UFormField>
<UFormField name="role" label="角色">
<USelect v-model="state.role" :items="['管理员', '普通用户']" />
</UFormField>
<UFormField name="notify" label="接收通知">
<USwitch v-model="state.notify" />
</UFormField>
<UFormField name="agree" label="同意条款">
<UCheckbox v-model="state.agree" label="我已阅读并同意" />
</UFormField>
<UButton type="submit" class="mt-4">提交</UButton>
</UForm>
</template>
<script setup>
const state = reactive({
name: '',
bio: '',
role: undefined,
notify: false,
agree: false
})
function onSubmit() {
console.log(state)
}
</script>
USelect 用 :items 传选项数组;USwitch 和 UCheckbox 绑定布尔值。UFormField 的 label 会自动渲染成字段标题。
用 zod 做校验
上面的 :validate 是手写判断,字段多了会啰嗦。Nuxt UI 支持传 :schema(用 zod 定义),校验规则集中写好,错误信息也能自动生成。
npm install zod
<template>
<UForm :schema="schema" :state="state" @submit="onSubmit">
<UFormField name="email" label="邮箱">
<UInput v-model="state.email" />
</UFormField>
<UButton type="submit" class="mt-4">提交</UButton>
</UForm>
</template>
<script setup>
import { z } from 'zod'
const schema = z.object({
email: z.string().email('请输入合法的邮箱')
})
const state = reactive({
email: ''
})
function onSubmit() {
console.log(state)
}
</script>
:schema 和 :validate 二选一即可,新手推荐先用 :validate 手写,规则复杂了再上 zod。
提交中的加载态
提交按钮常常要在请求期间禁用并转圈,用 loading-auto 最省事(见 按钮 UButton)。
<template>
<UForm :state="state" :validate="validate" @submit="onSubmit">
<UFormField name="email" label="邮箱">
<UInput v-model="state.email" />
</UFormField>
<UButton type="submit" loading-auto class="mt-4">提交</UButton>
</UForm>
</template>
<script setup>
const state = reactive({ email: '' })
function validate(state) {
const errors = []
if (!state.email.includes('@')) {
errors.push({ name: 'email', message: '邮箱格式不正确' })
}
return errors
}
// 返回 Promise,按钮会自动转圈直到它结束
function onSubmit() {
return new Promise(resolve => setTimeout(resolve, 1000))
}
</script>
常见问题
| 问题 | 解决方法 |
|---|---|
| 错误信息不显示 | 确认 UFormField 的 name 和 validate 返回的 name 一致 |
| 提交没反应 | 检查是否触发了校验失败(有错误时 @submit 不会执行) |
USelect 不显示选项 |
确认 :items 传的是数组,且 v-model 初始值类型匹配 |