父组件中修改子组件属性时的问题 [前端][Vue框架]

父组件中修改子组件属性时的问题

父组件中使用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元素就是正确的, 那么我们修改对应的属性获取调用对应的方法肯定就是正确的**

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue是一个非常流行的前端框架组件化开发是Vue的核心特点之一。在Vue组件之间的传值是非常常见的操作。对于组件如何获取组件动态变化的值的问题,我们可以通过以下几种方式实现。 1. 通过props传递数据 在Vue里面,我们可以通过props选项将数据从组件传递到组件。当组件的数据改变,就可以通过传递props的方式将更新后的值传递给组件。 例如组件: ``` <template> <div> <child :msg="message" /> </div> </template> <script> import child from './child.vue' export default { components: { child }, data () { return { message: 'Hello World!' } }, mounted () { this.timer = setInterval(() => { this.message = new Date().toLocaleTimeString() }, 1000) }, destroyed () { clearInterval(this.timer) } } </script> ``` 组件: ``` <template> <div>{{ msg }}</div> </template> <script> export default { props: { msg: { type: String, default: '' } } } </script> ``` 在这个例组件的数据message会在mounted钩函数里不断更新,然后通过props传递给组件child。组件child根据props的数据msg来更新视图。 2. 通过$parent和$emit方法 在Vue的实例,有一个特殊的属性$parent,它指向当前组件实例。我们可以通过$parent访问组件的数据。 同,我们也可以通过组件的$emit方法来触发组件的事件,即组件接收到事件并做出响应。 例如,组件: ``` <template> <div> <child @on-child-msg="handleChildMsg" /> <div>{{ message }}</div> </div> </template> <script> import child from './child.vue' export default { components: { child }, data () { return { message: '' } }, methods: { handleChildMsg (msg) { this.message = msg } } } </script> ``` 组件: ``` <template> <button @click="sendMsg">发送消息</button> </template> <script> export default { methods: { sendMsg () { this.$emit('on-child-msg', 'Hello Parent!') } } } </script> ``` 在这个例组件child通过$emit方法触发了一个名为'on-child-msg'的事件,并传递了一个字符串'Hello Parent!'。组件定义了一个名为handleChildMsg的方法来处理这个事件,并更新了自己的数据message。 3. 通过$attrs和$listeners选项 在Vue,可以通过$attrs选项传递组件的所有属性组件,而$listeners选项可以监听组件的所有事件并响应之。 例如,组件: ``` <template> <div> <child v-bind="$attrs" v-on="$listeners" /> <div>{{ message }}</div> </div> </template> <script> import child from './child.vue' export default { components: { child }, data () { return { message: '' } }, methods: { handleChildMsg (msg) { this.message = msg } } } </script> ``` 组件: ``` <template> <button @click="$emit('on-child-msg', 'Hello Parent!')">发送消息</button> </template> <script> export default { methods: { sendMsg () { this.$emit('on-child-msg', 'Hello Parent!') } } } </script> ``` 在这个例组件child并没有使用props接收组件的数据或事件。而是直接通过$emit方法触发了事件'on-child-msg'。组件直接通过$attrs和$listeners选项将所有的属性和事件传递给组件,从而实现了组件组件的数据交互。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值