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

Logo

前往低代码交流专区

更多推荐