Vue - Глубокий просмотр массива объектов и вычисление изменений?
У меня есть массив с именем people
, который содержит объекты следующим образом:
До
[
{id: 0, name: 'Bob', age: 27},
{id: 1, name: 'Frank', age: 32},
{id: 2, name: 'Joe', age: 38}
]
Он может измениться:
После
[
{id: 0, name: 'Bob', age: 27},
{id: 1, name: 'Frank', age: 33},
{id: 2, name: 'Joe', age: 38}
]
Обратите внимание, что Фрэнку исполнилось 33 года.
У меня есть приложение, в котором я пытаюсь посмотреть массив людей, и когда какое-либо из значений изменяется, запишите это изменение:
<style>
input {
display: block;
}
</style>
<div id="app">
<input type="text" v-for="(person, index) in people" v-model="people[index].age" />
</div>
<script>
new Vue({
el: '#app',
data: {
people: [
{id: 0, name: 'Bob', age: 27},
{id: 1, name: 'Frank', age: 32},
{id: 2, name: 'Joe', age: 38}
]
},
watch: {
people: {
handler: function (val, oldVal) {
// Return the object that changed
var changed = val.filter( function( p, idx ) {
return Object.keys(p).some( function( prop ) {
return p[prop] !== oldVal[idx][prop];
})
})
// Log it
console.log(changed)
},
deep: true
}
}
})
</script>
Я основывался на этом вопросе который вчера я спросил о сравнении массивов, и выбрал самый быстрый рабочий ответ.
Итак, в этот момент я ожидаю увидеть результат: { id: 1, name: 'Frank', age: 33 }
Но все, что я верну в консоль (имея в виду, я имел это в компоненте):
[Vue warn]: Error in watcher "people"
(found in anonymous component - use the "name" option for better debugging messages.)
И в codepen, который я создал, результатом является пустой массив, а не измененный объект, который изменился, что было бы тем, что я ожидал.
Если кто-нибудь может предположить, почему это происходит, или где я здесь не так, то было бы очень признательно, большое спасибо!
Ответы
Ответ 1
У вашей функции сравнения между старым значением и новым значением возникает некоторая проблема. Лучше не усложнять такие вещи, поскольку это увеличит ваши усилия по отладке позже. Вы должны держать его простым.
Лучший способ - создать person-component
и наблюдать за каждым человеком отдельно от его собственного компонента, как показано ниже:
<person-component :person="person" v-for="person in people"></person-component>
Ниже приведен рабочий пример для просмотра внутреннего компонента. Если вы хотите обработать его на родительской стороне, вы можете использовать $emit
для отправки события вверх, содержащего id
измененного человека.
Vue.component('person-component', {
props: ["person"],
template: `
<div class="person">
{{person.name}}
<input type='text' v-model='person.age'/>
</div>`,
watch: {
person: {
handler: function(newValue) {
console.log("Person with ID:" + newValue.id + " modified")
console.log("New age: " + newValue.age)
},
deep: true
}
}
});
new Vue({
el: '#app',
data: {
people: [
{id: 0, name: 'Bob', age: 27},
{id: 1, name: 'Frank', age: 32},
{id: 2, name: 'Joe', age: 38}
]
}
});
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<body>
<div id="app">
<p>List of people:</p>
<person-component :person="person" v-for="person in people"></person-component>
</div>
</body>
Ответ 2
Я изменил его реализацию, чтобы решить вашу проблему, я создал объект для отслеживания старых изменений и сравнения с ним. Вы можете использовать его для решения вашей проблемы.
Здесь я создал метод, в котором старое значение будет храниться в отдельной переменной и который затем будет использоваться в часах.
new Vue({
methods: {
setValue: function() {
this.$data.oldPeople = _.cloneDeep(this.$data.people);
},
},
mounted() {
this.setValue();
},
el: '#app',
data: {
people: [
{id: 0, name: 'Bob', age: 27},
{id: 1, name: 'Frank', age: 32},
{id: 2, name: 'Joe', age: 38}
],
oldPeople: []
},
watch: {
people: {
handler: function (after, before) {
// Return the object that changed
var vm = this;
let changed = after.filter( function( p, idx ) {
return Object.keys(p).some( function( prop ) {
return p[prop] !== vm.$data.oldPeople[idx][prop];
})
})
// Log it
vm.setValue();
console.log(changed)
},
deep: true,
}
}
})
Смотрите обновленный кодекс
Ответ 3
Это хорошо определенное поведение. Вы не можете получить старое значение для мутированного объекта. Это потому, что и newVal
и oldVal
ссылаются на один и тот же объект. Vue не будет хранить старую копию объекта, который вы мутировали.
Если бы вы заменили объект другим, Вью предоставил бы вам правильные ссылки.
Прочтите раздел " Note
" в документации. (vm.$watch
)
Подробнее об этом здесь и здесь.
Ответ 4
Это то, что я использую для глубокого наблюдения за объектом. Моим требованием было наблюдение за дочерними полями объекта.
new Vue({
el: "#myElement",
data:{
entity: {
properties: []
}
},
watch:{
'entity.properties': {
handler: function (after, before) {
// Changes detected.
},
deep: true
}
}
});