问题描述
我有一个子组件,它从父组件接收道具。单击按钮后,我添加了一个事件以更新父组件中的prop。但是子组件无法检测到道具更改。
近似代码:
父组件:
<template>
<table>
<Student v-for="(student,index) in students"
:key="index"
:student="student"
:attendance="attendance[student.id]"
@attended-clicked="changeAttendance"
></student>
</table>
</template>
<script>
export default {
data() {
return {
students: [
{id: 1,name: 'Pepito'},{id: 2,name: 'Sue'},],attendance: {
1: {attended: true},2: {attended: false},}
}
},methods: {
changeAttendance(studentId) {
this.attendance[studentId].attended = !this.attendance[studentId].attended;
}
}
}
</script>
子组件:
<template>
<tr>
<td>{{ student.name }}</td>
<td><v-btn :color="attendance.attended ? 'green' : 'red'"
@click="$emit('attended-clicked',student.id)"
>Attended</v-btn>
</tr>
</template>
<script>
export default {
props: ['student','attendance']
}
</script>
我已尝试将代码保持在最小限度,以使您了解我要实现的目标。期望当单击按钮时,attendance.attended
将会改变。
使用Vue开发人员工具单击时,我可以验证该值是否正在更改(尽管我必须按“强制刷新”按钮才能看到更改)。但是显然,孩子的观点并没有改变。
我在这里做错了什么,破坏了反应性?
我还尝试过将Vue.set
与以下代码结合使用:
methods: {
changeAttendance(studentId) {
let attendance = this.attendance[studentId];
attendance.attended = !attendance.attended;
Vue.set(this.attendance,studentId,attendance);
}
}
没有区别。
感谢您对我在做什么的任何见解,谢谢!
解决方法
根据评论,潜在的问题是attendance
设置为{}
,这意味着{{3}中的attendance[student.id]
对象并不存在或不再被跟踪}}。这是通过问题中提供的Vue.set
示例代码来完成的操作。
基本上,我看到两种解决方法:
- 将出勤率设置为在API返回结果时始终使所有学生ID可用,从而使该对象始终具有一个已经在工作的对象
- 需要从
attendance
获取值时,开始跟踪反应性。
由于我不知道API及其调用方式,因此我将为选项2提供答案。所提供的代码几乎可以正常工作,因此只需对Parent
组件进行两次更改即可确保学生出勤对象总是被填充,并且将来会被跟踪:
<template>
<table>
<!-- :attendance needs an object to work -->
<Student
v-for="(student,index) in students"
:key="index"
:student="student"
:attendance="attendance[student.id] || {}"
@attended-clicked="changeAttendance"
/>
</table>
</template>
<script>
import Vue from "vue";
import StudentVue from "./Student.vue";
export default {
data() {
return {
students: [{ id: 1,name: "Pepito" },{ id: 2,name: "Sue" }],attendance: {}
};
},methods: {
changeAttendance(studentId) {
// make sure to have an object when accessing a student!
let attendance = this.attendance[studentId] || {};
attendance.attended = !attendance.attended;
// start tracking - this will always "reset" the tracking though!
Vue.set(this.attendance,studentId,attendance);
}
},components: { Student: StudentVue }
};
</script>
由于this.attendance[student.id] || {}
被多次使用,因此我将其封装到getAttendance(studentId) {...}
函数中。在此处,Vue.set
可用于开始跟踪对象是否不存在,从而使changeAttendance
更加简单,并且使|| {}
的意图更加清晰。
<template>
<table>
<Student
v-for="(student,index) in students"
:key="index"
:student="student"
:attendance="getAttendance(student.id)"
@attended-clicked="changeAttendance"
/>
</table>
</template>
<script>
import Vue from "vue";
import StudentVue from "./Student.vue";
export default {
data() {
return {
students: [{ id: 1,methods: {
changeAttendance(studentId) {
let attendance = this.getAttendance(studentId);
attendance.attended = !attendance.attended;
},getAttendance(studentId) {
const studentAttendance = this.attendance[studentId];
if (!studentAttendance) {
Vue.set(this.attendance,{ attended: false });
}
return this.attendance[studentId];
}
},components: { Student: StudentVue }
};
</script>
如果代码库在其他组件中使用this.something[something]
,则this.getAttendance
可能看起来有点不一致。在这种情况下,最好将所有学生的API调用结果更改为默认值,然后再将其提供给Parent
组件。就个人而言,我会尝试使用该选项。而且我会使用this.$set
而不是Vue.set
来摆脱import Vue
行...;)