【发布时间】:2018-10-19 06:29:09
【问题描述】:
我在一个页面上有多个组件,我发现对父对象的更新会反映在其中一个组件中,而不会反映在另一个组件中。
主页PatientEditor.vue 包含以下组件:
<notes-editor v-model="pt" />
<chart-completion v-model="pt" arrange="horiz" />
并且有这个脚本:
module.exports = {
props: ["pt"]
};
所以,pt 对象在父对象中,它作为v-model 传递给多个组件
组件ChartCompletion.vue 运行良好。里面有这些。
module.exports = {
props: ["value", "arrange"],
computed: {
completed_notes: function() {
return this.value.notes.filter(function(note) {
return note.signed_at;
}).length;
},
不过,我的问题孩子是 NotesEditor.vue 模板,其中包含以下内容:
module.exports = {
props: ["value"],
computed: {
completed_notes: function() {
return this.value.notes.filter(function(note) {
return note.signed_at;
}).length;
}
},
不确定它是否重要,但 notes 对象是从另一个组件中的 ajax 调用填充的,如下所示:
this.value.notes.splice(0, this.value.notes.length, ...response.body.notes);
this.$emit("input", this.value);
太好了,问题来了。
当this.value.notes 更新时,结果会在ChartCompletion 组件中看到,但在NotesEditor 组件中看不到。当我在 chrome 中使用 Vue 调试器时,我可以看到 notes 对象已更改,但由于某种原因,计算属性不会重新触发,即使它在 ChartCompletion 组件中具有相同的定义。另外,我在NotesEditor 中有一个v-for,它也不会改变。
调试此问题的最佳方法是什么?
EDIT 1 -- 包括NotesEditor 组件
<template>
<span>
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Status</th>
<th>Audio</th>
<th>Text</th>
</tr>
</thead>
<tbody>
<tr v-for="n in value.notes" :key="n.id">
<th>{{n.created_at}}</th>
<td>{{n.note_status_id}}</td>
<td>
<span v-if="n.audio_length > 0">
<audio controls="controls" preload="none">
<source :src="audioURL(n.id)" type="audio/webm"> Your browser does not support the audio element.
</audio>
({{n.audio_length}}s)
</span>
<span v-else>
None
</span>
</td>
<td>
<span v-if="n.note_text">
<button data-toggle="tooltip" :title="n.note_text" class="btn btn-outline-primary btn-sm" @click.prevent="openChartEditor(n.id)">
<span class="glyphicon glyphicon-edit"></span> Edit Note ({{ n.note_text.length }} chars)
</button>
</span>
<span v-else>
<button class="btn btn-primary btn-sm" @click.prevent="openChartEditor(n.id)">
<span class="glyphicon glyphicon-pencil"></span> Create Note
</button>
</span>
</td>
</tr>
<tr>
<td colspan="3">
<record v-model="value" />
</td>
<td>
<button class="btn btn-primary" @click.prevent="openChartEditor(0)">
<span class="glyphicon glyphicon-pencil"></span> Create Note
</button>
</td>
</tr>
</tbody>
</table>
</span>
</template>
<script>
module.exports = {
props: ["value"],
data: function() {
return {
sendFaxWorking: false
};
},
computed: {
completed_notes: function() {
return this.value.notes.filter(function(note) {
return note.signed_at;
}).length;
}
},
methods: {
audioURL: function(id) {
return "/notes/getAudio/" + id;
},
openChartEditor: function(id) {
this.$root.$emit("showEditor", id);
}
}
};
</script>
<style>
audio {
vertical-align: middle;
border: 0;
margin: 0;
}
</style>
【问题讨论】:
-
你的
v-for使用了一些:key=吗? AFAIKthis.value.notes.splice(也改变了道具,而是创建一个新的道具然后你$emit -
两个组件中是否实际使用了计算属性?它们是惰性求值的,因此只有在需要时才会更新
-
实际上我无法重现您的问题(快速尝试jsfiddle.net/maxsinev/dp86Lzyu),但我可以对您的代码说的是 - 不要更改
this.value.notes(因为它是一个道具字段!)子组件,您需要另一个组件,其中notes将分别作为v-model传递,您应该执行类似this.$emit('input', response.body.notes)的操作。或者你可以使用 Vuex 来避免使用 props 的深度嵌套和代码重复。 -
@birdspider -
v-for确实使用了:key并且它第一次可以正常工作。它只是不更新;根据vuejs.org/v2/guide/list.html#Array-Change-Detection,看来 splice 是一个包装函数,应该引起通知。事实上,它确实在ChartCompletion组件中通知,只是不在NotesEditor组件中。 -
@mankowitz 是的,它不一样,但
value包含notes。您通过引用子组件传递value,作为最佳实践,您不应更改道具(或道具本身)的内部值。老实说,$emit在你的情况下什么都不做,这表明存在一些不一致,例如,你可以删除$emit,它会像以前一样工作(你可以在我上面的小提琴中尝试)。
标签: javascript vue.js vuejs2 vue-component