Vue.js - 如何正确观察嵌套数据

时间:2017-02-09 10:16:36

标签: javascript vue.js vue-component vue-resource

我试图了解如何正确地观察一些道具变异。 我有一个父组件(.vue文件)从ajax调用接收数据,将数据放入一个对象并使用它通过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} ...]
    },
}

现在,在我的孩子&#34;播放器&#34;组件I试图查看任何项目的属性变化,我使用:

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

它有效,但对我来说似乎有点棘手,我想知道这是否是正确的方法。我的目标是每次prop更改时执行某些操作,或myArray获取新元素或在现有元素内部进行某些更改。任何建议将不胜感激。

14 个答案:

答案 0 :(得分:381)

您可以使用deep watcher

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/

答案 1 :(得分:270)

另一个更好的方法和更优雅的方法如下:

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

(我在comment here

中从@peerbolte学到了这种方法

答案 2 :(得分:8)

我发现它也可以这样工作:

watch: {
    "details.position"(newValue, oldValue) {
        console.log("changes here")
    }
},
data() {
    return {
      details: {
          position: ""
      }
    }
}

答案 3 :(得分:5)

另一种方法。

请勿使用箭头功能。您无法访问对象。

watch:{
    'item.someOtherProp': function(newValue, oldValue){
        // Process
    }
}

答案 4 :(得分:4)

  

如果您想看一会儿财产然后不看它怎么办?

     

还是要观看库子组件属性?

您可以使用“动态观察器”:

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

$watch返回一个取消监视功能,如果调用该功能,它将停止监视。

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

您还可以使用deep选项:

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

请确保查看to docs

答案 5 :(得分:4)

我个人更喜欢这种干净的实现:

map

答案 6 :(得分: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();
  },
});

答案 7 :(得分:2)

这里没有看到它,但是如果您扩展render() { const { data } = this.state; const allObj = { value: 'all', label: 'All' }; return ( <div> <div className="grid-wrapper"> <div className="toolbar-sections comments"> <Select id="selectStage" getOptionLabel={(option) => 'Stage: ' + option.label} className="toolbar-select" options={this.state.optionsStages} onChange={(value) => { this.handleDropDownChange(value, 'selectedStage') }} placeholder="Stages" isSearchable={false} defaultValue={allObj} /> </div> <ReactTable filtered={this.state.filtered} defaultFilterMethod={(filter, row) => String(row[filter.id]) === filter.value} data={data} onFilteredChange={this.onFilteredChange.bind(this)} columns={[ { Header: "Author", id: "author", accessor: "author", width: 170, Cell: row => { return ( <span> {row.value} </span> ); } }, { Header: "Subject", accessor: "subject", Cell: row => ( <span> <span><a href="#">{row.value}...</a></span> </span> ) }, { Header: "Stage", accessor: "stage", width: 150 } ]} defaultPageSize={10} className="-highlight" /> <br /> </div> </div> ); } } export default withRouter(CommentsTableComponent); 类,也可以使用vue-property-decorator模式。

Vue

答案 8 :(得分:2)

对我来说,答案都没有奏效。实际上,如果您想监视嵌套数据,并且组件被多次调用。因此,它们被用不同的道具来识别。 例如let indexLat = locationString.index(locationString.startIndex, offsetBy: 8) let indexLong = locationString.index(indexLat, offsetBy: 9) let lat = String(locationString[locationString.startIndex..<indexLat]) let long = String(locationString[indexLat..<indexLong]) if let lattitude = Double(lat), let longitude = Double(long) { let location = CLLocation(latitude: lattitude, longitude: longitude) } 我的解决方法是创建一个附加的vuex状态变量,我手动对其进行更新以指向最后更新的属性。

这是Vuex.ts的实现示例:

<MyComponent chart="chart1"/>  <MyComponent chart="chart2"/>

在任何Component函数上更新商店:

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

答案 9 :(得分:1)

我对使用deep: true的答案的问题是,在深入观察数组时,我不容易确定哪个元素包含更改。我找到的唯一明确的解决方案是this answer, which explains how to make a component so you can watch each array element individually.

答案 10 :(得分:1)

VueJ深入观察子对象

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

答案 11 :(得分:0)

我用来“破解”此解决方案的另一种添加方法是: 我设置了一个单独的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...
    }
}

答案 12 :(得分:0)

我使用了 deep:true,但发现监视函数中的旧值和新值始终相同。作为先前解决方案的替代方案,我尝试了此方法,它将通过将其转换为字符串来检查整个对象中的任何更改:

created() {
    this.$watch(
        () => JSON.stringify(this.object),
            (newValue, oldValue) => {
                //do your stuff                
            }
    );
},

答案 13 :(得分:-1)

这是一种为嵌套属性编写观察程序的方法:

    new Vue({
        ...allYourOtherStuff,
        watch: {
            ['foo.bar'](newValue, oldValue) {
                // Do stuff here
            }
        }
    });

您甚至可以将此语法用于异步观察程序:

    new Vue({
        ...allYourOtherStuff,
        watch: {
            async ['foo.bar'](newValue, oldValue) {
                // Do stuff here
            }
        }
    });