145 votes

Vue - Deep regarde un tableau d'objets et calcule le changement?

J'ai un tableau appelé people qui contient des objets de la façon suivante:

Avant

[
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 32},
  {id: 2, name: 'Joe', age: 38}
]

Il peut changer:

Après

[
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 33},
  {id: 2, name: 'Joe', age: 38}
]

Avis Frank vient d'avoir 33.

J'ai une appli où je suis en train de regarder les gens tableau et lorsque l'une des valeurs les modifications, puis journal de la modification:

<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>

J'ai basé cette sur la question que j'ai demandé hier à propos de la matrice de comparaisons et choisis le moyen le plus rapide de travail la réponse.

Donc, à ce stade, je m'attends à voir un résultat de: { id: 1, name: 'Frank', age: 33 }

Mais tout ce que je rentre dans la console (en gardant à l'esprit que je l'ai eu dans un composant):

[Vue warn]: Error in watcher "people" 
(found in anonymous component - use the "name" option for better debugging messages.)

Et dans le codepen que j'ai fait, le résultat est un tableau vide et non pas de l'objet modifié qui a changé, qui serait ce à quoi je m'attendais.

Si quelqu'un pourrait suggérer pourquoi ce qui se passe ou où je suis allé mal ici, alors il serait grandement apprécié, merci beaucoup!

176voto

Mani Points 11600

Votre fonction de comparaison entre l'ancienne valeur et la nouvelle valeur est d'avoir quelques problème. Il est préférable de ne pas compliquer les choses à ce point, car il permettra d'augmenter votre débogage effort plus tard. Vous devez garder les choses simples.

La meilleure façon est de créer un person-component et de regarder chaque personne séparément à l'intérieur de son propre composant, comme indiqué ci-dessous:

<person-component :person="person" v-for="person in people"></person-component>

Veuillez trouver ci-dessous un exemple de travail pour regarder à l'intérieur de personne composant. Si vous souhaitez gérer sur parent côté, vous pouvez utiliser $emit d'envoyer un événement à la hausse, contenant l' id de toutes les modifications de la personne.

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/vue@2.1.5/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>

25voto

Viplock Points 2409

J'ai changé son implémentation pour résoudre votre problème, j'ai créé un objet pour suivre les anciens changements et le comparer à cela. Vous pouvez l'utiliser pour résoudre votre problème.

Ici, j'ai créé une méthode, dans laquelle l'ancienne valeur sera stockée dans une variable distincte et, qui sera ensuite utilisée dans une montre.

 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,
    }
  }
})
 

Voir le codepen mis à jour

25voto

Quirk Points 852

Elle est bien définie comportement. Vous ne pouvez pas obtenir l'ancienne valeur pour une muté objet. C'est parce que à la fois l' newVal et oldVal font référence au même objet. Vue va pas conserver un vieux copie d'un objet qui vous muté.

Vous avait remplacé l'objet avec un autre, la Vue aurait fourni références correctes.

Lire l' Note section dans les docs. (vm.$watch)

En savoir plus sur ce ici et ici.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X