vue + element 動態渲染、移除表單並添加驗證

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

又接到新需求了吧~~vue

背景

在一個大表單裏,有可能會出現這種需求,用戶能夠本身操做動態添加、移除表單,更加個性化的效果。
常見於填寫我的信息、附加內容的表單數組

例如:
「工做經歷」能夠用戶本身點擊繼續添加按鈕,在原有的表單後面 append 多一個表單,不須要就點擊右上方 X 按鈕移除app

問題

在實現以前,提出幾個問題async

  1. vue 怎麼動態渲染或移除表單上去
  2. v-model 怎麼綁定動態添加表單的 value 值
  3. 動態新增的表單如何驗證
  4. 動態表單怎麼填寫對應的 prop

...
好吧,我當時也思考了一會,最後選擇數組方式,動態渲染ui

代碼實現講解

利用數組,v-for 循環方式,能夠完美實現動態渲染和移除,由於操做的只有對象數組而已this

請格外注意動態添加表單的 rule 和 prop編碼

每一個動態添加的表單都要加上 rule
prop 須要根據對象數組下標綁定設置對應的 value(:prop="'azList[' + index + '].azName'")spa

<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 代碼爲code

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

相關文章
相關標籤/搜索