(三)vue父子组件通信(一)

当前端某个页面代码过多的时候,我们可以把代码拆分,做为自定义组件来引用,这样不至于代码过于繁杂,清晰易懂。

一、语法:

把拆分的代码单独放到某个vue文件中做为子组件,这个子组件中就是普通的vue代码,可以放在components目录下,也可以放在views目录下(放在views下,不需要在router中声明路径),还可以放在其他自定义目录中。父组件调用子组件的步骤:

1、使用import引入子组件并在components中声明这个子组件:

<script>
//your-child-file-name为子组件文件名,YourChildFileName为别名(可以和文件名一样)
import YourChildFileName from './children/your-child-file-name'; 

export default {
 components: {
   YourChildFileName,
   ......  
  }
}
</script>

2、使用定义的子组件做为标签使用:

<template>
  <div>
    <YourChildFileName></YourChildFileName>
</div>
</template>

二、父子组件通信:

1、父组件传值给子组件:(1)父组件使用v-bind传递,值可加引号也可以不加,如 v-bind:formDataList="formDataList"和 v-bind:formDataList=formDataList都可以,也可以直接去掉v-bind,表示使用this.可以拿到的数据,如果是直接定义的字符串等常量则不需要使用前面的冒号,如这里的idColumn、nameColumn;

 <SelectItem
            v-for="(item, index) in userArea.cityIds"
            :key="index"
            :id="item"
            :dataList="cityList"
            idColumn="cityId"
            nameColumn="cityName"
            @deleteId="deleteId"
          >
          </SelectItem>

(2)子组件接收:使用props接收父组件的传值,使用时相当于data里面的数据,可以使用this.来获取。如:

 父组件:把formDataList传给子组件:

 <flow-node v-for="(item, index) in nodeData" :key="index" :index="index" :node="item" 
                    v-bind:formDataList="formDataList"
                    @draggableEvent="updateDraggable">

                    </flow-node>


<script>
import updateNode from '../../components/flow/updateNode'
export default {

data() {
    return {
      formDataList: []
  }
},

 components: {
   'flow-node': updateNode
  }
}
</script>

  子组件:updateNode.vue:

 export default {
    name: 'updateNode',
    data() {
      return {
       
      }
    },
    props: {
      
      formDataList: {
        required: true,
        type: Array
      }
    },
   
   
    }
  }

2、子组件调用父组件的方法:实现子数据传输给父,子组件通过emit传递调用父组件函数,其中,父组件函数名后不要加()。如:  子:

 <el-col :span="10">
            <el-select v-model="dataType" @change="onMappingChange">
                <el-option v-for="(item, index) in dataTypes" :label="item" :value="item" :key="index"></el-option>
            </el-select>
        </el-col>


 methods: {
      onMappingChange() {
        var _this = this
        this.$emit('mappingChange', {
          index: _this.itemIndex,
          action: 'change',
          mapping: {
            name: _this.name,
            dataType: _this.dataType
          }
        })
      },
      onDeleteMapping() {
        var _this = this
        this.$emit('mappingChange', {
          index: _this.itemIndex,
          action: 'delete'
        })
      }
}

  父:在调用处声明方法:

  <vue-mapping v-for="(item, index) in fieldMetas" :key="index" :item-index="index" :item-mapping="item" @mappingChange="fieldMetaChange"></vue-mapping>
        

fieldMetaChange(val) {
      if (val.action === 'change') {
        this.fieldMetas.splice(val.index, 1, val.mapping)
      } else if (val.action === 'delete') {
        this.fieldMetas.splice(val.index, 1)
      }
    },

3、父组件调用子组件的方法:使用$refs。如:  子:

<!-- @format -->
<template>
</template>
<script>
export default {
  methods: {
   
    loadData() {
      alert("我是子组件的方法");
    }
  },
};
</script>

父:

<el-table  name="0" label="用户">
        <PersonIn ref="personIn"></PersonIn>
 </el-table>

<script>
import PersonIn from './children/person-in';
methods:{
    searchHandler() {
      if (this.activeName === '0') {
        this.$refs.personIn.loadData();
      } 
}
</script>

三、父组件循环调用子组件:

v-for有两种写法,写在外面和里面都可以,如以下两种写法效果是一样的:

  <div  v-for="(item, index) in mongoFieldMetas " :key="index"> <mongo-vue-mapping
      :item-index="index"
      :item-mapping="item"
      @mappingChange="mongoFieldMetaChange"
    ></mongo-vue-mapping></div>
<mongo-vue-mapping
      v-for="(item, index) in mongoFieldMetas"
      :key="index"
      :item-index="index"
      :item-mapping="item"
      @mappingChange="mongoFieldMetaChange"
    ></mongo-vue-mapping>

综合举例:

1、例1:子组件为一个下拉框公用组件,接收父组件传递的下拉框列表、输入框初始值,并把改变后的值返回给父组件。

(1)、router:

{
    path: '/parent',
    redirct: '/test/parent',
    name: 'test',
    component: Layout,
    meta: { title: 'parent', authority: ['role_manage'] },
    noDropdown: true,
    children: [
      {
        path: 'parent',
        name: 'parent',
        component: () => import('@/views/test/parent'),
        meta: { title: 'parent', authority: ['role_manage'], keepAlive: false }
      }
     
    ]
  }

(2)、parent.vue:

<template>
   <div>
       <span> 我是parent,select:{{selectValue}},input:{{name}}</span>
       <select-child v-bind:list = list v-bind:name=name  @slectChange = "parentGetSelectValues"
       @changeInput = 'parentGetInput'></select-child>
   </div>
</template>
<script>
import child from './child'
export default {
    data () {
        return {
            list:['c1','c2','c3','c4','c5','c6'],
            selectValue:'',
            name:'parent'
        }
    },
    methods: {
        parentGetSelectValues(val){
            this.selectValue = val.data;
            alert('parent'+this.selectValue)
        },
        parentGetInput(val){
            this.name = val.data
            
        }
    },
    components: {
        'select-child':child
    }
}
</script>
<style scoped>

</style>

 (3)、组件child.vue:

<template>
  <div>
    <span>我是child</span>
    <el-input v-model="name" @blur="inputChange()"></el-input>
    <el-select v-model="val" placeholder="请选择" @change="childChange()">
      <el-option v-for="item in list" :key="item" :label="item" :value="item"></el-option>
    </el-select>
  </div>
</template>
<script>
export default {
  data() {
    return {
      val: ""
    };
  },
  props: {
    list: {
      required: true,
      type: Array
    },
    name: {
      required: true,
      type: String
    }
  },
  methods: {
    childChange() {
      alert("child" + this.val);
      this.$emit("slectChange", {
        data: this.val
      });
    },
    inputChange(){
        
        this.$emit("changeInput", {
        data:this.name
      });
    }
  }
};
</script>
<style scoped>
</style>

访问http://localhost:9528/#/parent/parent

选择一个值,弹框两次 ,

2、例2:动态新增映射:如页面初始化有两个值,可以点击+号新增项,也能点击-号删除项:

(1)index.vue:为父页面:

<template>
  <div>
    <div class="btn-add-mapping">
      <el-button @click="addMongoMapping">+</el-button>
    </div>
    <mongo-vue-mapping
      v-for="(item, index) in mongoFieldMetas"
      :key="index"
      :item-index="index"
      :item-mapping="item"
      @mappingChange="mongoFieldMetaChange"
    ></mongo-vue-mapping>
    <div><el-button @click="submit()" type="primary">提交</el-button></div>
  </div>

</template>

<script>
import MongoMapping from "../../components/common/mongomapping";
export default {
  data () {
    return {
      mongoFieldMetas:[{"name":"初始值name1","path":"path1","type":"String"},{"name":"初始值name2","path":"path2","type":"Date"}]
    }
  },
  components: {
     'mongo-vue-mapping': MongoMapping,
  },
  methods: {
    //新增
     addMongoMapping() {
      this.mongoFieldMetas.push({})
    },
    //
     mongoFieldMetaChange(val) {
      if (val.action === 'change') {
        this.mongoFieldMetas.splice(val.index, 1, val.mapping)
      } else if (val.action === 'delete') {
        this.mongoFieldMetas.splice(val.index, 1)
      }
    },
    //提交
    submit(){
alert(JSON.stringify(this.mongoFieldMetas))
    }
  }
};
</script>

<style scoped>
</style>

 (2)mongomapping.vue为子组件:

<template>
    <el-row>
        <el-col :span="7">
            <el-input v-model="name" @change="onMappingChange" placeholder="字段名"></el-input>
        </el-col>
         <el-col :span="7">
            <el-input v-model="path" @change="onMappingChange"  placeholder="path"></el-input>
        </el-col>
        <el-col :span="7">
            <el-select v-model="type" @change="onMappingChange">
                <el-option v-for="(item, index) in dataTypes" :label="item" :value="item" :key="index"></el-option>
            </el-select>
        </el-col>
        <el-col :span="2">
            <el-button @click="onDeleteMapping">x</el-button>
        </el-col>
    </el-row>
</template>

<script>
  export default {
    name: 'mongomapping',
    data() {
      return {
        dataTypes: [
          'Integer', 'String', 'Boolean', 'Date'
        ],
        name: '',
        path: '',
        type: ''
      }
    },
    props: {
      itemIndex: {
        required: true,
        type: Number
      },
      itemMapping: {
        required: true,
        type: Object
      }
    },
    created() {
      this.name = this.itemMapping.name || ''
      this.path = this.itemMapping.path || ''
      this.type = this.itemMapping.type || ''
    },
    watch: {
      itemMapping() {
        this.name = this.itemMapping.name || ''
        this.path = this.itemMapping.path || ''
        this.type = this.itemMapping.type || ''
      }
    },
    methods: {
      onMappingChange() {
        var _this = this
        this.$emit('mappingChange', {
          index: _this.itemIndex,
          action: 'change',
          mapping: {
            name: _this.name,
            path: _this.path,
            type: _this.type
          }
        })
      },
      onDeleteMapping() {
        var _this = this
        this.$emit('mappingChange', {
          index: _this.itemIndex,
          action: 'delete'
        })
      }
    }
  }
</script>

<style scoped>

</style>

再如:

<template>
  <div>
    <!--用户区-->
    <div v-for="(item,index) in users" :key="index">
      <addUser :index="index" :user="item" @userChange="updateUser"></addUser>
    </div>
    <el-button @click="addUser()">+</el-button>
    <div>
      <el-button @click="submit()">提交</el-button>
    </div>
  </div>
</template>
<script>
import addUser from "../../components/common/addUser";
export default {
  data() {
    return {
      users: [{ name: "张三", age: 19 }]
    };
  },
  components: { addUser: addUser },
  methods: {
    addUser() {
      this.users.push({});
    },
    updateUser(param) {
  
      var index = param.index;
      var user = param.user;
      if (param.type === 'update') {
         
        this.users.splice( index,1, user);
      } else if (param.type === 'delete') {
     
        this.users.splice( index,1);
      }
    },
    submit() {
      alert(JSON.stringify(this.users));
    }
  }
};
</script>
<style scoped>
</style>
<template>
  <div>
    <el-row>
      <el-col :span="6">
        <el-input placeholder="姓名" v-model="user.name" @change="change('update')"></el-input>
      </el-col>
      <el-col :span="6">
        <el-input placeholder="年龄" v-model="user.age" @change="change('update')"></el-input>
      </el-col>
      <el-col :span="2">
        <el-button @click="change('delete')">-</el-button>
      </el-col>
    </el-row>
  </div>
</template>

<script>
export default {
  props: {
    index: { required: true, type: Number },
    user: { required: true, type: Object }
  },

  methods: {
    change(val) {
   
      let param = {};
      param.type = val;
      param.index = this.index;
      param.user = this.user;
     
      this.$emit("userChange", param);
    }
  }
};
</script>

<style scoped>
</style>

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_t_y_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值