javascript - 如何在 Vue JS 观察程序中更改对象的属性而没有无限循环?
问题描述
我有一个这样的对象数组:
let tree = [
{
task: "Some Task",
spentTime : 2,
subTasks: {
task: "Some Sub Task",
spentTime: 1,
subTasks:{
task:"Some sub sub task",
spentTime:30
}
}
}
]
正如你在这里看到的,我有这种类型的树结构,我正在展示某种嵌套的手风琴。所以每个节点都有一个输入框,它有 2 路绑定与花费时间属性( using v-model )
。
现在,如果我输入任何节点的输入。我需要对这些花费时间值进行一些操作并重新填充或在同一对象中插入不同的值。
在这里,我正在考虑做深watch
。但我认为这会导致无限循环,因为我正在更改同一个对象并重新分配值,它会watch
再次触发:)
如果我想在输入更改时触发一个函数并将不同的值放回同一个对象中,我可以做什么。
谢谢!
解决方案
tl;博士
干净的解决方案,基于@djiss 的建议,并且正确地冒泡到最高父级,使用$set
and watch
,在这里:
https
://codesandbox.io/s/peaceful-kilby-yqy9v
下面是最初的答案/逻辑,它使用$emit
和任务“键”来移动父项中的更新。
在 Vue 中你不能直接修改子元素。我的意思是,你可以,但你不应该。当你这样做时,Vue 会警告你,通知你刚刚所做的更改将在父级更改时被覆盖。
唯一的选择是使用状态来管理你的应用程序的单一真实来源(Vuex 或简单的 Vue 对象),或者你打电话给父母告诉它:“用这个特定的值改变这个特定的孩子”。你只需倾听来自父母的变化。
这就是我在这里所做的:
const task = {
task: "Some Task",
spentTime: 2,
subTasks: [{
task: "Some Sub Task",
spentTime: 1,
subTasks: [{
task: "Some sub sub task",
spentTime: 30
}, {
task: "Some other sub sub task",
spentTime: 12
}]
}]
};
Vue.config.productionTip = false;
Vue.config.devtools = false;
Vue.component('Task', {
template: `
<div>
<h2>{{task.task}} ({{spentTime}})</h2>
<div v-if="hasTasks">
<Task v-for="(t, k) in task.subTasks" :key="k" :task="t" @fromChild="fromChild" :tid="k"/>
</div>
<input v-else v-model="localTime" type="number" @input="updateParent(localTime)">
</div>
`,
props: {
task: {
type: Object,
required: true
},
tid: {
type: Number,
default: 0
}
},
data: () => ({
localTime: 0
}),
mounted() {
this.updateParent(this.spentTime);
},
computed: {
spentTime() {
return this.hasTasks ? this.subtasksTotal : this.task.spentTime;
},
subtasksTotal() {
return this.task.subTasks.map(t => t.spentTime).reduce(this.sum, 0)
},
hasTasks() {
return !!(this.task.subTasks && this.task.subTasks.length);
}
},
methods: {
fromChild(time, task) {
this.task.subTasks[task].spentTime = time;
this.updateParent(this.spentTime);
},
updateParent(time) {
this.$emit("fromChild", Number(time), this.tid);
this.localTime = this.spentTime;
},
sum: (a, b) => a + b
},
watch: {
"task.spentTime": function() {
this.localTime = this.task.spentTime;
}
}
});
new Vue({
el: "#app",
data: () => ({
task
}),
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<Task :task="task" :tid="0" />
</div>
只要它具有相同的结构,它就会消耗你扔给它的任何树。逻辑是:如果没有子任务则显示输入,否则从子任务计算。
显然,您可以更改它以满足您的需求。
推荐阅读
- three.js - 从 CatmullRomCurve3 和 ExtrudeBufferGeometry 更新路径
- python - 如何使用python客户端api从GCS云存储桶下载文件夹中的所有文件?
- react-table - 使用 React 表编辑特定列
- python - 如何使用 Pandas 操作包含列表的列以删除非常相似的元素?
- mysql - MySQL 连接/联合
- python - 访问嵌套键中的每个键
- c++ - 多个模板的笛卡尔积
- c - 在 C 语言的 if 或 while 语句上带有宏的 HardFault
- javascript - material-ui useStyles 真的需要整个 props 对象吗?
- android - Base64 iOS vs Android 不同的结果