I ran into this headache before, and I did solve/ cheat on it with deep watch and Lodash _.cloneDeep and _.isEqual.
Inside your child component, create your own data componentTask. You will watch your componentTask and your prop. Every time they change, compare them using _.isEqual. When componentTask changes, emit an event to its parent.
SubTask:
<template>
<div>
<input type="text" v-model="componentTask.task">
<input type="number" min="0" v-model.number="componentTask.spentTime">
<SubTask v-if="task.subTasks" @task-change="handleTaskChange" :task="task.subTasks" />
</div>
</template>
<script lang="ts">
import {Vue, Component, Prop, Watch} from 'vue-property-decorator'
import {Task} from "@/components/Test/Test";
import _ from "lodash";
@Component
export default class SubTask extends Vue {
@Prop() task!: Task;
componentTask: Task | undefined = this.task;
@Watch('task', {deep: true, immediate: true})
onTaskChange(val: Task, oldVal: Task) {
if (_.isEqual(this.componentTask, val))
return;
this.componentTask = _.cloneDeep(val);
}
@Watch('componentTask', {deep: true, immediate: true})
onComponentTaskChange(val: Task, oldVal: Task) {
if (_.isEqual(val, this.task))
return;
this.$emit("task-change");
}
handleTaskChange(subTasks: Task){
this.componentTask = subTasks;
}
}
</script>
Parent class:
<template>
<div style="margin-top: 400px">
<h1>Parent Task</h1>
<br>
<div style="display: flex;">
<div style="width: 200px">
<h4>task</h4>
<p>{{task.task}}</p>
<p>{{task.spentTime}}</p>
<br>
</div>
<div style="width: 200px">
<h4>task.subTasks</h4>
<p>{{task.subTasks.task}}</p>
<p>{{task.subTasks.spentTime}}</p>
<br>
</div>
<div style="width: 200px">
<h4>task.subTasks.subTasks</h4>
<p>{{task.subTasks.subTasks.task}}</p>
<p>{{task.subTasks.subTasks.spentTime}}</p>
<br>
</div>
</div>
<SubTask :task="task" @task-change="handleTaskChange"/>
</div>
</template>
<script lang="ts">
import {Vue, Component, Prop} from 'vue-property-decorator'
import SubTask from "@/components/Test/SubTask.vue";
import {defaultTask, Task} from "@/components/Test/Test";
@Component({
components: {SubTask}
})
export default class Test extends Vue {
task: Task = defaultTask;
handleTaskChange(task: Task) {
this.task = task;
}
}
</script>
Defined interface:
export interface Task {
task: string;
spentTime: number;
subTasks?: Task;
}
export const defaultTask: Task = {
task: "Some Task",
spentTime : 2,
subTasks: {
task: "Some Sub Task",
spentTime: 1,
subTasks:{
task:"Some sub sub task",
spentTime:30
}
}
};
spentTimeof parent whenspentTimeof child changes? And what should happen tospentTimeof children whenspentTimeof parent changes? What's the expected relation between them and which value should be prevalent when they don't add up? - tao12. But its children add up to2. And you change one of the children and they now add up to4. Do you want to override the initial12with4? You don't seem to have a technical implementation issue. It's more of a requirements definition issue. As long as you know what should happen, it's easy to do. :). We're getting back to my initial question: "Which value should be prevalent in case of conflicts?" - tao12should be overridden with4"? If you think it's relevant, provide the function you have. Eventually, create a minimal reproducible example even if it creates a stack overflow. At least we can see the required logic. - tao