高性能表单控件,自带数据域管理。包含数据录入、校验以及对应样式。
何时使用
- 用于创建一个实体或收集信息。
- 需要对输入的数据类型进行校验时。
表单
我们为 form
提供了以下三种排列方式:
- 水平排列:标签和表单控件水平排列;(默认)
- 垂直排列:标签和表单控件上下垂直排列;
- 行内排列:表单项水平行内排列。
表单域
表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等。
代码演示
基本用法
基本用法。
<template>
<f-form :model="formState" ref="formRef" :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
<f-form-item label="Username" name="username" :rules="[{ required: true, message: 'Please input your username!' }]">
<f-input v-model:value="formState.username" />
</f-form-item>
<f-form-item label="Password" name="password" :rules="[{ required: true, message: 'Please input your password!' }]">
<f-input-password v-model:value="formState.password" />
</f-form-item>
<f-form-item name="remember" :wrapper-col="{ offset: 4, span: 16 }">
<f-checkbox v-model:checked="formState.remember">Remember me</f-checkbox>
</f-form-item>
<f-form-item :wrapper-col="{ offset: 4, span: 16 }">
<f-button type="primary" @click="submit">Submit</f-button>
</f-form-item>
</f-form>
</template>
<script lang="ts">
import { ref } from 'vue'
import { defineComponent, reactive } from 'vue'
interface FormState {
username: string
password: string
remember: boolean
}
export default defineComponent({
setup() {
const formState = reactive<FormState>({
username: '',
password: '',
remember: true,
})
const formRef = ref()
const submit = () => {
formRef.value
.validate()
.then(result => {
console.log('result :>> ', result)
})
.catch(err => {
console.log('err :>> ', err)
})
}
return {
formState,
submit,
formRef
}
},
})
</script>
表单布局
表单有三种布局。
<template>
<f-form :layout="formState.layout" :model="formState" v-bind="formItemLayout">
<f-form-item label="Form Layout">
<f-radio-group v-model:value="formState.layout">
<f-radio-button value="horizontal">Horizontal</f-radio-button>
<f-radio-button value="vertical">Vertical</f-radio-button>
<f-radio-button value="inline">Inline</f-radio-button>
</f-radio-group>
</f-form-item>
<f-form-item label="Field A">
<f-input v-model:value="formState.fieldA" placeholder="input placeholder" />
</f-form-item>
<f-form-item label="Field B">
<f-input v-model:value="formState.fieldB" placeholder="input placeholder" />
</f-form-item>
<f-form-item :wrapper-col="buttonItemLayout.wrapperCol">
<f-button type="primary">Submit</f-button>
</f-form-item>
</f-form>
</template>
<script lang="ts">
import { computed, defineComponent, reactive } from 'vue'
import type { UnwrapRef } from 'vue'
interface FormState {
layout: 'horizontal' | 'vertical' | 'inline'
fieldA: string
fieldB: string
}
export default defineComponent({
setup() {
const formState: UnwrapRef<FormState> = reactive({
layout: 'horizontal',
fieldA: '',
fieldB: '',
})
const formItemLayout = computed(() => {
const { layout } = formState
return layout === 'horizontal'
? {
labelCol: { span: 4 },
wrapperCol: { span: 14 },
}
: {}
})
const buttonItemLayout = computed(() => {
const { layout } = formState
return layout === 'horizontal'
? {
wrapperCol: { span: 14, offset: 4 },
}
: {}
})
return {
formState,
formItemLayout,
buttonItemLayout,
}
},
})
</script>
嵌套结构与校验信息
name 属性支持嵌套数据结构。通过 validateMessages 或 message 自定义校验信息模板,模板内容可参考此处。
<template>
<f-form
:model="formState"
v-bind="layout"
name="nest-messages"
:validate-messages="validateMessages"
@finish="onFinish"
>
<f-form-item :name="['user', 'name']" label="Name" :rules="[{ required: true }]">
<f-input v-model:value="formState.user.name" />
</f-form-item>
<f-form-item :name="['user', 'email']" label="Email" :rules="[{ type: 'email' }]">
<f-input v-model:value="formState.user.email" />
</f-form-item>
<f-form-item :name="['user', 'age']" label="Age" :rules="[{ type: 'number', min: 0, max: 99 }]">
<f-input-number v-model:value="formState.user.age" />
</f-form-item>
<f-form-item :name="['user', 'website']" label="Website">
<f-input v-model:value="formState.user.website" />
</f-form-item>
<f-form-item :name="['user', 'introduction']" label="Introduction">
<f-textarea v-model:value="formState.user.introduction" />
</f-form-item>
<f-form-item :wrapper-col="{ ...layout.wrapperCol, offset: 4 }">
<f-button type="primary" html-type="submit">Submit</f-button>
</f-form-item>
</f-form>
</template>
<script lang="ts">
import { defineComponent, reactive } from 'vue'
export default defineComponent({
setup() {
const layout = {
labelCol: { span: 4 },
wrapperCol: { span: 16 },
}
const validateMessages = {
required: '${label} is required!',
types: {
email: '${label} is not a valid email!',
number: '${label} is not a valid number!',
},
number: {
range: '${label} must be between ${min} and ${max}',
},
}
const formState = reactive({
user: {
name: '',
age: undefined,
email: '',
website: '',
introduction: '',
},
})
const onFinish = (values: any) => {
console.log('Success:', values)
}
return {
formState,
onFinish,
layout,
validateMessages,
}
},
})
</script>
API
参数 | 说明 | 类型 | 默认值 | 版本 |
---|
colon | 配置 Form.Item 的 colon 的默认值 (只有在属性 layout 为 horizontal 时有效) | boolean | true | |
hideRequiredMark | 隐藏所有表单项的必选标记 | Boolean | false | |
labelAlign | label 标签的文本对齐方式 | 'left' | 'right' | 'right' | |
labelCol | label 标签布局,同 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12} 或 sm: {span: 3, offset: 12} | object | | |
labelWrap | label 标签的文本换行方式 | boolean | false | 3.0 |
layout | 表单布局 | 'horizontal'|'vertical'|'inline' | 'horizontal' | |
model | 表单数据对象 | object | | |
name | 表单名称,会作为表单字段 id 前缀使用 | string | - | 2.0.0 |
noStyle | 为 true 时不带样式,作为纯字段控件使用 | boolean | false | 3.0 |
rules | 表单验证规则 | object | | |
scrollToFirstError | 提交失败自动滚动到第一个错误字段 | boolean | optionsopen in new window | false | 2.0.0 |
validateOnRuleChange | 是否在 rules 属性改变后立即触发一次验证 | boolean | true | |
validateTrigger | 统一设置字段校验规则 | string | string[] | change | 2.0.0 |
wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | object | | |
事件
事件名称 | 说明 | 回调参数 | 版本 | |
---|
finish | 提交表单且数据验证成功后回调事件 | function(values) | - | 2.0.0 |
finishFailed | 提交表单且数据验证失败后回调事件 | function({ values, errorFields, outOfDate }) | - | 2.0.0 |
submit | 数据验证成功后回调事件 | Function(e:Event) | | | |
validate | 任一表单项被校验后触发 | Function(name, status, errorMsgs) | | |
方法
方法名 | 说明 | 参数 | 版本 |
---|
clearValidate | 移除表单项的校验结果。传入待移除的表单项的 name 属性或者 name 组成的数组,如不传则移除整个表单的校验结果 | (nameList?: NamePath[]) => void | |
resetFields | 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果 | (nameList?: NamePath[]) => void | |
scrollToField | 滚动到对应字段位置 | (name: NamePath, options: [ScrollOptionsopen in new window]) => void | |
validate | 触发表单验证, 同 validateFields | (nameList?: NamePath[]) => Promise | |
validateFields | 触发表单验证 | (nameList?: NamePath[]) => Promise | |
NamePath
string | number | (string | number)[]
参数 | 说明 | 类型 | 默认值 | 版本 |
---|
autoLink | 是否自动关联表单域,对于大部分情况都可以使用自动关联,如果不满足自动关联的条件,可以手动关联,参见下方注意事项 | boolean | true | |
colon | 配合 label 属性使用,表示是否显示 label 后面的冒号 | boolean | true | |
extra | 额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 | string|slot | | |
hasFeedback | 配合 validateStatus 属性使用,展示校验状态图标,建议只配合 Input 组件使用 | boolean | false | |
help | 提示信息,如不设置,则会根据校验规则自动生成 | string|slot | | |
htmlFor | 设置子元素 label htmlFor 属性 | string | | |
label | label 标签的文本 | string|slot | | |
labelAlign | 标签文本对齐方式 | 'left' | 'right' | 'right' | |
labelCol | label 标签布局,同 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12} 或 sm: {span: 3, offset: 12} | object | | |
name | 表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的 | string | | |
required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean | false | |
rules | 表单验证规则 | object | array | | |
validateFirst | 当某一规则校验不通过时,是否停止剩下的规则的校验。 | boolean | false | 2.0.0 |
validateStatus | 校验状态,如不设置,则会根据校验规则自动生成,可选:'success' 'warning' 'error' 'validating' | string | | |
validateTrigger | 设置字段校验的时机 | string | string[] | change | 2.0.0 |
wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | object | | |
注意:
3.x
自 3.0 版本以后,Form.Item 不再劫持子元素,而是通过 provider / inject 依赖注入的方式进行自动校验,这种方式可以提高组件性能,子元素也不会限制个数,同样子元素也可以是进一步封装的高级组件。
但它同样会有一些缺点:
1、自定义组件如果希望 Form.Item 进行校验展示,你需要 const {id, onFieldChange, onFieldBlur} = useInjectFormItemContext()
注入,并调用相应的方法。
2、一个 Form.Item 只能收集一个表单项的数据,如果有多个表单项,会导致收集错乱,例如,
<a-form-item>
<a-input name="a"></a-input>
<a-input name="b"></a-input>
</a-form-item>
如上 Form.Item 并不知道需要收集 name="a"
还是 name="b"
,你可以通过如下三种方式去解决此类问题:
第一种,使用多个 a-form-item
:
<a-form-item>
<a-input name="a"></a-input>
<a-form-item><a-input name="b"></a-input></a-form-item>
</a-form-item>
第二种,使用自定义组件包裹,并在自定义组件中调用 useInjectFormItemContext
,相当于把多个表单项合并成了一个
<script>
import { Form } from 'ant-design-vue';
export default {
setup() {
const formItemContext = Form.useInjectFormItemContext();
},
};
</script>
<a-form-item>
<custom-com>
<a-input name="a"></a-input>
<a-input name="b"></a-input>
</custom-com>
</a-form-item>
第三种,组件库提供了一个 a-form-item-rest
组件,它会阻止数据的收集,你可以将不需要收集校验的表单项放到这个组件中即可,它和第一种方式很类似,但它不会产生额外的 dom 节点。
<a-form-item>
<a-input name="a"></a-input>
<a-form-item-rest><a-input name="b"></a-input></a-form-item-rest>
</a-form-item>