Form组件介绍
form表单常用在回显表单信息 + 验证表单 + 提交表单信息。通常包含输入框, 选择框,日期选择框,文本框等可输入的组件。
封装思路
通过配置文件生成一个基本的表单,然后配合数据的双向绑定得到我们提交的数据,同时尽量保留第三方 UI 库组件提供的属性(Attributes)、插槽(Slots)和自定义事件(Events)
实现
1. 数据驱动
<template>
<a-form
:model="formState"
name="basic"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }"
autocomplete="off"
>
<a-form-item
label="Username"
name="username"
:rules="[{ required: true, message: 'Please input your username!' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="Password"
name="password"
:rules="[{ required: true, message: 'Please input your password!' }]"
>
<a-input-password v-model:value="formState.password" />
</a-form-item>
<a-form-item name="remember" :wrapper-col="{ offset: 8, span: 16 }">
<a-checkbox v-model:checked="formState.remember">Remember me</a-checkbox>
</a-form-item>
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
<a-button type="primary" html-type="submit">Submit</a-button>
</a-form-item>
</a-form>
</template>
- Form表单数据绑定在a-form上::model="form",form就是表单的数据对象。
- 表单里面的每一项是放在a-form-item标签里面,放入我们想渲染出来的组件,如输入框,选择框等。
- 每个a-form-item中可以绑定了label、rules等属性
通过分析Form代码我们可以通过一个配置文件去遍历a-form-item,然后在a-form-item上面绑定我们需要的属性,以及a-form-item标签里面各种输入组件。
这个配置文件应包括 label 标签的文本 name表单域 model 字段rules检验规则等表单的api,也应该包括内部可输入组件的api,比如类型(输入框、选择框、日期框等)
2.v-model实现父子组件双向数据绑定
原理: v-model绑定在组件上的时候做了以下步骤 在父组件内给子组件标签添加 v-model ,其实就是给子组件绑定了 value 属性 子组件内使用 props 拿到父组件传递下来的value。 子组件内部更改 value 的时候,必须通过 emit('update:value', e)更新父组件v-model绑定的变量
props 传值是单向的,即父组件中的数据改变会反映到子组件中,但在子组件内部不能主动改变 props的值,子组件中不能使用 v-model="modelValue" 之类方法试图直接改变父组件的值。
父组件 v-model传递的值的默认名称是modelValue,子组件通过props接收modelValue
<Ainput v-model="data" />
子组件
1.拆成:value='modelValue',监控子组件的更新事件触发emit('update:modelValue', e)
// 子组件
//
<template>
<div>
<a-input :value="modelValue" v-bind="$attrs" @change="change" />
</div>
</template>
<script lang="ts" setup name="AInput">
defineProps({
modelValue: {
type: String,
default: '',
},
})
const emit = defineEmits(['update:modelValue'])
// 更新modelValue
const change = (e: { target: HTMLInputElement }) => {
emit('update:modelValue', e.target.value.trim())
}
</script>
- 通过计算属性computed的set/get实现双向绑定
// 子组件
<template>
<div>
<a-input v-model:value="modelValue" v-bind="$attrs" />
</div>
</template>
<script lang="ts" setup name="AInput">
import { computed } from 'vue'
const props = defineProps({
modelValue: {
type: String,
default: '',
},
})
const emit = defineEmits(['update:modelValue'])
// 双向绑定
const formValue = computed({
//get父组件的modelValue
get: () => {
return props.modelValue
},
// input更新formValue时触发set,set通过emit通知父组件更新modelValue
set: (val) => {
emit('update:modelValue', val)
emit('getvalue', val, props.meta.colName)
},
})
</script>
如果是对象的话可以使用vueuse的useVModel方法
<script lang="ts" setup>
import { useVModel } from '@vueuse/core'
const props = defineProps({
//表单数据
formData: {
type: Object as PropType<ObjectExtends>,
default: () => {
return {}
},
},
})
const emit = defineEmits(['update:formData'])
// 实现双向绑定 每当 prop 发生变化时,model 就会改变。但只要它从这个组件中被改变,它就会发出update:formData, 通过父组件的v-model指令触发更新。
const model = useVModel(props, 'formData', emit)
</script>
3. Attributes透传
在 Vue 3.x 当中,通过v-bind
和$attrs
属性就可以进行 props 属性和 event 事件的透传分发了。
$attrs
透传属性包括props
,events
, class
,style
,id
等。(不包含接收组件显式声明的 props
、emits
以及slots
)
{
title: 'input1',
colName: 'input1',
controlType: 100,
rules: [{ required: true, message: 'input1不可为空' }],
style: 'background: red',
class: 'normalInput',
},
// 子组件
<template>
<div>
<a-input v-model:value="modelValue" v-bind="$attrs" />
</div>
</template>
如果不希望透传某些属性比如rules
, 可以通过useAttrs
来实现
// 子组件
<template>
<div>
<a-input v-model:value="modelValue" v-bind="filteredAttrs" />
</div>
</template>
const attrs = useAttrs()
const filteredAttrs = computed(() => {
return { ...attrs, rules: undefined }
})