Vue - Bir dizi nesneyi derinlemesine izlemek ve değişimi hesaplamak?


108

peopleAşağıdaki gibi nesneleri içeren bir dizim var :

Önce

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

Değişebilir:

Sonra

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

Frank'in 33 yaşına girdiğine dikkat edin.

People dizisini izlemeye çalıştığım bir uygulamam var ve değerlerden herhangi biri değiştiğinde değişikliği günlüğe kaydedin:

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

Bunu dün dizi karşılaştırmaları hakkında sorduğum soruya dayandırdım ve en hızlı çalışan cevabı seçtim .

Dolayısıyla, bu noktada şunların bir sonucunu görmeyi bekliyorum: { id: 1, name: 'Frank', age: 33 }

Ancak konsolda geri aldığım tek şey (bir bileşende olduğunu aklımızda tutarak):

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

Ve yaptığım codepen'de sonuç boş bir dizidir, değişen nesne değil, beklediğim gibi.

Birisi bunun neden olduğunu veya burada nerede yanlış yaptığımı önerebilirse, o zaman çok takdir edilecektir, çok teşekkürler!

Yanıtlar:


136

Eski değer ile yeni değer arasındaki karşılaştırma fonksiyonunuzda bazı sorunlar var. Daha sonra hata ayıklama çabanızı artıracağından işleri bu kadar karmaşıklaştırmamak daha iyidir. Basit tutmalısın.

En iyi yol, person-componentaşağıda gösterildiği gibi, her kişiyi kendi bileşeni içinde ayrı ayrı izlemek ve izlemektir:

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

Aşağıda, içeriden kişi bileşenini izlemek için çalışan bir örnek bulun. Ebeveyn tarafında halletmek istiyorsanız , değiştirilmiş kişinin $emitbulunduğu bir etkinliği yukarı doğru göndermek için kullanabilirsiniz 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/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>


Bu gerçekten çalışan bir çözüm ama tamamen benim kullanım durumuma göre değil. Görüyorsunuz, gerçekte uygulamaya ve bir bileşene sahibim, bileşen vue-material tablosunu kullanıyor ve verileri satır içi olarak düzenleme becerisiyle birlikte verileri listeliyor. Değerlerden birini değiştirmeye çalışıyorum ve sonra neyin değiştiğini kontrol ediyorum, bu durumda, aslında ne fark olduğunu görmek için önceki ve sonraki dizileri karşılaştırıyor. Sorunu çözmek için çözümünüzü uygulayabilir miyim? Gerçekten de bunu yapabilirdim, ancak bu bağlamda vue-material içinde mevcut olanın akışına karşı çalışacağını hissediyorum
Craig van Tonder

2
Bu arada, bunu açıklamak için zaman ayırdığınız için teşekkürler, takdir ettiğim Vue hakkında daha fazla şey öğrenmeme yardımcı oldu!
Craig van Tonder

Bunu anlamam biraz zaman aldı ama kesinlikle haklısın, bu bir cazibe gibi çalışıyor ve kafa karışıklığından ve başka sorunlardan kaçınmak istiyorsanız işleri yapmanın doğru yolu :)
Craig van Tonder

1
Bunu ben de fark ettim ve aynı düşünceye sahip oldum ama nesnede de bulunan şey değeri içeren değer indeksi, alıcılar ve ayarlayıcılar oradalar ama buna kıyasla onları yok sayıyor, daha iyi bir anlayış olmadığı için öyle olduğunu düşünüyorum. herhangi bir prototip üzerinde değerlendirilmez. Diğer cevaplardan biri, işe yaramayacağının nedenini açıklıyor, çünkü newVal ve oldVal aynı şeydi, biraz karmaşık ama birkaç yerde ele alınan bir şey, yine de başka bir cevap, kolay yaratma için iyi bir çalışma sağlıyor. karşılaştırma amaçlı değişmez bir nesne.
Craig van Tonder

1
Nihayetinde, yolunuz bir bakışta daha kolay anlaşılır ve değer değiştiğinde nelerin mevcut olduğu konusunda daha fazla esneklik sağlar. Vue'da bunu basit tutmanın faydalarını anlamama çok yardımcı oldu ama diğer sorumda gördüğünüz gibi biraz takılıp kaldı. Çok teşekkürler! :)
Craig van Tonder

21

Sorununuzu çözmek için uygulamasını değiştirdim, eski değişiklikleri takip etmek ve bununla karşılaştırmak için bir nesne yaptım. Sorununuzu çözmek için kullanabilirsiniz.

Burada eski değerin ayrı bir değişkende saklanacağı ve daha sonra bir saatte kullanılacak bir yöntem oluşturdum.

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

Güncellenen kod parçasına bakın


Bu yüzden monte edildiğinde verilerin bir kopyasını saklayın ve bunu onunla karşılaştırmak için kullanın. İlginç, ancak kullanım durumum daha karmaşık olacaktı ve diziye nesne ekleyip çıkarırken bunun nasıl çalışacağından emin değilim, @Quirk de sorunu çözmek için iyi bağlantılar sağladı. Ama kullanabileceğini bilmiyordum vm.$data, teşekkürler!
Craig van Tonder

evet, ve saatten sonra da yöntemi tekrar arayarak güncelliyorum, orijinal değere geri dönerseniz, o zaman değişikliği de izleyecektir.
Viplock

Ohhh, orada saklanmanın çok mantıklı olduğunu ve bununla baş etmenin daha az karmaşık bir yolu olduğunu fark etmedim (github'daki çözümün aksine).
Craig van Tonder

ve ya, eğer orijinal diziden bir şey ekliyor veya çıkarıyorsanız, sadece yöntemi tekrar çağırın ve çözüme tekrar devam etmeniz iyi olur.
Viplock

1
_.cloneDeep () benim durumumda gerçekten yardımcı oldu. Teşekkür ederim!! Gerçekten yardımcı!
Cristiana Pereira

18

İyi tanımlanmış bir davranıştır. Mutasyona uğramış bir nesnenin eski değerini alamazsınız . Her iki nedeni budur newValve oldValaynı nesneyi. Vue olacak değil sen mutasyona uğramış olduğu bir nesnenin eski bir kopyasını saklayın.

Eğer olsaydı değiştirilir başka bir nesneyi, Vue doğru referanslarla sizi sağlayabilirdi.

BelgelerdekiNote bölümü okuyun . (vm.$watch )

Bu konuda daha fazlası burada ve burada .


3
Oh şapkam, çok teşekkürler! Bu aldatıcı bir şey ... val ve oldVal'in farklı olmasını tamamen bekliyordum ama onları inceledikten sonra yeni dizinin iki kopyası olduğunu görüyorum, daha önce takip etmiyor. Biraz daha okuyun ve aynı yanlış anlaşılmayla ilgili şu yanıtlanmamış SO sorusunu bulduk: stackoverflow.com/questions/35991494/…
Craig van Tonder

5

Bir nesneyi derinlemesine izlemek için kullandığım şey bu. Benim ihtiyacım nesnenin alt alanlarını izlemekti.

new Vue({
    el: "#myElement",
    data:{
        entity: {
            properties: []
        }
    },
    watch:{
        'entity.properties': {
            handler: function (after, before) {
                // Changes detected.    
            },
            deep: true
        }
    }
});

Ben de nitelendirildi CAVet anlayışını eksik olabilir inanıyoruz stackoverflow.com/a/41136186/2110294 . Açık olmak gerekirse, bu sorunun çözümü değildir ve belirli durumlarda beklediğiniz gibi çalışmayacaktır.
Craig van Tonder

tam olarak aradığım buydu !. Teşekkürler
Jaydeep Shil

Burada da aynısı, tam olarak ihtiyacım olan şey !! Teşekkürler.
Guntar

4

Bileşen çözümü ve derin klon çözümünün avantajları vardır, ancak aynı zamanda sorunları da vardır:

  1. Bazen soyut verilerdeki değişiklikleri izlemek istersiniz - bu veriler etrafında bileşenler oluşturmak her zaman mantıklı değildir.

  2. Her değişiklik yaptığınızda tüm veri yapınızı derinlemesine klonlamak çok pahalı olabilir.

Bence daha iyi bir yol var. Bir listedeki tüm öğeleri izlemek ve listedeki hangi öğenin değiştiğini bilmek istiyorsanız, aşağıdaki gibi her öğe için ayrı ayrı özel izleyiciler ayarlayabilirsiniz:

var vm = new Vue({
  data: {
    list: [
      {name: 'obj1 to watch'},
      {name: 'obj2 to watch'},
    ],
  },
  methods: {
    handleChange (newVal) {
      // Handle changes here!
      console.log(newVal);
    },
  },
  created () {
    this.list.forEach((val) => {
      this.$watch(() => val, this.handleChange, {deep: true});
    });
  },
});

Bu yapı ile, handleChange() değişen belirli liste öğesini alacaksınız - buradan istediğiniz herhangi bir işlemi yapabilirsiniz.

Listenize öğe ekleyip çıkarmanız durumunda (yalnızca zaten var olan öğeleri değiştirmek yerine) burada daha karmaşık bir senaryo da belgeledim .


Teşekkürler Erik, geçerli noktaları sunuyorsunuz ve sağlanan metodoloji, soruya bir çözüm olarak uygulanırsa kesinlikle yararlıdır.
Craig van Tonder
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.