elementUI表单的【通信原理&循环使用】

1,000 阅读1分钟

elForm通信原理(required规则的校验)

  1. 点击 在父组件el-from上的validate方法,去触发子组件el-form-item上的validate方法

    • 通过this.$children拿到子组件
    • 遍历所有的子组件,依次调用其valiate方法,进行规则验证
    • 如果有一个子组件的验证不通过,就返回false(即验证失败)
  2. 在子组件上的validate验证规则的实现

    • 通过this.$parent拿到父组件form
    • 通过form.model[this.prop]拿到父组件上对应项的数据value
    • 通过form.rules[this.prop]拿到父组件上对应项的规则rule
    • 再遍历rule,查看里面是否有required==true的规则以及value不为空,如果都满足,就表示验证通过,返回true

注意:

  • 这里的value和rule都是通过this.prop来拿到,所以可以只用为子元素绑定prop属性即可

  • 如果有一个不能直接通过this.prop拿到,就需要分开写prop属性拿到对应value,rule属性来拿到对应的rule,将在下一个例子详细说明

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
  </head>
  <body>
    <div id="app">
        <el-form :model="form" :rules="rules" ref="formRef">
            <el-form-item  prop="uname">
                <input type="text" v-model="form.uname" placeholder="请填写内容"></input>
            </el-form-item>
            <div style="margin-top: 10px;">
                <button @click="submit">提交</button>
            </div>
        </el-form>
    </div>
</body>
<script>
//父组件    
Vue.component('el-form', {
    props: ['model', 'rules'],
    template: `
        <div>
            <slot></slot>
        </div>
    `,
    methods: {
        validate(callback){
            //调用子组件的valiate方法
            //1.得到子组件
            // 方式1.通过ref
            // 方式2.通过$children
            //2.循环子组件
            let isAllOk = true
            this.$children.forEach(item=>{
                let isOneOk = item.validate()
                if(!isOneOk) isAllOk = false
            })
            callback(isAllOk)
        }
    }
})
//子组件
Vue.component('el-form-item', {
    props: ["prop"],
    template: `
        <div>
            <slot></slot>
        </div>
    `,
    methods: {
        validate(){
            // console.log('子组件', this)
            //0.得到父组件
            let form = this.$parent
            console.log(form);
            //方式1-通过父子通信,子->父通过$emit
            //方式2-
            //1.得到数据
            let value = form.model[this.prop]
            //2.得到规则
            let rule = form.rules[this.prop]
            //3.做判断
            //如果存在required为true,并且有值返回true,否则false
            if(rule.some(t=>t.required==true) && value!==''){
                return true 
            }else{
                false
            }
            // return true //false
            // return false
        }
    }
})
var vm = new Vue({
    el: "#app",
    data() {
        return {
            form: {
                uname: ''
            },
            rules: {
                uname: [{required:true}]
            }
        }
    },
    methods: {
        submit(){
            this.$refs.formRef.validate(isValid=>{
                if(isValid){
                    console.log('验证通过')
                }else{
                    console.log('验证不通过')
                }
            })
        }
    }
})    
</script>
</html>

表单rule与value不一致案例

方式一:表单rule与value的数据结构保持一致

可以看到,data中form和rules对应的数据结构是一致的,所以可以只写prop属性即可

- form.unit <-----> rules.unit

- form.now.unit <-----> rules.now.unit

          // form
          form: {
            unit: '',
            now: {
              unit: ''
            }
          },
          
          // rules
          rules: {
            unit: [
              {
                required: true,
                message: '请输入工作单位',
                trigger: 'blur'
              }
            ],
            'now.unit': [
              {
                required: true,
                message: '请输入工作单位',
                trigger: 'blur'
              }
            ]
          }
  • 第一项,prop都为unit
    • 得到数据:this.form.unit
    • 得到规则:this.rules.unit
  • 第二项,prop都为now.unit
    • 得到数据: this.form.now.unit
    • 得到规则: this.rules.now.unit
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.1/index.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.1/theme-chalk/index.css" rel="stylesheet" />
    <style>
      .el-button,
      .el-date-editor.el-input,
      .el-date-editor.el-input__inner {
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <!-- 得到数据:this.form.unit -->
        <!-- 得到规则:this.rules.unit -->
        <el-form-item label="之前单位" prop="unit">
          <el-input v-model="form.unit" placeholder="请填写工作单位"></el-input>
        </el-form-item>
        <!-- 得到数据: this.form.now.unit -->
        <!-- 得到规则: this.rules.now.unit-->
        <el-form-item label="现在单位" prop="now.unit">
          <el-input v-model="form.now.unit" placeholder="请填写工作单位"></el-input>
        </el-form-item>
        <el-row style="margin-top: 10px">
          <el-button type="primary" @click="submit">提交</el-button>
        </el-row>
      </el-form>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data() {
        return {
          form: {
            unit: '',
            now: {
              unit: ''
            }
          },
          rules: {
            unit: [
              {
                required: true,
                message: '请输入工作单位',
                trigger: 'blur'
              }
            ],
            'now.unit': [
              {
                required: true,
                message: '请输入工作单位',
                trigger: 'blur'
              }
            ]
          }
        }
      },
      methods: {
        submit() {
          this.$refs.formRef.validate(isValid => {
            if (isValid) {
              this.$message.success('验证通过')
            } else {
              this.$message.error('验证不通过')
            }
          })
        }
      }
    })
  </script>
</html>

方式一:表单rule与value的数据结构不一致

可以看到,data中form和rules对应的数据结构是不一致的,两个表单项都使用了同一个规则rules.unit,第二个表单项是没有一致对应的

- form.unit <-----> rules.unit

- form.now.unit <-----> rules.unit

  • 第一项,prop都为unit
    • 得到数据:this.form.unit
    • 得到规则:this.rules.unit
  • 第二项,需要额外再绑定rules属性
    • 得到数据: this.form.now.unit-----prop='now.unit'
    • 得到规则: this.rules.unit------ :rules='rules.unit'
  ...省略其他代码
  <body>
    <div id="app">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <!-- 得到数据:this.form.unit -->
        <!-- 得到规则:this.rules.unit -->
        <el-form-item label="之前单位" prop="unit">
          <el-input v-model="form.unit" placeholder="请填写工作单位"></el-input>
        </el-form-item>
        <!-- 得到数据: this.form.now.unit -->
        <!-- 得到规则: this.rules.unit-->
        <el-form-item label="现在单位" prop="now.unit" :rules="rules.unit">
          <el-input v-model="form.now.unit" placeholder="请填写工作单位"></el-input>
        </el-form-item>
        <el-row style="margin-top: 10px">
          <el-button type="primary" @click="submit">提交</el-button>
        </el-row>
      </el-form>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data() {
        return {
          form: {
            unit: '',
            now: {
              unit: ''
            }
          },
          rules: {
            unit: [
              {
                required: true,
                message: '请输入工作单位',
                trigger: 'blur'
              }
            ]
          }
        }
      },
      methods: {
        submit() {
          this.$refs.formRef.validate(isValid => {
            if (isValid) {
              this.$message.success('验证通过')
            } else {
              this.$message.error('验证不通过')
            }
          })
        }
      }
    })
  </script>
</html>

循环表单验证

方式一:循环整个form表单【结果:多个form表单】

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.1/index.min.js"></script>
    <link
      href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.1/theme-chalk/index.css"
      rel="stylesheet"
    />
    <style>
        .el-button,.el-date-editor.el-input, .el-date-editor.el-input__inner{width: 100%;}
    </style>
  </head>
  <body>
    <div id="app">
        <el-form  label-width="100px" ref="formRef" :model="item" :rules="rules" v-for="(item,index) in form.workExperience">
            <template >
                <h4>工作经历</h4>
                <!-- 得到数据:form.workExperience[0].unit -->
                <!-- 得到规则:rules.unit -->
                <el-form-item label="工作单位" prop="unit">
                    <el-input v-model="item.unit" placeholder="请填写工作单位"></el-input>
                </el-form-item>
                <el-form-item label="开始时间" prop="undergoStartTime">
                    <el-date-picker v-model="item.undergoStartTime"  type="date" placeholder="请选择开始时间"></el-date-picker>
                </el-form-item>
                <el-form-item label="结束时间" prop="undergoEndTime">
                    <el-date-picker v-model="item.undergoEndTime"  type="date"  placeholder="请选择结束时间"></el-date-picker>
                </el-form-item>
                <el-form-item label="担任职务" prop="job">
                    <el-input v-model="item.job"  placeholder="请选择结束时间"></el-input>
                </el-form-item>
                <el-form-item label="离职原因" prop="cause">
                    <el-input v-model="item.cause"  placeholder="请填写离职原因"></el-input>
                </el-form-item>
            </template>
        </el-form>
        <el-row>
            <el-button @click="add">添加工作经历</el-button>
        </el-row>
        <el-row style="margin-top: 10px;">
            <el-button type="primary" @click="submit">提交</el-button>
        </el-row>

    </div>
</body>
<script>
var vm = new Vue({
    el: "#app",
    data() {
    return {
        form: {
			workExperience: [{
                unit: '',
                isWoke:false,
                undergoStartTime: '',
                undergoEndTime:'',
                job: '',
                cause: '',
			}]
        },
        rules: {
            unit: [{
                required: true,
                message: '请输入工作单位',
                trigger: 'blur',
            }],
            undergoStartTime: [{
                required: true,
                message: '请选择起止时间',
                trigger: 'blur',
            }],
            undergoEndTime: [{
                required: true,
                message: '请选择结束时间',
                trigger: 'blur',
            }],
            job: [{
                required: true,
                message: '请输入担任职务',
                trigger: 'blur',
            }],
            cause: [{
                required: true,
                message: '请选输入离职原因',
                trigger: 'blur',
            }]
        },
    }
    },
    methods: {
        add(){
            this.form.workExperience.push({
                unit: '',
                isWoke:false,
                undergoStartTime: '',
                undergoEndTime:'',
                job: '',
                cause: '',
			})
        },
        async submit(){
            //1.通过回调函数
            /*
            let isAllOk = true
            this.$refs.formRef.forEach(t=>{
                t.validate((isOk)=>{
                    if(!isOk) isAllOk = false
                })
            })
            console.log('isAllOk', isAllOk)
            */ 
           //2.通过Promise.all
            /*
            let l = []
            this.$refs.formRef.forEach(t=>{
                let p = t.validate()
                l.push(p)
            })
            Promise.all(l).then(()=>{
                console.log('全部成功')
            }).catch(()=>{
                console.log('存在失败')
            })
            */
            //2.2通过map优化代码
            /*
            let l = this.$refs.formRef.map(t=> t.validate())
            Promise.all(l).then(()=>{
                console.log('全部成功')
            }).catch(()=>{
                console.log('存在失败')
            })
            */
            //2.3通过await简化promise接受
            /*
            let l = this.$refs.formRef.map(t=> t.validate())
            try{
                await Promise.all(l)
                console.log('全部成功')
            }catch(err){
                console.log('存在失败')
            }
            */
           // 2.4取消中间变量
            try{
                await Promise.all(this.$refs.formRef.map(t=> t.validate()))
                console.log('全部成功')
            }catch(err){
                console.log('存在失败')
            }
        }
    }
})    
</script>
</html>

方式二:只循环表单项 【结果:一个form表单】

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.1/index.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.1/theme-chalk/index.css" rel="stylesheet" />
    <style>
      .el-button,
      .el-date-editor.el-input,
      .el-date-editor.el-input__inner {
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <el-form label-width="100px" ref="formRef" :model="form" :rules="rules">
        <div v-for="(workExperience,index) in form.workExperience" :key="index">
          <h4>工作经历</h4>
          <!-- 拿到的value:form.workExperience[index].unit -->
          <!-- 拿到的rules:rules.unit -->
          <el-form-item label="工作单位" :prop="'workExperience.' + index + '.unit'" :rules="rules.unit">
            <el-input placeholder="请填写工作单位" v-model="workExperience.unit"></el-input>
          </el-form-item>
          <el-form-item label="担任职务" :prop="'workExperience.'+index+'.job'" :rules="rules.job">
            <el-input placeholder="请选择担任职务" v-model="workExperience.job"></el-input>
          </el-form-item>
        </div>
      </el-form>
      <el-row>
        <el-button @click="add">添加工作经历</el-button>
      </el-row>
      <el-row style="margin-top: 10px">
        <el-button type="primary" @click="submit">提交</el-button>
      </el-row>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data() {
        return {
          form: {
            workExperience: [
              {
                unit: '',
                isWoke: false,
                job: ''
              }
            ]
          },
          rules: {
            unit: [
              {
                required: true,
                message: '请输入工作单位',
                trigger: 'blur'
              }
            ],

            job: [
              {
                required: true,
                message: '请输入担任职务',
                trigger: 'blur'
              }
            ]
          }
        }
      },
      methods: {
        add() {
          this.form.workExperience.push({
            unit: '',
            isWoke: false,
            undergoStartTime: '',
            undergoEndTime: '',
            job: '',
            cause: ''
          })
        },
        async submit() {
          this.$refs.formRef.validate(isOK => {
            if (isOK) {
              alert('submit!')
            } else {
              console.log('error submit!!')
              return false
            }
          })
        }
      }
    })
  </script>
</html>