zoukankan      html  css  js  c++  java
  • vue + element 动态渲染、移除表单并添加验证

    博客地址:https://ainyi.com/66

    又接到新需求了吧~~

    背景

    在一个大表单里,有可能会出现这种需求,用户可以自己操作动态添加、移除表单,更加个性化的效果。
    常见于填写个人信息、附加内容的表单

    例如:
    “工作经历”可以用户自己点击继续添加按钮,在原有的表单后面 append 多一个表单,不需要就点击右上方 X 按钮移除

    问题

    在实现之前,提出几个问题

    1. vue 怎么动态渲染或移除表单上去
    2. v-model 怎么绑定动态添加表单的 value 值
    3. 动态新增的表单如何验证
    4. 动态表单怎么填写对应的 prop

    ...
    好吧,我当时也思考了一会,最后选择数组方式,动态渲染

    代码实现讲解

    利用数组,v-for 循环方式,可以完美实现动态渲染和移除,因为操作的只有对象数组而已

    请格外注意动态添加表单的 rule 和 prop

    每个动态添加的表单都要加上 rule
    prop 需要根据对象数组下标绑定设置对应的 value(:prop="'azList[' + index + '].azName'")

    <div class="section-form" v-for="(item, index) in form.azList" :key="index"> 
      <span v-if="isShowCloseBtn" class="close" @click="deleteItem(index)">
        <i class="el-icon-close"></i>
      </span> 
      <el-form-item label="可用区名称:"
        :rules="[{ required: true, message: '可用区名称不能为空' }]"
        :prop="'azList[' + index + '].azName'"
        label-width="150px"> 
        <el-input placeholder="请输入可用区名称" v-model="item.azName" :maxlength="30"></el-input> 
      </el-form-item> 
      <el-form-item label="逻辑可用区编码:"
        :rules="[{ required: true, message: '逻辑可用区编码不能为空' }]"
        label-width="150px"
        :prop="'azList[' + index + '].logicCode'"> 
        <el-input placeholder="请输入唯一ID" v-model="item.logicCode" :maxlength="30"></el-input> 
      </el-form-item> 
      <el-form-item label="物理可用区编码:"
        :rules="[{ required: true, message: '物理可用区编码不能为空' }]"
        label-width="150px"
        :prop="'azList[' + index + '].physicCode'"> 
        <el-input placeholder="请输入唯一ID" v-model="item.physicCode" :maxlength="30"></el-input> 
      </el-form-item>
    </div>
    

    那么对应的 js 代码为

    export default {
      name: 'vouchersDetail',
      data() {
        return {
          form: {
            regionName: '',
            regionCode: '',
    	// 动态添加的对象数组
            azList: [
              {
                azName: '',
                logicCode: '',
                physicCode: ''
              }
            ]
          }
        }
      },
      computed: {
        // 至少保留一个动态表单的开关
        isShowCloseBtn() {
          return this.form['azList'].length > 1
        }
      },
      methods: {
        addItem() {
          // 点击添加表单的按钮,只需要将表单绑定的 value 作为对象 push 到对象数组
          this.form['azList'].push({
            azName: '',
            logicCode: '',
            physicCode: '',
            weight: ''
          })
        },
        deleteItem(index) {
          // 点击移除表单的按钮,根据点击的当前 index 移除对象数组的元素
          this.form['azList'].splice(index, 1)
        },
        goBack() {
          window.history.back(-1)
        }
      }
    }
    


    更新

    19号更新,分离组件方法,写法更简便,易维护,还可以将校验规则剥离出去

    根据上面的方法 利用数组,v-for 循环方式
    此次更新,关键在于,是父组件引用子组件的 template 循环
    v-for 循环数组的 item 对象传入子组件 template
    每个子组件的 form 的 :model = 传入的 item,也就不需要用到数组下标 index,每个子组件是独立的一个 form,也就是说,每个动态添加字段的校验规则可以剥离出去了

    父组件

    template 循环

    <create-region
      class="section-form"
      ref="refCreateAz"
      :infoData="item"
      :indexNum="index"
      :isShowCloseBtn="isShowCloseBtn"
      v-for="(item, index) in form.azList"
      :key="index"
      @deleteItem="deleteItem">
    </create-region>
    

    js 与原来无差,只是多了引入子组件的 component

    components: {
      CreateRegion: () => import('@/views/region/models/CreateRegion')
    }
    

    子组件

    <template>
      <el-form :model="infoData" :rules="rulesAz" label-width="150px" ref="formAz">
        <span v-if="isShowCloseBtn" class="close" @click="deleteItem">
          <i class="el-icon-close"></i>
        </span>
        <el-form-item label="可用区名称:" prop="azName" label-width="150px">
          <el-input placeholder="请输入可用区名称" v-model="infoData.azName" :maxlength="30"></el-input>
        </el-form-item>
        <el-form-item label="逻辑可用区编码:" label-width="150px" prop="logicCode">
          <el-input placeholder="请输入唯一ID" v-model="infoData.logicCode" :maxlength="30"></el-input>
        </el-form-item>
        <el-form-item label="物理可用区编码:" label-width="150px" prop="physicCode">
          <el-input placeholder="请输入唯一ID" v-model="infoData.physicCode" :maxlength="30"></el-input>
        </el-form-item>
        <el-form-item label="权重设置:" label-width="150px">
          <el-input placeholder="请设置权重" v-model="infoData.weight"></el-input>
        </el-form-item>
      </el-form>
    </template>
    
    <script>
    import { ORGION_AZLIST_RULES } from '@/views/service/rules'
    export default {
      props: {
        infoData: {
          require: true
        },
        indexNum: {
          type: Number
        },
        isShowCloseBtn: {
          type: Boolean
        }
      },
      data() {
        return {
          form: this.infoData,
          rulesAz: ORGION_AZLIST_RULES.call(this)
        }
      },
      computed: {},
      methods: {
        deleteItem() {
          this.$emit('deleteItem', this.indexNum)
        },
        validates() {
          return new Promise((resolve, reject) => {
            this.$refs['formAz'].validate(async valid => {
              if (valid) {
                // 验证通过
                resolve(true)
              } else {
                reject(false)
              }
            })
          })
        }
      }
    }
    </script>
    

    校验

    export const ORGION_AZLIST_RULES = function() {
      return {
        logicCode: [
          {
            required: true,
            message: '逻辑可用区编码不能为空',
            trigger: 'blur'
          },
          {
            validator: CHECK_AZEXITS_CODE.bind(this),
            trigger: 'blur'
          }
        ],
        physicCode: [
          {
            required: true,
            message: '物理可用区编码不能为空',
            trigger: 'blur'
          },
          {
            validator: CHECK_AZEXITS_CODE.bind(this),
            trigger: 'blur'
          }
        ],
        azName: [
          {
            required: true,
            message: '可用区名称不能为空',
            trigger: 'blur'
          }
        ]
      }
    }
    

    自定义校验

    export const CHECK_AZEXITS_CODE = async function(rule, value, callback) {
      let paramName = rule.field
      let reqData = {}
      reqData[paramName] = value
      let { result } = await getAzExist(reqData)
      if (result.result) {
        if (paramName === 'logicCode') {
          callback(new Error('逻辑可用区编码已存在,请重新输入'))
        } else {
          callback(new Error('物理可用区编码已存在,请重新输入'))
        }
      } else {
        callback()
      }
    }
    

    写在后面

    如果大家有啥更好的方法实现,欢迎在评论区相互探讨~

    写完下班、

    博客地址:https://ainyi.com/66

  • 相关阅读:
    英雄会 高校俱乐部 题解(均分01)
    win7下装ubuntu
    UVA10142/PC110108Australian Voting
    解决打不开jar包
    pc110301QWERTYU
    寒假的ACM训练三(PC110107/UVa10196)
    寒假ACM训练(二)
    寒假的ACM训练(一)
    『ORACLE』授予hr用户查看执行计划权限(11g)
    『ORACLE』SPM(下)-baseline实验(11g)
  • 原文地址:https://www.cnblogs.com/ainyi/p/10274404.html
Copyright © 2011-2022 走看看