---
title: "条件渲染"
description: "根据表单状态动态显示或隐藏字段，创建智能响应式表单。"
seo_title: "Reactive & Conditional"
seo_description: "Drive AutoForm fields reactively — conditional rendering, dependent values and context-aware meta via reactive functions."
canonical_url: "https://nuxt.mhaibaraai.cn/docs/auto-form/reactive"
---
# 条件渲染

> 根据表单状态动态显示或隐藏字段，创建智能响应式表单。

## 概述

AutoForm 的 meta 字段均可传入**响应式函数** `(ctx) => value`，`ctx` 为 [`AutoFormFieldContext`](/docs/auto-form/slots#autoformfieldcontext)（含 `state`、`path`、`value`）。函数会随表单状态自动重算，从而实现条件渲染与字段联动。

> \[\!NOTE\]
> 
> 条件渲染有两种方式，行为不同：
> 
> - **`if`** \- 显示条件，结果为 `false` 时字段**不会创建**（不挂载、不参与校验）。
> - **`hidden`** \- 是否隐藏，结果为 `true` 时字段**仍保留在 DOM 与表单数据中**，只是视觉隐藏。

## 示例

### 条件渲染

`if` 与 `hidden` 接收返回布尔值的函数：切换账户类型时，企业字段（`if`）按需挂载/卸载；勾选条款后，订阅字段（`hidden`）从隐藏切换为显示。

> \[\!TIP\]
> 
> 需要彻底排除字段（不校验、不提交）用 
> 
> if
> 
> ；只想临时隐藏但保留取值用 
> 
> hidden
> 
> 。

```vue [AutoFormReactiveConditionalExample.vue]
<script lang="ts" setup>
import type { FormSubmitEvent } from '@nuxt/ui'
import type { z } from 'zod'
import type { AutoFormFieldContext } from '@movk/nuxt'

const { afz } = useAutoForm()
const toast = useToast()

const schema = afz.object({
  accountType: afz
    .enum(['personal', 'business'])
    .default('personal')
    .meta({ label: '账户类型', description: '切换后观察 company/tax 字段的挂载与卸载' }),

  username: afz
    .string({ controlProps: { placeholder: '请输入用户名' } })
    .min(3, '用户名至少需要 3 个字符')
    .meta({ label: '用户名' }),

  companyName: afz
    .string({ controlProps: { placeholder: '请输入公司名称' } })
    .meta({
      label: '公司名称（if）',
      hint: 'if：仅企业账户挂载，个人账户下字段不创建、不参与校验',
      if: ({ state }: AutoFormFieldContext) => state?.accountType === 'business'
    }),

  taxId: afz
    .string({ controlProps: { placeholder: '请输入税号' } })
    .meta({
      label: '税号（if）',
      hint: 'if：与公司名称同条件挂载',
      if: ({ state }: AutoFormFieldContext) => state?.accountType === 'business'
    }),

  agreeToTerms: afz
    .boolean()
    .default(false)
    .meta({ label: '同意服务条款', description: '勾选后下方订阅字段显示' }),

  newsletter: afz
    .boolean()
    .default(true)
    .meta({
      label: '订阅邮件通知（hidden）',
      description: 'hidden：未同意条款时隐藏，但字段始终保留在 DOM 与表单数据中',
      hidden: ({ state }: AutoFormFieldContext) => !state?.agreeToTerms
    })
})

type Schema = z.output<typeof schema>

const form = ref<Partial<Schema>>({})

async function onSubmit(event: FormSubmitEvent<Schema>) {
  toast.add({
    title: '提交成功',
    color: 'success',
    description: JSON.stringify(event.data, null, 2)
  })
}
</script>

<template>
  <MAutoForm :schema="schema" :state="form" @submit="onSubmit" />
</template>
```

### 依赖联动

函数式 `controlProps` 随其它字段变化重算控件属性。下例中城市选项依赖国家字段，并通过 `watch` 在国家变化时清空已失效的旧值。

> \[\!NOTE\]
> 
> 联动仅重算控件属性，不会自动清除旧值。需要清空失效选项时，配合 
> 
> watch
> 
>  手动重置。

```vue [AutoFormReactiveDependentExample.vue]
<script lang="ts" setup>
import type { z } from 'zod'

const { afz } = useAutoForm()

const countryOptions = ['CN', 'US', 'JP'] as const
type CountryCode = typeof countryOptions[number]

const cityItemsByCountry: Record<CountryCode, string[]> = {
  CN: ['北京', '上海', '广州'],
  US: ['New York', 'San Francisco', 'Seattle'],
  JP: ['Tokyo', 'Osaka', 'Kyoto']
}

const schema = afz.object({
  country: afz.enum(countryOptions).default('CN').meta({ label: '国家' }),
  city: afz.enum([], {
    type: 'enum',
    controlProps: ({ state }) => ({
      items: cityItemsByCountry[(state.country as CountryCode | undefined) ?? 'CN']
    })
  }).meta({ label: '城市', hint: '选项随国家变化，切换国家后会清空已选城市' })
})

const state = reactive<Partial<z.output<typeof schema>>>({})

watch(() => state.country, () => {
  state.city = undefined
})
</script>

<template>
  <MAutoForm :schema="schema" :state="state" />
</template>
```

> \[\!NOTE\]
> 
> 当 `items` 来源是**异步或外部响应式数据**（基于 `useApiFetch`、`useAsyncData` 的 `computed` / `ref`）时，务必用**函数式** `controlProps: () => ({ items: source.value })`，或直接传响应式来源 `items: source`，切勿写成 `items: source.value`。
> 
> schema 在 `setup` 期只构建一次，`source.value` 是当时的一次性快照：SSR 注水时数据恰好就绪会「看似正常」，但客户端导航重新挂载、异步数据尚未到达时会冻结为空数组，后续选项更新不再生效。函数式 `controlProps` 在渲染时实时求值，可规避此问题。

## API

### `meta`

| Name     | Type                                           | Description               |
| -------- | ---------------------------------------------- | ------------------------- |
| `if`     | `ReactiveValue<boolean, AutoFormFieldContext>` | 显示条件，`false` 时字段不创建       |
| `hidden` | `ReactiveValue<boolean, AutoFormFieldContext>` | 是否隐藏，`true` 时保留 DOM 但视觉隐藏 |

> \[\!TIP\]
> 
> 除上述字段外，
> 
> label
> 
> 、
> 
> hint
> 
> 、
> 
> description
> 
> 、
> 
> controlProps
> 
> 、
> 
> class
> 
>  等 meta 字段同样支持 
> 
> (ctx) => value
> 
>  函数形态，按表单状态动态求值。


## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
