父组件中修改子组件属性时的问题
父组件中使用this.$refs.ref属性值方式修改子组件属性值时或者调用子组件方法时, 如果在父组件中使用子组件的时候加上v-if指令之后直接修改子组件属性就会报错:
父组件:
<template>
<el-card class="box-card">
<!-- 查询条件-->
<el-row :gutter="20">
<el-col :span="6">
<el-input placeholder="性别" v-model="gender"></el-input>
</el-col>
<el-col :span="6">
<el-input placeholder="姓名" v-model="name"></el-input>
</el-col>
<el-col :span="6">
<el-button type="primary" icon="el-icon-search" @click="findStudent(1)">查询</el-button>
</el-col>
</el-row>
<el-button type="primary" round @click="addStudent()">增加</el-button>
<el-table :data="tableData" :border = "true" style="width: 100%">
<el-table-column prop="name" label="姓名" width="180">
</el-table-column>
<el-table-column prop="gender" label="性别" width="180">
</el-table-column>
<el-table-column prop="phone" label="电话" width = "180">
</el-table-column>
<el-table-column prop="nianj" label="年级" width = "180">
</el-table-column>
<el-table-column prop="account" label="管理员" width = "180">
</el-table-column>
<el-table-column prop="operTime" label="操作时间" width = "180" :formatter = "formatterDate">
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button size="mini" @click="updateStudent(scope.row.id)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteStudent(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<add v-if = "red" ref = "add"></add>
<update ref = "update"></update>
</el-card>
</template>
<script>
import Add from "./add.vue";
import Update from "./update.vue";
export default {
data() {
return {
tableData: [],
gender : "",
name : "",
visable : false,
visablep : false,
red : false
}
},
methods: {
formatterDate(row, column, cellValue, index){
var date = new Date(cellValue);
return date.toLocaleString();
},
//就是在这里修改的子组件属性
updateStudent(id){
this.$refs.update.dialogFormVisible = true;
this.$refs.update.seleById(id);
},
deleteStudent(id){
alert(id)
},
findStudent(flag){
var path = "http://localhost:8080/webback/back/stuservlet?mark=list";
if(flag == 1){
path += "&gender="+this.gender+"&name="+this.name;
}
this.$http.get(path).then((resp) => {
this.tableData = resp.data;
})
},
//这里也修改了子组件属性
addStudent(){
this.$refs.add.dialogFormVisible = true;
}
},
mounted(){
//向后端发送请求获取数据
this.findStudent(0)
},
components : {
Add,
Update,
}
}
</script>
<style>
</style>
子组件:
<template>
<!-- 这里写一个弹窗, 弹窗中填写我们要提交的信息 -->
<div>
<el-dialog title="增加" :visible.sync="dialogFormVisible">
<el-form :model="form">
<el-form-item label="姓名" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="性别" :label-width="formLabelWidth">
<el-input v-model="form.gender" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="生日" :label-width="formLabelWidth">
<el-date-picker v-model="form.birthday" type="date" placeholder="选择日期" value-format="yyyy-MM-dd">
</el-date-picker>
</el-form-item>
<el-form-item label="电话" :label-width="formLabelWidth">
<el-input v-model="form.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="年级" :label-width="formLabelWidth">
<el-select v-model="form.nianj" placeholder="请选择年级">
<el-option v-for="(item,i) in nianJList" :key="i" :label="item.nianJ" :value="item.nianJ">
</el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="addStudent()">确认添加</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "add",
data() {
return {
dialogFormVisible: false,
form: {
name: '',
gender: '',
birthday: "",
phone: "",
nianj: "",
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: '',
value1: ""
},
nianJList: [],
formLabelWidth: '120px'
};
},
methods: {
addStudent() {
//这个时候我们直接将这个请求发送到后端服务器中, 然后根据传输的数据在后端中建立sql语句之后进行添加操作
//这个时候我们一定要注意: 这个时候传输的时候我们同样要传输过去一个mark标记值为add
this.dialogFormVisible = false;
var account = window.sessionStorage.getItem("account");
var operTime = new Date().getFullYear() + "-" + new Date().getMonth() + "-" + new Date().getDate();
this.$http.get("http://localhost:8080/webback/back/stuservlet?mark=add&name=" + this.form.name +
"&gender=" + this.form.gender + "&birthday=" + this.form.birthday + "&phone=" + this.form.phone +
"&nianj=" + this.form.nianj + "&account=" + account + "&operTime=" + operTime).then((resp) => {
//弹出一个添加成功或者添加失败
if (resp.data == 200) {
this.$message("添加成功")
this.$router.go();
} else if (resp.data == 500) {
this.$message("添加失败")
}
})
},
},
mounted() {
this.$http.get("http://localhost:8080/webback/back/stuservlet?mark=seleNianJList").then((resp) => {
this.nianJList = resp.data;
})
}
};
</script>
<style>
</style>
报错:Error in v-on handler: "TypeError: Cannot set properties of undefined (setting ‘dialogFormVisible’
解决方法一: 删去v-if指令之后就没有问题了
解决方法二: 我们可以将父组件中修改子组件的操作放到nextTick()的回调函数中执行
我们给出修改后的父组件:
<template>
<el-card class="box-card">
<!-- 查询条件-->
<el-row :gutter="20">
<el-col :span="6">
<el-input placeholder="性别" v-model="gender"></el-input>
</el-col>
<el-col :span="6">
<el-input placeholder="姓名" v-model="name"></el-input>
</el-col>
<el-col :span="6">
<el-button type="primary" icon="el-icon-search" @click="findStudent(1)">查询</el-button>
</el-col>
</el-row>
<el-button type="primary" round @click="addStudent()">增加</el-button>
<el-table :data="tableData" :border = "true" style="width: 100%">
<el-table-column prop="name" label="姓名" width="180">
</el-table-column>
<el-table-column prop="gender" label="性别" width="180">
</el-table-column>
<el-table-column prop="phone" label="电话" width = "180">
</el-table-column>
<el-table-column prop="nianj" label="年级" width = "180">
</el-table-column>
<el-table-column prop="account" label="管理员" width = "180">
</el-table-column>
<el-table-column prop="operTime" label="操作时间" width = "180" :formatter = "formatterDate">
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button size="mini" @click="updateStudent(scope.row.id)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteStudent(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<add ref = "add"></add>
<update ref = "update"></update>
</el-card>
</template>
<script>
import Add from "./add.vue";
import Update from "./update.vue";
export default {
data() {
return {
tableData: [],
gender : "",
name : "",
visable : false,
visablep : false,
}
},
methods: {
formatterDate(row, column, cellValue, index){
var date = new Date(cellValue);
return date.toLocaleString();
},
updateStudent(id){
this.$nextTick(() => {
this.$refs.update.dialogFormVisible = true;
this.$refs.update.seleById(id);
})
},
deleteStudent(id){
alert(id)
},
findStudent(flag){
var path = "http://localhost:8080/webback/back/stuservlet?mark=list";
if(flag == 1){
path += "&gender="+this.gender+"&name="+this.name;
}
this.$http.get(path).then((resp) => {
this.tableData = resp.data;
})
},
addStudent(){
//这里设置的是下一次更新, 所以可能视图与数据不符合就是这个原因
this.$nextTick(() => {
this.$refs.add.dialogFormVisible = true;
});
}
},
mounted(){
//向后端发送请求获取数据
this.findStudent(0)
},
components : {
Add,
Update,
}
}
</script>
<style>
</style>
那么为什么我们使用了v-if之后我们就会报出找不到对应的组件的属性和方法的错误?
其实就是因为我们的v-if就是满足条件的时候才渲染除了我们的DOM元素, 但是我们的DOM元素的更新是要在事件队列执行完之后才更新的, 这个时候我们的v-if渲染和我们的获取子组件中的对应属性的操作在同一个方法中, 也就是在同一个事件中, 这个时候执行到修改子组件中对应属性的时候其实事件队列还没有清空, 这个时候对应的子组件DOM元素还没有更新, 这个时候我们获取子组件对应DOM元素中的属性进行修改的时候肯定就是报错,显示的就是找不到该属性, 其实就是此时DOM元素还没有渲染出来, 我们其实是可以做一个测试的: 我们使用this. r e f [ " 子组件引用时 r e f 属性值 " ] 的方式获取到子组件对应的 D O M 元素之后进行一个打印 , 这个时候打印出的结果就应该是一个 u n d e f i n e d − − − > ∗ ∗ 所以我们的解决办法就是将我们的获取子组件对应 D O M 元素的操作放到我们的 t h i s . ref["子组件引用时ref属性值"]的方式获取到子组件对应的DOM元素之后进行一个打印, 这个时候打印出的结果就应该是一个undefined ---> **所以我们的解决办法就是将我们的获取子组件对应DOM元素的操作放到我们的this. ref["子组件引用时ref属性值"]的方式获取到子组件对应的DOM元素之后进行一个打印,这个时候打印出的结果就应该是一个undefined−−−>∗∗所以我们的解决办法就是将我们的获取子组件对应DOM元素的操作放到我们的this.nextTick()中的回调中来, this.$nextTick()中的回调是会在DOM元素更新之后自动调用的, 这个时候我们DOM更新之后去获取对应的DOM元素就是正确的, 那么我们修改对应的属性获取调用对应的方法肯定就是正确的**