useFieldArray
字段列表:订单项、电话、参与者。为 v-for 提供稳定的键以及行操作。删除或重新排序行时,“已访问”标记会随行移动 —— 错误不会跳到相邻的行。
签名
ts
function useFieldArray<T>(path: string): FieldArray<T>
function useFieldArray<T>(form: Form, path: string): FieldArray<T>| 返回值 | 说明 |
|---|---|
fields | { key, index, path, value }[] —— key 用于 v-for,path 用于错误和 touch |
append(value)、prepend(value) | 在末尾 / 开头添加行 |
insert(index, value) | 插入行 |
remove(index) | 删除行 |
move(from, to)、swap(a, b) | 调整行的顺序 |
replace(values) | 替换整个列表 |
不传 form 时从上下文获取表单。如果 values 中没有该数组,会自动创建。直接修改(form.values.items.push(...))同样有效 —— 新行会获得键。
示例
vue
<script setup lang="ts">
const Order = object({
items: pipe(array(object({ title: pipe(string(), minLength(2)), qty: pipe(coerce.number(), min(1)) })), minLength(1)),
});
const form = useForm(Order, { initial: { items: [{ title: '', qty: '1' }] } });
const items = useFieldArray<{ title: string; qty: string }>(form, 'items');
</script>
<template>
<div v-for="field in items.fields.value" :key="field.key">
<input v-model="field.value.title" :name="`${field.path}.title`" @blur="form.touch(`${field.path}.title`)" />
<span>{{ form.errors[`${field.path}.title`] }}</span>
<input v-model="field.value.qty" :name="`${field.path}.qty`" @blur="form.touch(`${field.path}.qty`)" />
<button type="button" :disabled="field.index === 0" @click="items.move(field.index, field.index - 1)">↑</button>
<button type="button" @click="items.remove(field.index)">×</button>
</div>
<button type="button" @click="items.append({ title: '', qty: '1' })">+</button>
</template>为什么不用索引作为 :key:删除第一行后所有索引都会移动,Vue 会复用错误行的输入框 —— 焦点和已输入的文本会“跳动”。