作为一名刚接触vue不到一个月的菜鸟,思想还没有从操作DOM转变为数据驱动,看vue的代码处处别扭。组里为了让我熟悉vue交给了我一个将element 表单封装成组件的练手任务。由于开发过程中遇到的表单需求千奇百怪,我们不能直接将表单封装成一个组件。所以我尝试把输入框,下拉菜单,滑块,时间选择器,单选,多选等功能各封一个组件(感觉很蠢),但这毕竟是练手任务嘛,最后开发时也不会用我的这个。在封装的过程中遇到了很多问题和疑惑,以下记录我的收获与尚未解决的问题。

1 <template>
 2   <el-form :model="ruleForm" ref="ruleForm" label-width="100px"  class="demo-ruleForm">
 3       <commonformtext
 4       prop="biao"
 5       placeholder="这个是测试的"
 6       label="活动区域"
 7       v-model="ruleForm.biao"
 8       :rules="[{ required: true, message: '请输入活动名称', trigger: 'blur' }]"
 9       >
10       </commonformtext>
11       <commonformselect
12       prop="select"
13       placeholder="这个是测试的下拉框"
14       label="下拉框"
15       v-model="ruleForm.select"
16       :rules="{ required: true, message: '请选择活动区域', trigger: 'change' }"
17       :selectdata='selectdata'
18       >
19       </commonformselect> 
20       <el-form-item>
21         <el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button>
22         <el-button @click="resetForm('ruleForm')">重置</el-button>
23       </el-form-item>
24 </el-form>
25 </template>
26 <script> 
27 import commonformtext from "@/components/common/formtext.vue";
28 import commonformselect from "@/components/common/formselect.vue";
29   export default {
30     data() {
31       return {
32         ruleForm: {
33           biao:"",
34           select:""
35         },
36         selectdata:[
37           {lable:"区域1",value:"1"},
38           {lable:"区域2",value:"2"},
39           {lable:"区域3",value:"3"},
40           {lable:"区域4",value:"4"},
41           {lable:"区域5",value:"5"}
42         ]
43       };
44     },
45     components:{
46         commonformtext,
47         commonformselect,
48     },
49     methods: {
50       submitForm(formName) {
51         this.$refs[formName].validate((valid) => {
52           if (valid) {
53             alert('submit!');
54           } else {
55             console.log('error submit!!');
56             return false;
57           }
58         });
59       },
60       resetForm(formName) {
61         this.$refs[formName].resetFields();
62       }
63     }
64   }
65 </script>

以上是父组件,本篇先传输入框,下拉菜单两个子组件

/*
* @property { rules :  {String Object} 表单验证 一种验证传对象 一种以上把对象组成数组 }
* @property { prop :  {String} input的name 传回的字段名与html中input标签name值一样。 }
* @property { placeholder :  {String} 提示语,与html中input标签placeholder值一样。 }
* @property { label :  {String} 标签文本。 }
* @property { v-model :  {String} 语法糖,利用value接值父子组件相互传值 当前表单填写的内容  }
* @version 1.0.0
* @edit: 2018/7/30
*/
<template>
  <el-form-item :label="label" :prop="prop" :rules="rules">
    <el-input v-model="myValue" :placeholder="placeholder" name="biao"></el-input>
  </el-form-item>
</template>
<script>
    export default {
        props: {
            prop: {
                type: String
            },
            placeholder:{
                type: String
            },
            label:{
                type: String
            },
            value:{
                type: String
            },
            rules:[Object,Array]
        },
    data() {
        return {
            activeIndex: '',
            menuIndex: 0,
            myValue:""
        };
    },
    mounted(){
        this.myValue = this.value;
    },
    watch:{
        myValue(val){
            this.$emit("input",val)
        }
    }
    }
</script>

上面的是输入框子组件

/*
* @property { rules :  {String Object} 表单验证 一种验证传对象 一种以上把对象组成数组 }
* @property { prop :  {String} input的name 传回的字段名与html中input标签name值一样。 }
* @property { placeholder :  {String} 提示语,与html中input标签placeholder值一样。 }
* @property { label :  {String} 标签文本。 }
* @property { v-model :  {String} 语法糖,利用value接值父子组件相互传值 当前表单填写的内容  }
* @property { selectdata:  {Array} option中的数据 }
* @version 1.0.0
* @edit: 2018/7/30
*/
<template>
    <el-form-item :label="label" :prop="prop" :rules="rules">
    <el-select v-model="myValue" :placeholder="placeholder" v-on:change="change">
       <el-option v-for="item in selectdata" :key="item.id" :label="item.lable" :value="item.value"></el-option>
    </el-select>
  </el-form-item>
</template>
<script>
    export default {
        props: {
            selectdata: {
                type: Array
            },
            prop: {
                type: String
            },
            placeholder:{
                type: String
            },
            label:{
                type: String
            },
            value:{
                type: String
            },
            rules:[Object,Array]
        },
    data() {
        return {
            myValue:""
        };
    },
    mounted(){
        this.myValue = this.value;
    },
    methods:{
        change(){
            this.$emit("input",this.myValue)
        }
    }
    }
</script>

这个是下拉框子组件

首先是一个语法糖的知识点,父组件属性中加入v-model子组件需要在props中使用value来接值,然后子组件中用  this.$emit("input",val) 来把值返回父组件,这样父组件中就可以获得当前输入框的内容了。

在输入框子组件中我们采用的是监听输入框中的值的变化,每当输入的值变化我们都会 this.$emit("input",val)  将值返回父组件,但是在下拉栏中我们同样用此方法监听的时候会出现bug

ant design vue 表单二次封装 vue封装表单组件_输入框

 

我们明明选择了区域2表单检测却给我们报了错,然后当我们再次选择的时候就好使了

此时我们做一个假设,表单检测值事件运行时我们还没有将已改变的值发给父组件,所以在父组件进行检测的时候没有值,那我们不用watch改用methods来传值会不会变快呢,我们给select绑上change事件?

接下来我们来验证它

methods:{
        change(){
            console.log(this.myValue,"aaa")
        }
    },
    watch:{
        myValue(val){
             console.log(this.myValue,"bbb")
            this.$emit("input",val)
        }
    }

我们在子组件中加入这两句话,来比一比监听值的变化快还是change事件快

ant design vue 表单二次封装 vue封装表单组件_input标签_02

先打印出aaa再打印出bbb,所以change事件比较快,那我们可以试试用change将值传给父组件,看看表单检测的时候 this.$emit("input",val) 有没有运行过。

结果change事件确实在表单检测之前将值塞了进去,bug解决了。