Ant design vue 动态表单
1、官方示例
首先我们先看一下官网给出的示例:
点击Add filed 会增加一个新的input输入框
点击删除的时候可以删除该行input框
下面给出官网的demo示例:
<template>
<a-form :form="form" @submit="handleSubmit">
<a-form-item
v-for="(k, index) in form.getFieldValue('keys')"
:key="k"
v-bind="index === 0 ? formItemLayout : formItemLayoutWithOutLabel"
:label="index === 0 ? 'Passengers' : ''"
:required="false"
>
<a-input
v-decorator="[
`names[${k}]`,
{
validateTrigger: ['change', 'blur'],
rules: [
{
required: true,
whitespace: true,
message: 'Please input passenger\'s name or delete this field.',
},
],
},
]"
placeholder="passenger name"
style="width: 60%; margin-right: 8px"
/>
<a-icon
v-if="form.getFieldValue('keys').length > 1"
class="dynamic-delete-button"
type="minus-circle-o"
:disabled="form.getFieldValue('keys').length === 1"
@click="() => remove(k)"
/>
</a-form-item>
<a-form-item v-bind="formItemLayoutWithOutLabel">
<a-button type="dashed" style="width: 60%" @click="add">
<a-icon type="plus" /> Add field
</a-button>
</a-form-item>
<a-form-item v-bind="formItemLayoutWithOutLabel">
<a-button type="primary" html-type="submit">
Submit
</a-button>
</a-form-item>
</a-form>
</template>
<script>
let id = 0;
export default {
data() {
return {
formItemLayout: {
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 },
},
},
formItemLayoutWithOutLabel: {
wrapperCol: {
xs: { span: 24, offset: 0 },
sm: { span: 20, offset: 4 },
},
},
};
},
beforeCreate() {
this.form = this.$form.createForm(this, { name: 'dynamic_form_item' });
this.form.getFieldDecorator('keys', { initialValue: [], preserve: true });
},
methods: {
remove(k) {
const { form } = this;
// can use data-binding to get
const keys = form.getFieldValue('keys');
// We need at least one passenger
if (keys.length === 1) {
return;
}
// can use data-binding to set
form.setFieldsValue({
keys: keys.filter(key => key !== k),
});
},
add() {
const { form } = this;
// can use data-binding to get
const keys = form.getFieldValue('keys');
const nextKeys = keys.concat(id++);
// can use data-binding to set
// important! notify form to detect changes
form.setFieldsValue({
keys: nextKeys,
});
},
handleSubmit(e) {
e.preventDefault();
this.form.validateFields((err, values) => {
if (!err) {
const { keys, names } = values;
console.log('Received values of form: ', values);
console.log(
'Merged values:',
keys.map(key => names[key]),
);
}
});
},
},
};
</script>
<style>
.dynamic-delete-button {
cursor: pointer;
position: relative;
top: 4px;
font-size: 24px;
color: #999;
transition: all 0.3s;
}
.dynamic-delete-button:hover {
color: #777;
}
.dynamic-delete-button[disabled] {
cursor: not-allowed;
opacity: 0.5;
}
</style>
2、重点解析
我们分析官网给出的demo,可以知道整个表单的重点构成在于:
遍历form表单中keys数组,为form-item赋予不同的key,紧接着生成a-input
所以我们也可以推测出,进行动态的动态增加、减少表单项的关键在于操作keys数组。
我们在通过v-decorator为input绑定数据源时,需要使用names[${k}],如下图所示:
我们在表单构建之前使用生命周期beforeCreate,首先使用createForm创建表单,然后为表单的keys设置初始值为空数组,并且设置keys是保留项。
在remove和add方法中主要是操作keys数组的变化,然后通过setFieldsValue设置keys的值。
例如:
form.setFieldsValue({
keys: keys.filter(key => key !== k),
});
到这里官网示例可以满足我们大部分需求,但是在使用接口数据动态的加载表单的时候,因为异步问题会存在一些问题。下面我将会为大家解答这些问题。
3、异步的动态表单
废话不多说先上代码:
<template>
<a-form
:form="form"
>
<template v-if="show">
<a-form-item
v-for="(key, index) in form.getFieldValue('keys')"
:key="key"
:label="`${index+1}WHY`"
>
<a-input
v-decorator="[
`names[${key}]`, {
}]"
/>
</a-form-item>
<a-form-item
:label="'test'"
>
<a-input
v-decorator="[
`test`, {
}]"
/>
</a-form-item>
</template>
</a-form>
</template>
</template>
<script>
export default {
data () {
return {
show: false,
};
},
beforeCreate () {
this.form = this.$form.createForm(this);
// 通过setTimeout模拟网络请求
setTimeout((res) => {
this.form.getFieldDecorator('keys', { initialValue: res.keys, preserve: true });
this.show = true;
this.$nextTick(() => {
this.form.setFieldsValue(res);
});
}, 2000, { keys: [0, 1, 2, 3], names: ['窗前明月光', '疑似地上霜', '举头望明月', '低头思故乡'], test: 'test' });
},
};
</script>
我们使用setTimeout模拟网络请求,来实现模仿异步请求生成动态表单。
其中需要注意的是:
1、我们需要先将表单设置为隐藏状态,在接口返回数据之后,设置表单为显示状态。
2、我们使用setFieldsValue设置表单的显示值时,需要使用nextTick,否则无法设置成功。
3、另外需要注意的一点时,keys数组必须从0开始。
运行结果:
注解:
1、如果你在使用的过程中遇到了,第一个input框无法赋值的时候,可以尝试单独设置第一个input框的v-decorator的names[0]改为下标非0的值。
例如:this.form.setFieldsValue({ ‘names[11]’: res[0].whyDescription });
2、如果你在生命周期中设置了names的初始值,例如:
this.form.getFieldDecorator(‘names’, { initialValue: [], preserve: false });
那么每次刷新的表单的时候都会把默认值设置成空,如果不需要此功能,请删除此行代码。
参考文档:https://www.jianshu.com/p/a22604f88f3b