Подтвердить что ты не робот

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, который я создал, результатом является пустой массив, а не измененный объект, который изменился, что было бы тем, что я ожидал.

Если кто-нибудь может предположить, почему это происходит, или где я здесь не так, то было бы очень признательно, большое спасибо!

4b9b3361

Ответ 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
        }
    }
});