跳到主要内容

3.4 事件与表单绑定

本章目标: 让输入框与状态保持一致,处理提交、防止刷新,给出清晰的错误反馈。

一个完整的本地表单​

src/App.vue
<script setup lang="ts">
import { ref } from 'vue';

const title = ref('');
const error = ref('');
const tasks = ref<{ id: number; title: string }[]>([]);
let nextId = 1;

function addTask() {
const value = title.value.trim();
if (!value) {
error.value = '请输入任务名称';
return;
}
if (value.length > 100) {
error.value = '任务名称不能超过 100 个字符';
return;
}
tasks.value.push({ id: nextId++, title: value });
title.value = '';
error.value = '';
}
</script>

<template>
<main>
<form @submit.prevent="addTask" novalidate>
<label for="task-title">任务名称</label>
<input id="task-title" v-model.trim="title" maxlength="100"
:aria-invalid="Boolean(error)" aria-describedby="title-error" />
<button type="submit">添加</button>
<p id="title-error" role="alert">{{ error }}</p>
</form>
<ul><li v-for="task in tasks" :key="task.id">{{ task.title }}</li></ul>
</main>
</template>

novalidate 关闭浏览器自动弹出的验证提示,便于观察自定义校验。若使用原生 required 校验,也要为业务和 API 层保留独立检查。

事件与修饰符​

@submit.prevent 会调用 preventDefault(),阻止表单默认导航。常用修饰符:

修饰符意义示例
.prevent阻止默认行为表单提交
.stop阻止事件冒泡行内按钮不触发行点击
.once只监听一次一次性初始化动作
.enter限定 Enter 键@keyup.enter="search"

需要原生事件时可以使用 $event:@click="handleClick($event)"。TypeScript 函数里将它标注为 MouseEvent。

v-model 与控件类型​

控件绑定状态
文本输入框字符串
单个 checkbox布尔值
多个同组 checkbox选中值数组
radio / select选中项的值
select multiple多个选中值的数组

常用修饰符 .trim 去掉首尾空白,.number 尝试数字转换,.lazy 改为在 change 时同步。.number 在空输入等场景不保证一定返回数字,仍要验证范围和类型。

脚本中已定义 filter 的选择框片段
<select v-model="filter">
<option value="all">全部</option>
<option value="pending">未完成</option>
<option value="done">已完成</option>
</select>

异步提交状态​

接入 API 后需要处理重复操作。脚本里同时检查 saving 并禁用按钮:

createTask 已定义时的提交片段
const saving = ref(false);

async function submit() {
if (saving.value) return;
saving.value = true;
try {
await createTask(title.value.trim());
title.value = '';
error.value = '';
} catch (cause) {
error.value = cause instanceof Error ? cause.message : '保存失败';
} finally {
saving.value = false;
}
}

finally 确保失败后也能再次提交。提交失败时保留输入,用户才能修改或重试。服务端仍需为重要操作提供幂等或重复请求保护。

练习​

给表单添加一个优先级 select,值为 normal 和 high。任务新增后同时展示名称和优先级。

参考答案

脚本增加 const priority = ref<'normal' | 'high'>('normal'),给任务模型增加同类型的 priority。新增时把 priority.value 保存到任务对象;模板中用 v-model="priority" 绑定 select,选项值分别为 normal 和 high。

这是一次模型、输入和显示同步修改的练习。接入 API 时 DTO 也要一起更新。

参考:事件处理、表单输入绑定。