Form表单——动态增加、减少表单项的运用


官方参考地址

业务场景:添加问卷问题,一共两类,选择题或简答题
在这里插入图片描述

一、代码示例

<!--questionData 是请求回去的数据-->
<template>
  <a-form :label-col="{ span: 5 }"
                :wrapper-col="{ span: 14 }"
                :form="form">
          <a-form-item label="序号">
            {{isEdit?questionIndex:questionData.questionList?questionData.questionList.length+1:1}}
          </a-form-item>
          <a-form-item label="类型">
            <a-select v-decorator="['currentType',{initialValue:2}]"
                      :disabled="forbidSelect">
              <a-select-option :value=1>
                选择题
              </a-select-option>
              <a-select-option :value=2>
                文本题
              </a-select-option>
            </a-select>
          </a-form-item>
          <a-form-item label="是否必填">
            <a-switch v-decorator="['currentRequired', { valuePropName: 'checked' }]" />
          </a-form-item>
          <a-form-item label="标题">
            <a-input :maxLength="51"
                     v-decorator="['title',{
              rules:[
              {required: true, message: '请输入问卷标题'},
              { validator: lengthLimit(50, '字符过长,1~50个字符'),}
              ]
            }]"></a-input>
          </a-form-item>
          <div v-if="form.getFieldValue('currentType') === 1">
            <a-form-item v-for="(k, index) in form.getFieldValue('keys')"
                         :key="k"
                         v-bind="index === 0 ? formItemLayout : formItemLayoutWithOutLabel"
                         :label="index === 0 ? '选项' : ''"
                         :required="true">
              <a-input :maxLength="21" v-decorator="[
                        `names[${k}]`,
                        {
                          validateTrigger: ['change', 'blur'],
                          rules: [
                            {
                              required: true,
                              whitespace: true,
                              message: '请输入选项名称',
                            },
                            { validator: lengthLimit(20, '字符过长,1~20个字符'),}
                          ],
                        },
                      ]"
                       placeholder="选项名称"
                       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-button type="primary"
                      @click="addOption"
                      style="margin-left:120px">选项添加</a-button>
          </div>

        </a-form>
	
	<a-button @click="handleCancel">
            取消
          </a-button>
	<a-button type="primary"
                    @click="handleSubmit()">
            确定
          </a-button>
</template>
<style lang="less" scoped>
	.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>
<script lang="ts">
import {  Vue, Watch } from 'vue-property-decorator';

export default class SurveyEdit extends Vue {
	form: any;
	title = '新增问题';
    isEdit = false;
    //新增问题的用于展示问题序号
   	questionIndex: any;
   	// 是否必填
  	currentRequired = false;
  	// 添加选项的参数
  	choiceIndex = 0;
    // 问题id
  	questionId: any;

  formItemLayout = {
    labelCol: {
      xs: { span: 24 },
      sm: { span: 5 },
    },
    wrapperCol: {
      xs: { span: 24 },
      sm: { span: 18 },
    },
  };

  formItemLayoutWithOutLabel = {
    wrapperCol: {
      xs: { span: 24, offset: 0 },
      sm: { span: 18, offset: 5 },
    },
  };

// 禁止修改类型标识  当为修改操作时,题目类型不改修改
  forbidSelect = false;

	  @Watch('isEdit')
  	 onChangeValue(newVal: boolean) {
    	this.title = newVal ? '编辑问卷' : '新增问卷';
    	 this.forbidSelect = newVal ? true : false;
  	}
  	
created() {
	//创建form
    this.form = this.$form.createForm(this, {
      name: 'dynamic_form_item',
      // 用于判断修改后,当前页面是否保存
     // onFieldsChange: (v: any, r: any) => {
       // this.unSave = true;
      //},
    });
    // 这个动态创建表单项的初始化
    this.form.getFieldDecorator('keys', { initialValue: [], preserve: true });
	//获取 数据函数,自己按具体请求添加
	this.getDetail();
    }

//点击添加问题 展开添加选项弹窗
addQuestion() {
	this.visible = true;
    // 清空已有选项值
    // 不知为何已经在弹窗关闭时,已经 this.form.resetFields();重置过数据,但还是不行,所有在打开弹窗时,又再单独重置了keys
    this.form.setFieldsValue({
      keys: [],
    });
}

// 添加选择项
addOption() {
    const { form } = this;
    const keys = form.getFieldValue('keys');
    const nextKeys = keys.concat(this.choiceIndex++);
    form.setFieldsValue({
      keys: nextKeys,
    });
  }

// 删除选择项
  remove(k: any) {
    const { form } = this;
    const keys = form.getFieldValue('keys');
    if (keys.length === 1) {
      return;
    }

    form.setFieldsValue({
      keys: keys.filter((key: any) => key !== k),
    });
  }

  // 确认添加
  async handleSubmit() {
  // 校验数据规范
    await this.form.validateFields();
    //获取表单数据
    const value = this.form.getFieldsValue();
        
    const item = {
      title: value.title,
      type: value.currentType,
      // 这是我根据后台传参要求编辑的,具体情况自行修改
      isRequired: value.currentRequired ? 1 : 0, 
    };
    // 选择题
    if (value.currentType === 1) {
      if (!value.names) {
        this.$message.error('请添加选择!');
        return;
      } else {
       // 过滤删除选项时产生的empty项
        const filterArr = value.names.filter((item: any) => item);
        if (!filterArr.length) {
          this.$message.error('请添加选择!');
          return;
        } else {
          Object.assign(item, {
            optionsList: filterArr,
          });
        }
      }
    }
	
	// 关闭modal
    this.visible = false;
    // 重置标题判断标识
    this.isEdit = false;
    //添加完后要把选项index 重新置为0
    this.choiceIndex = 0;
    // 清空表单数据
    this.form.resetFields();
    // 重新获取数据
    this.getDetail();
  }

// 取消添加
  handleCancel() {
    this.visible = false;
    this.isEdit = false;
    this.choiceIndex = 0;
    this.form.resetFields();
  }

  // 编辑问题  questionItem就是上面添加项的数据(item)
  linkToEdit(questionItem: any, index: number) {
    this.isEdit = true;
    this.visible = true;
    this.questionId = questionItem.id;	
    // 设置表单数据
       setTimeout(() => {
      this.form.setFieldsValue({
        title: questionItem.title,
        currentRequired: questionItem.isRequired ? true : false,
        currentType: questionItem.type,
      });
    }, 100);

	//有这个代表是选择题
    if (questionItem.optionsList) {
	    // 生成 选择项的 key
	    const keys = questionItem.optionsList.map(
	        (item: any, index: any) => index,
	      );
      // 动态添加项的初始化
      this.form.getFieldDecorator('keys', {
        initialValue: keys,
        preserve: true,
      });
      // 初始化添加项的index
      this.choiceIndex = questionItem.optionsList.length ;
      // 添加定时器,延时加载,不然会warning,同时选择值无法正常显示
      setTimeout(() => {
        this.form.setFieldsValue({
          keys,
          names: questionItem.optionsList,
        });
      }, 100);
      // 要展示问题的序号
      this.questionIndex = index;
    }
}
</script>

二、相关问题及解决

2.1 动态添加选择题选项时,获取的选项数组有问题,导致渲染有问题

	 	//获取表单数据
    const value = this.form.getFieldsValue();
 		// 过滤删除选项时产生的empty项
    const filterArr = value.names.filter((item: any) => item);
2.2 编辑时如何复现动态选择项数据
// 1. 生成 选择项的 key
const keys = questionItem.optionsList.map(
    (item: any, index: any) => index,
);
// 2. 动态添加项的初始化
this.form.getFieldDecorator('keys', {
    initialValue: keys,
    preserve: true,
});


// 添加定时器,延时加载,不然会warning,同时选择值无法正常显示
setTimeout(() => {
    // 3. 设置keys、names的值
    this.form.setFieldsValue({
        keys,
        names: questionItem.optionsList,
    });
}, 100);
}
// 4. 添加项的起始index
this.choiceIndex = questionItem.optionsList.length;
2.3 渲染问题

因为动态添加表单的项不像title,是确定的表单项,所以会报错

warning.js:34 Warning: You cannot set a form field before rendering a field associated with the value. You can usegetFieldDecorator(id, options)insteadv-decorator="[id, options]"to register it before render.

同时渲染也会出现问题

解决方案:
通过setTimeout延时加载解决

setTimeout(() => {
        this.form.setFieldsValue({
          keys: keys,
          names: questionItem.optionsList,
        });
      }, 100);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值