Form
Form是 Vant 端的轻量表单容器,负责承接 Formily 的提交与预览态,同时给内部FormItem提供统一的布局上下文。
提示
这里没有直接复用 Vant 原生 Form 的校验流程,而是保留 Formily 作为唯一校验入口。Form 主要负责承接 form.submit() / onAutoSubmit,并给内部 FormItem 透传 labelWidth、labelAlign、inputAlign 等布局属性。
基础使用
vue
<script setup lang="ts">
import { createForm } from '@formily/core'
import { Form, FormButtonGroup, FormItem, Input, Submit } from '@silver-formily/vant'
import { Field } from '@silver-formily/vue'
import { showDemoResult } from '../shared'
const form = createForm({
values: {
username: 'silver-formily',
bio: '让 Form 负责布局和提交,让 Formily 负责校验',
},
})
async function handleSubmit(values: typeof form.values) {
await showDemoResult(values)
}
</script>
<template>
<Form
:form="form"
label-width="5em"
label-align="left"
:on-auto-submit="handleSubmit"
>
<Field
name="username"
title="用户名"
:decorator="[FormItem]"
:component="[
Input,
{
placeholder: '请输入用户名',
},
]"
/>
<Field
name="bio"
title="简介"
:decorator="[
FormItem,
{
extra: 'Form 负责统一 labelWidth,FormItem 仍然可以单独覆盖',
labelAlign: 'top',
},
]"
:component="[
Input.TextArea,
{
rows: 3,
placeholder: '介绍一下这版封装思路',
},
]"
/>
<FormButtonGroup>
<Submit />
</FormButtonGroup>
</Form>
</template>统一标签冒号
vue
<script setup lang="ts">
import { Form, FormItem, Input } from '@silver-formily/vant'
</script>
<template>
<Form label-width="4.5em" colon>
<FormItem label="用户名">
<Input model-value="silver-formily" />
</FormItem>
<FormItem label="简介" label-align="top">
<Input.TextArea model-value="给所有标签统一加上冒号" rows="2" />
</FormItem>
</Form>
</template>标签对齐
vue
<script setup lang="ts">
import { Form, FormItem, Input } from '@silver-formily/vant'
</script>
<template>
<Form label-width="4.5em" label-align="left">
<FormItem label="左对齐">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form label-width="4.5em" label-align="right">
<FormItem label="右对齐">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form label-align="top">
<FormItem label="顶部对齐">
<Input.TextArea model-value="标签会被放到输入框上方" rows="2" />
</FormItem>
</Form>
</template>输入对齐
vue
<script setup lang="ts">
import { Form, FormItem, Input } from '@silver-formily/vant'
</script>
<template>
<Form input-align="left">
<FormItem label="左对齐">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form input-align="center">
<FormItem label="居中对齐">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form input-align="right">
<FormItem label="右对齐">
<Input model-value="silver-formily" />
</FormItem>
</Form>
</template>错误样式开关
vue
<script setup lang="ts">
import { Form, FormItem, Input } from '@silver-formily/vant'
</script>
<template>
<Form :show-error="true">
<FormItem label="显式开启" feedback-status="error" feedback-text="这里会显示红色错误样式">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form :show-error="false">
<FormItem label="显式关闭" feedback-status="error" feedback-text="错误文案还在,但不再渲染红色错误态">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form>
<FormItem label="默认值" feedback-status="error" feedback-text="默认也不会渲染红色错误态">
<Input model-value="silver-formily" />
</FormItem>
</Form>
</template>错误文案开关
vue
<script setup lang="ts">
import { Form, FormItem, Input } from '@silver-formily/vant'
</script>
<template>
<Form>
<FormItem label="默认文案" feedback-status="error" feedback-text="这里会展示错误文案">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form :show-error-message="false">
<FormItem label="关闭文案" feedback-status="error" feedback-text="这段文案会被统一隐藏">
<Input model-value="silver-formily" />
</FormItem>
</Form>
</template>错误文案对齐
vue
<script setup lang="ts">
import { Form, FormItem, Input } from '@silver-formily/vant'
</script>
<template>
<Form :show-error="true" error-message-align="left">
<FormItem label="左对齐" feedback-status="error" feedback-text="错误文案跟随 Form 配置左对齐">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form :show-error="true" error-message-align="center">
<FormItem label="居中对齐" feedback-status="error" feedback-text="错误文案跟随 Form 配置居中对齐">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form :show-error="true" error-message-align="right">
<FormItem label="右对齐" feedback-status="error" feedback-text="错误文案跟随 Form 配置右对齐">
<Input model-value="silver-formily" />
</FormItem>
</Form>
</template>禁用与只读
vue
<script setup lang="ts">
import { Form, FormItem, Input } from '@silver-formily/vant'
</script>
<template>
<Form disabled>
<FormItem label="整体禁用" extra="Form 级 disabled 会同步到真实输入框">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form readonly>
<FormItem label="整体只读" extra="Form 级 readonly 会同步到真实输入框">
<Input model-value="silver-formily" />
</FormItem>
</Form>
<Form readonly>
<FormItem label="单项覆盖" extra="Form 级 readonly 仍然可以被 FormItem 显式覆盖">
<Input model-value="这里仍然是只读态" />
</FormItem>
<FormItem label="允许编辑" :readonly="false">
<Input model-value="这项显式关闭 readonly" />
</FormItem>
</Form>
</template>Formily 实例切换状态
vue
<script setup lang="ts">
import { createForm } from '@formily/core'
import { Form, FormItem, Input } from '@silver-formily/vant'
import { Field } from '@silver-formily/vue'
import { Radio, RadioGroup } from 'vant'
import { ref, watch } from 'vue'
type FormPattern = 'editable' | 'readOnly' | 'disabled'
const form = createForm({
values: {
username: 'silver-formily',
bio: '点击下方单选框切换整个 Formily 实例的状态',
},
})
const currentPattern = ref<FormPattern>('editable')
watch(currentPattern, (pattern) => {
form.setPattern(pattern)
})
</script>
<template>
<Form :form="form" label-width="4.5em">
<Field
name="username"
title="用户名"
:decorator="[FormItem]"
:component="[Input]"
/>
<Field
name="bio"
title="简介"
:decorator="[
FormItem,
{
labelAlign: 'top',
},
]"
:component="[Input.TextArea, { rows: 2 }]"
/>
</Form>
<RadioGroup v-model="currentPattern" direction="horizontal" shape="dot">
<Radio name="editable">
编辑态
</Radio>
<Radio name="readOnly">
只读态
</Radio>
<Radio name="disabled">
禁用态
</Radio>
</RadioGroup>
</template>自动滚动到错误项
vue
<script setup lang="ts">
import { createForm } from '@formily/core'
import { Form, FormButtonGroup, FormItem, Input, Submit } from '@silver-formily/vant'
import { Field } from '@silver-formily/vue'
import { Radio, RadioGroup } from 'vant'
import { ref } from 'vue'
import { showDemoResult } from '../shared'
type ScrollPosition = 'start' | 'center' | 'end' | 'nearest'
const scrollPosition = ref<ScrollPosition>('center')
const form = createForm({
values: {
contact: 'Silver Formily',
mobile: '13800138000',
company: '银色科技园',
routeHint: '从东门进入后直行,在 2 号楼大厅前台登记。',
landmark: '靠近湖滨公园,旁边有一辆白色咖啡车。',
},
})
async function handleSubmit(values: typeof form.values) {
await showDemoResult(values)
}
</script>
<template>
<p class="demo-tip">
点击顶部提交按钮后,会自动滚动到第一个报错项。切换单选框可以观察不同的滚动定位位置。
</p>
<RadioGroup v-model="scrollPosition" direction="horizontal" shape="dot" class="demo-radios">
<Radio name="start">
start
</Radio>
<Radio name="center">
center
</Radio>
<Radio name="end">
end
</Radio>
<Radio name="nearest">
nearest
</Radio>
</RadioGroup>
<Form
:form="form"
label-width="5em"
label-align="left"
scroll-to-error
:scroll-to-error-position="scrollPosition"
:on-auto-submit="handleSubmit"
>
<FormButtonGroup>
<Submit>提交并定位错误</Submit>
</FormButtonGroup>
<Field
name="contact"
title="联系人"
:validator="{ required: true, message: '请输入联系人' }"
:decorator="[FormItem]"
:component="[Input, { placeholder: '请输入联系人' }]"
/>
<Field
name="mobile"
title="联系电话"
:validator="{ required: true, message: '请输入联系电话' }"
:decorator="[FormItem]"
:component="[Input, { placeholder: '请输入联系电话' }]"
/>
<Field
name="company"
title="园区名称"
:validator="{ required: true, message: '请输入园区名称' }"
:decorator="[FormItem]"
:component="[Input, { placeholder: '请输入园区名称' }]"
/>
<Field
name="routeHint"
title="送达提示"
:validator="{ required: true, message: '请输入送达提示' }"
:decorator="[
FormItem,
{
labelAlign: 'top',
},
]"
:component="[Input.TextArea, { rows: 3, placeholder: '描述一下送达路线' }]"
/>
<Field
name="landmark"
title="周边地标"
:validator="{ required: true, message: '请输入周边地标' }"
:decorator="[
FormItem,
{
labelAlign: 'top',
},
]"
:component="[Input.TextArea, { rows: 3, placeholder: '补充一个容易识别的地标' }]"
/>
<Field
name="building"
title="楼栋门牌"
:validator="{ required: true, message: '请输入楼栋门牌' }"
:decorator="[FormItem]"
:component="[Input, { placeholder: '这里默认留空,用于演示自动滚动' }]"
/>
<Field
name="detail"
title="详细地址"
:validator="{ required: true, message: '请输入详细地址' }"
:decorator="[
FormItem,
{
labelAlign: 'top',
},
]"
:component="[Input.TextArea, { rows: 3, placeholder: '继续补充楼层、门禁或收货说明' }]"
/>
</Form>
</template>
<style scoped>
.demo-tip {
padding: 12px 16px 0;
color: var(--van-text-color-2);
font-size: 13px;
line-height: 1.6;
}
.demo-radios {
padding: 12px 16px;
}
</style>API
Form 专属属性
| 属性名 | 类型 | 描述 | 默认值 |
|---|---|---|---|
form | Form | 显式传入 Formily form 实例 | - |
onAutoSubmit | (values: Record<string, any>) => any | 原生 submit 时触发 Formily submit 成功 | - |
onAutoSubmitFailed | (error: IFormFeedback[]) => void | 原生 submit 时触发 Formily submit 失败 | - |
scrollToError | boolean | 提交失败时是否自动滚动到第一个错误项 | false |
scrollToErrorPosition | 'start' | 'center' | 'end' | 'nearest' | 自动滚动时传给 scrollIntoView 的 block 位置 | - |
继承给 FormItem 的布局属性
下列属性会作为表单级默认值透传给内部 FormItem,单个 FormItem 上显式传值时会覆盖表单级配置。对于 disabled 和 readonly,如果 Form 组件自身没有显式传值,还会继续回退读取传入的 Formily form.disabled / form.readOnly。
| 属性名 | 类型 | 描述 | 默认值 |
|---|---|---|---|
colon | boolean | 标签后是否展示冒号 | - |
disabled | boolean | 是否整体禁用 | - |
readonly | boolean | 是否整体只读 | - |
required | boolean | enum | 是否整体展示必填态 | - |
labelWidth | number | string | 统一标签宽度 | - |
labelAlign | enum | 统一标签对齐 | - |
inputAlign | enum | 统一输入区域对齐 | - |
errorMessageAlign | enum | 统一错误文案对齐 | - |
showError | boolean | 是否统一展示错误态 | false |
showErrorMessage | boolean | 是否展示错误文案 | true |