You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NuxtUI中配置UForm和UFormGroup实现嵌套动态表单错误提示

动态嵌套表单错误提示不显示的解决方法

问题场景

我用Vue 3 + Zod构建了一个动态训练表单,支持添加训练日和每个训练日的训练项目。一级字段(如name)能自动显示错误提示,但嵌套的day、exerciseName字段无法触发错误提示。虽然通过schema.parse(state)能捕获到Zod错误(包含字段路径信息),但表单组件不会自动关联这些错误到对应的输入框。

我的表单代码:

<template>
  <div>
    <h1>Training Form</h1>
  </div>
  <UForm :state="state" :schema="schema" @submit="handleSubmit">
    <UFormGroup label="Name" name="name">
      <UInput v-model="state.name" />
    </UFormGroup>
    <UFormGroup label="Training Week" name="trainingWeek">
      <div v-for="(day, index) in state.trainingWeek" :key="index">
        <UFormGroup label="Day" name="trainingWeek[' + index + '].day">
          <UInput v-model="day.day" />
        </UFormGroup>
        <UFormGroup label="Exercises" name="trainingWeek[' + index + '].exercises">
          <div v-for="(exercise, exerciseIndex) in day.exercises" :key="exerciseIndex">
            <UInput v-model="exercise.exerciseName" />
          </div>
        </UFormGroup>
        <div class="mt-4 flex justify-end">
          <UButton @click="addExercise(index)">Add Exercise</UButton>
        </div>
      </div>
    </UFormGroup>
    <div class="mt-4 flex justify-end">
      <UButton @click="addDay">Add Day</UButton>
      <UButton type="submit">Submit</UButton>
    </div>
  </UForm>
</template>

<script setup lang="ts">
import { z } from "zod";
import type { FormSubmitEvent } from "#ui/types";
const state = reactive({
  name: "",
  trainingWeek: [
    {
      day: "",
      exercises: [
        {
          exerciseName: "",
        },
      ],
    },
  ],
});

const schema = z.object({
  name: z.string().min(1, "Required field"),
  trainingWeek: z.array(
    z.object({
      day: z.string().min(1, "Required field"),
      exercises: z.array(
        z.object({
          exerciseName: z.string().min(1, "Required field"),
        }),
      ),
    }),
  ),
});

type Schema = z.infer<typeof schema>;

const handleSubmit = (event: FormSubmitEvent<Schema>) => {
  console.log(JSON.stringify(event.data, null, 2));
};

const addExercise = (index: number) => {
  state.trainingWeek[index].exercises.push({ exerciseName: "" });
};

const addDay = () => {
  state.trainingWeek.push({ day: "", exercises: [{ exerciseName: "" }] });
};
</script>

捕获到的Zod错误示例:

ZodError: [
  {
    "code": "too_small",
    "minimum": 1,
    "type": "string",
    "inclusive": true,
    "exact": false,
    "message": "Required field",
    "path": [
      "trainingWeek",
      0,
      "day"
    ]
  },
  {
    "code": "too_small",
    "minimum": 1,
    "type": "string",
    "inclusive": true,
    "exact": false,
    "message": "Required field",
    "path": [
      "trainingWeek",
      0,
      "exercises",
      0,
      "exerciseName"
    ]
  }
]

解决步骤

1. 修正UFormGroup的name属性格式

当前嵌套字段的name写法存在语法错误,表单组件需要正确的数组索引+点路径格式来关联字段与错误信息。改成模板字符串的正确写法:

  • 训练日字段:name="trainingWeek[${dayIndex}].day"
  • 训练项目字段:为每个exercise单独包裹UFormGroup,指定路径:name="trainingWeek[${dayIndex}].exercises[${exerciseIndex}].exerciseName"

修正后的模板核心部分:

<UFormGroup label="Training Week" name="trainingWeek">
  <div v-for="(day, dayIndex) in state.trainingWeek" :key="dayIndex">
    <!-- 修正训练日的name属性 -->
    <UFormGroup label="Day" :name="`trainingWeek[${dayIndex}].day`">
      <UInput v-model="day.day" />
    </UFormGroup>

    <!-- 为每个训练项目单独设置UFormGroup -->
    <div class="mt-3">
      <label class="block text-sm font-medium text-gray-700">Exercises</label>
      <div v-for="(exercise, exerciseIndex) in day.exercises" :key="exerciseIndex" class="mt-2">
        <UFormGroup 
          :name="`trainingWeek[${dayIndex}].exercises[${exerciseIndex}].exerciseName`"
          label="Exercise Name"
        >
          <UInput v-model="exercise.exerciseName" />
        </UFormGroup>
      </div>
    </div>

    <div class="mt-4 flex justify-end">
      <UButton @click="addExercise(dayIndex)">Add Exercise</UButton>
    </div>
  </div>
</UFormGroup>

2. 开启实时验证(可选)

如果需要在输入过程中实时显示错误,可以给UForm添加validate-on="input"属性,触发输入时自动验证:

<UForm :state="state" :schema="schema" @submit="handleSubmit" validate-on="input">

3. 手动映射错误(兜底方案)

如果组件仍未自动关联错误,可以手动捕获Zod错误并映射到响应式错误对象中:

import { ZodError } from "zod";

const errors = reactive<Record<string, string>>({});

watchEffect(() => {
  try {
    schema.parse(state);
    // 验证通过时清空错误
    Object.keys(errors).forEach(key => delete errors[key]);
  } catch (error) {
    if (error instanceof ZodError) {
      Object.keys(errors).forEach(key => delete errors[key]);
      // 将Zod的路径数组转为点格式字段名
      error.issues.forEach(issue => {
        const fieldPath = issue.path.join(".");
        errors[fieldPath] = issue.message;
      });
    }
  }
});

然后在UFormGroup中手动绑定错误:

<UFormGroup 
  :name="`trainingWeek[${dayIndex}].day`"
  label="Day"
  :error="errors[`trainingWeek.${dayIndex}.day`]"
>

关键说明

  • 表单组件依赖name属性的路径与Zod Schema结构完全匹配,才能自动关联错误信息。
  • 每个需要验证的输入框必须包裹在对应的UFormGroup中,确保错误提示能正确挂载。
  • 实时验证会带来一定性能开销,可根据场景选择input/blur/submit等验证时机。

内容的提问来源于stack exchange,提问作者Adharsh M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 03:50:17