Vue.js-ネストされたデータを適切に監視する方法


414

小道具のバリエーションを適切に監視する方法を理解しようとしています。ajax呼び出しからデータを受け取る親コンポーネント(.vueファイル)があり、データをオブジェクト内に配置し、それを使用してv-forディレクティブを介していくつかの子コンポーネントをレンダリングします(実装の簡略化の下)。

<template>
    <div>
        <player v-for="(item, key, index) in players"
            :item="item"
            :index="index"
            :key="key"">
        </player>
    </div>
</template>

...そして<script>タグの内側:

 data(){
     return {
         players: {}
 },
 created(){
        let self = this;
        this.$http.get('../serv/config/player.php').then((response) => {
            let pls = response.body;
            for (let p in pls) {
                self.$set(self.players, p, pls[p]);
            }
    });
}

アイテムオブジェクトは次のとおりです。

item:{
   prop: value,
   someOtherProp: {
       nestedProp: nestedValue,
       myArray: [{type: "a", num: 1},{type: "b" num: 6} ...]
    },
}

今、私の子の「プレーヤー」コンポーネント内で、アイテムのプロパティの変化を監視しようとしています。

...
watch:{
    'item.someOtherProp'(newVal){
        //to work with changes in "myArray"
    },
    'item.prop'(newVal){
        //to work with changes in prop
    }
}

それは機能しますが、それは私には少しトリッキーなようであり、これがそれを行う正しい方法であるかどうか疑問に思っていました。私の目標は、prop変更するたびにアクションを実行するかmyArray、新しい要素や既存の要素のバリエーションを取得することです。任意の提案をいただければ幸いです。


9
"item.someOtherProp": function (newVal, oldVal){@Ronの提案どおりに使用し てください。
ライナー2017年

1
@Reinerこれはまさに彼が質問で避けようとしていたことでした。ES5構文を使用しても同じです。実際には、計算されたものを監視することによる追加のオーバーヘッドはなく、さまざまな状況で役立つテクニックです。
craig_h 2017年

1
keysオブジェクト内のすべての変化を監視することは可能ですか?例:"item.*": function(val){...}
チャーリー

回答:


623

あなたはそのために深いウォッチャーを使うことができます:

watch: {
  item: {
     handler(val){
       // do stuff
     },
     deep: true
  }
}

これにより、item配列内のオブジェクトへの変更と配列自体への追加が検出されます(Vue.setで使用した場合)。これがJSFiddleです:http ://jsfiddle.net/je2rw3rs/

編集

最上位のオブジェクトのすべての変更を監視したくない場合、ネストされたオブジェクトを直接監視するための扱いにくい構文を使用したい場合は、computed代わりにを監視できます。

var vm = new Vue({
  el: '#app',
  computed: {
    foo() {
      return this.item.foo;
    }
  },
  watch: {
    foo() {
      console.log('Foo Changed!');
    }
  },
  data: {
    item: {
      foo: 'foo'
    }
  }
})

これがJSFiddleです:http ://jsfiddle.net/oa07r5fw/


2
このようにして、任意のプロップにバリエーションがある場合は常に「ハンドラー」が呼び出されます。私の目的は、ハンドラーを分離して、「someOtherProp」の「prop」またはmyArrayで変更が個別に発生したかどうかを確認することです
Plastic

9
特定のネストされたオブジェクトの変更を監視したいだけの場合は、問題ありません。扱いにくい構文が必要な場合は、computed代わりにjsfiddle.net/c52nda7x
craig_h

まさに私が探していたものですが、今では論理的に思えます。ネストされたプロップを返す計算されたプロパティを作成し、それを監視します。Tnxたくさん。
プラスチック

追加したいのですが、2番目のオプションは、元のウォッチャーとほとんど変わりません。内部的には、計算されたプロパティは、データ値を関数の結果に設定する関数内のすべての参照オブジェクト/値の監視にすぎません。-したがって、おそらくコードをもう少し複雑にするだけで、同じ効果が得られます。
Falco

22
@ファルコ私はここコメントで答えを見つけました。peerbolteは、ウォッチ名を一重引用符で囲むことで実行できることを示しました。私はこれをテストしましたが、うまくいきます。したがって、この場合はwatch: { 'item.foo' : function(newVal, oldVal) { // do work here }} かなり滑らかになります。Vueが大好きです。
ロンC

436

別の優れたアプローチで、もう少しエレガントな方法は次のとおりです。

 watch:{
     'item.someOtherProp': function (newVal, oldVal){
         //to work with changes in someOtherProp
     },
     'item.prop': function(newVal, oldVal){
         //to work with changes in prop
     }
 }

(私はここコメントで @peerbolteからこのアプローチを学びました


47
よりエレガントな方法(少しだけではない):P。これは一般的なユースケースであるため、Vue.jsのドキュメントにあるはずです。私は何時間も解決策を探してきました、あなたの答えに感謝します。
Claudiu

11
@symba興味深いのは、これがOPが回避するように求めたものと同じであり、ES5構文だけであることです。もちろん、ES2015 method definitionそれ自体は非常にエレガントではないますがそれは質問の要点を少し見逃してしまいます。それは、名前が引用符で囲まれないようにする方法です。ほとんどの人は、すでに回答が含まれている元の質問に
つまづいて

1
@craig_h超面白いポイント。私はもともと、入れ子になった小道具を見るための良い方法を見つけるために何時間も探していました。そして、この質問のタイトルは私が見つけた最も近い質問でしたが、彼が質問の本文に引用された小道具を記載しているのを見逃しましたが、答えはそれほどエレガントではありませんでした。私は最後に別の質問のコメントで引用された小道具のアプローチを見つけ、SOで引用された小道具のアプローチのドキュメントとしてそれを自己回答するためだけに質問を作成するように誘惑されました。しかし、この質問は私のタイトルが正しかったものであったので、ここに答えを追加しました。多分私は新しい質問を作成するべきでした。
ロンC

9
@RonCこれは人気のある質問であり、あなたの答えは多くの人が探しているものなので、ここにぴったりだと思います。私たち全員が限られた時間しか持っておらず、ほとんどの人が質問をほとんど読んでいないので、これは確かにあなたが提供した答えに対する批判ではありませんでした。私の元の答えは、質問され、意見を述べることを意図したものではありませんでした。私は実際には「監視計算」アプローチが好きですが、多くの人はそれほど複雑ではない「見積もり」アプローチを好んでいます。
craig_h

10
公式ドキュメントは、今、このアプローチが含まれて
feihcsim

19

子オブジェクトのVueJのディープウォッチ

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

8

プロパティをしばらく監視してから、監視を解除する場合はどうすればよいですか?

または、ライブラリの子コンポーネントプロパティを監視するには?

「動的ウォッチャー」を使用できます。

this.$watch(
 'object.property', //what you want to watch
 (newVal, oldVal) => {
    //execute your code here
 }
)

$watchそれが呼び出された場合見て停止しますunwatch機能を返します。

var unwatch = vm.$watch('a', cb)
// later, teardown the watcher
unwatch()

また、あなたはdeepオプションを使うことができます:

this.$watch(
'someObject', () => {
    //execute your code here
},
{ deep: true }
)

必ずドキュメントをご覧ください


2
docsによると、ウォッチャーの定義に矢印関数を使用しないでください:Note that you should not use an arrow function to define a watcher (e.g. searchQuery: newValue => this.updateAutocomplete(newValue)). The reason is arrow functions bind the parent context, so this will not be the Vue instance as you expect and this.updateAutocomplete will be undefined.
wonder95

7

ここでは言及されていませんvue-property-decoratorが、Vueクラスを拡張する場合はパターンを使用することもできます。

import { Watch, Vue } from 'vue-property-decorator';

export default class SomeClass extends Vue {
   ...

   @Watch('item.someOtherProp')
   someOtherPropChange(newVal, oldVal) {
      // do something
   }

   ...
}

5

このソリューションを「ハッキング」するために使用したもう1つの方法は、これを実行することでしたcomputed。ネストされたオブジェクトの値を返すだけの別の値を設定しました。

data : function(){
    return {
        my_object : {
            my_deep_object : {
                my_value : "hello world";
            }.
        },
    };
},
computed : {
    helper_name : function(){
        return this.my_object.my_deep_object.my_value;
    },
},
watch : {
    helper_name : function(newVal, oldVal){
        // do this...
    }
}

良いもの;)しかし、配列ではどうですか?:)
WEBCENTER

3

使用の受け入れられた答えに関する私の問題は、deep: true配列を詳細に監視するときに、配列のどの要素に変更が含まれているかを簡単に識別できないことです。私が見つけた唯一の明確な解決策は、この答えです。これは、各配列要素を個別に監視できるようにコンポーネントを作成する方法を説明しています。


1
はい、それは本当ですが、それはディープウォッチャーの目的ではありません(実際には、同じオブジェクトを見つけoldValnewVal参照するため、同じです)。aの目的は、イベントの発行、ajax呼び出しの実行など、値が変更されたときに何かdeep watcher実行することです。それ以外の場合は、おそらくManiの回答で指摘されているように、コンポーネントが必要です。
craig_h

個々の変更を追跡するのと同じ問題がありました!私は解決策を見つけ、ここに文書化しました
Erik Koopmans

3

リスト内の変更された個々のアイテムを追跡する

リスト内のすべてのアイテムを監視し、リスト内のどのアイテムが変更されたか知りたい場合は、次のように、すべてのアイテムに個別にカスタムウォッチャーを設定できます。

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

リストがすぐに入力されていない場合(元の質問のように)、ロジックをcreated必要な場所(.then()ブロック内など)に移動できます。

変化するリストを見る

リスト自体が更新されて新しいアイテムまたは削除されたアイテムがある場合、私は「浅い」リスト自体を監視し、リストの変更に応じてアイテムを動的に監視/監視解除する便利なパターンを開発しました。

// NOTE: This example uses Lodash (_.differenceBy and _.pull) to compare lists
//       and remove list items. The same result could be achieved with lots of
//       list.indexOf(...) if you need to avoid external libraries.

var vm = new Vue({
  data: {
    list: [
      {name: 'obj1 to watch'},
      {name: 'obj2 to watch'},
    ],
    watchTracker: [],
  },
  methods: {
    handleChange (newVal, oldVal) {
      // Handle changes here!
      console.log(newVal);
    },
    updateWatchers () {
      // Helper function for comparing list items to the "watchTracker".
      const getItem = (val) => val.item || val;

      // Items that aren't already watched: watch and add to watched list.
      _.differenceBy(this.list, this.watchTracker, getItem).forEach((item) => {
        const unwatch = this.$watch(() => item, this.handleChange, {deep: true});
        this.watchTracker.push({ item: item, unwatch: unwatch });
        // Uncomment below if adding a new item to the list should count as a "change".
        // this.handleChange(item);
      });

      // Items that no longer exist: unwatch and remove from the watched list.
      _.differenceBy(this.watchTracker, this.list, getItem).forEach((watchObj) => {
        watchObj.unwatch();
        _.pull(this.watchTracker, watchObj);
        // Optionally add any further cleanup in here for when items are removed.
      });
    },
  },
  watch: {
    list () {
      return this.updateWatchers();
    },
  },
  created () {
    return this.updateWatchers();
  },
});

0

私にとっての答えはどれもうまくいきませんでした。実際に、コンポーネントが複数回呼び出されてネストされたデータを監視したい場合。そのため、それらを識別するためにさまざまな小道具で呼び出されます。例えば<MyComponent chart="chart1"/> <MyComponent chart="chart2"/> 私の回避策は、追加のvuex状態変数を作成することです。これを手動で更新して、最後に更新されたプロパティをポイントします。

Vuex.tsの実装例を次に示します。

export default new Vuex.Store({
    state: {
        hovEpacTduList: {},  // a json of arrays to be shared by different components, 
                             // for example  hovEpacTduList["chart1"]=[2,6,9]
        hovEpacTduListChangeForChart: "chart1"  // to watch for latest update, 
                                                // here to access "chart1" update 
   },
   mutations: {
        setHovEpacTduList: (state, payload) => {
            state.hovEpacTduListChangeForChart = payload.chart // we will watch hovEpacTduListChangeForChart
            state.hovEpacTduList[payload.chart] = payload.list // instead of hovEpacTduList, which vuex cannot watch
        },
}

ストアを更新するコンポーネント関数:

    const payload = {chart:"chart1", list: [4,6,3]}
    this.$store.commit('setHovEpacTduList', payload);

次に、任意のコンポーネントで更新を取得します。

    computed: {
        hovEpacTduListChangeForChart() {
            return this.$store.state.hovEpacTduListChangeForChart;
        }
    },
    watch: {
        hovEpacTduListChangeForChart(chart) {
            if (chart === this.chart)  // the component was created with chart as a prop <MyComponent chart="chart1"/> 
                console.log("Update! for", chart, this.$store.state.hovEpacTduList[chart]);
        },
    },
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.