VueJs观察物体的深层变化 [英] VueJs watching deep changes in object

查看:64
本文介绍了VueJs观察物体的深层变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在VueJS中有这3个组件.我要解决的问题是:当我单击vehicle组件时,需要选择它(selected = true),而未选择其他车辆. 我需要为双向数据绑定做什么?因为我要在VehiclesList.vue组件中更改此selected属性,并且还需要在Monit.vue(它是父级)中进行更改,并且'Vehicle.vue'需要监视此属性以获取更改类.

I have this 3 components in VueJS. The problem i want to solve is: When i click at vehicle component, it needs to be selected (selected = true) and other vehicles unselected. What i need to do for two-way data binding? Because i'm changing this selected property in VehiclesList.vue component and it also need to be changed in Monit.vue (which is a parent) and 'Vehicle.vue' need to watch this property for change class.

更新车辆也存在问题.在Monit.vue中,我没有像this.vehicles = response.vehicles那样更新完整的对象,但是我每个对象都做一次,并且仅更改monit属性.

Also problem is with updating vehicles. In Monit.vue i do not update full object like this.vehicles = response.vehicles, but i do each by each one, and changing only monit property.

为此可能更容易使用商店.但是我想在组件中做到这一点.

Maybe easier would be use a store for this. But i want to do this in components.

已数据结构

 {
   "m":[
      {
         "id":"v19",
         "regno":"ATECH DOBLO",
         "dt":"2017-10-09 13:19:01",
         "lon":17.96442604,
         "lat":50.66988373,
         "v":0,
         "th":0,
         "r":0,
         "g":28,
         "s":"3",
         "pow":1
      },
      {
         "id":"v20",
         "regno":"ATECH DUCATO_2",
         "dt":"2017-10-10 01:00:03",
         "lon":17.96442604,
         "lat":50.6698494,
         "v":0,
         "th":0,
         "r":0,
         "g":20,
         "s":"3"
      },
   ]
}

Monit.vue

<template>
  <div class="module-container">
      <div class="module-container-widgets">
        <vehicles-list :vehicles="vehicles"></vehicles-list>
      </div>
  </div>
</template>
<script>
import VehiclesList from '@/components/modules/monit/VehiclesList.vue';

export default {
  name: "Monit",
  data (){
      return {
          vehicles: null
      }
  },
  components: {
    VehiclesList
  },

  methods: {
    getMonitData(opt){
        let self = this;

        if (this.getMonitDataTimer) clearTimeout(this.getMonitDataTimer);

        this.axios({
            url:'/monit',
          })
          .then(res => {
                let data = res.data;
                console.log(data);
                if (!data.err){
                    self.updateVehicles(data.m);
                }

                self.getMonitDataTimer = setTimeout(()=>{
                    self.getMonitData();
                }, self.getMonitDataDelay);

              })
          .catch(error => {

          })
    },
    updateVehicles(data){
        let self = this;
        if (!this.vehicles){
            this.vehicles = {};
            data.forEach((v,id) => {
                self.vehicles[v.id] = {
                    monit: v,
                    no: Object.keys(self.vehicles).length + 1
                }
            });
        } else {
            data.forEach((v,id) => {
                if (self.vehicles[v.id]) {
                    self.vehicles[v.id].monit = v;
                } else {
                    self.vehicles[v.id] = {
                        monit: v,
                        no: Object.keys(self.vehicles).length + 1
                    }
                }
            });
        }
    },
  },
  mounted: function(){
     this.getMonitData();
  }
};
</script>

VehiclesList.vue

<template>
  <div class="vehicles-list" :class="{'vehicles-list--short': isShort}">
      <ul>
          <vehicle 
                v-for="v in vehicles" 
                :key="v.id" 
                :data="v"
                @click.native="select(v)"
          ></vehicle>
      </ul>
  </div>
</template>
<script>
import Vehicle from '@/components/modules/monit/VehiclesListItem.vue';

export default {
    data: function(){
        return {
            isShort: true
        }
    }, 
    props:{
        vehicles: {}
    },
    methods:{
        select(vehicle){
            let id = vehicle.monit.id;
            console.log("Select vehicle: " + id);
            _.forEach((v, id) => {
                v.selected = false;
            });
            this.vehicles[id].selected = true;
        }
    },
    components:{
        Vehicle
    }
}
</script>

Vehicle.vue

<template>
  <li class="vehicle" :id="data.id" :class="classes">
      <div class="vehicle-info">
        <div class="vehicle-info--regno font-weight-bold"><span class="vehicle-info--no">{{data.no}}.</span> {{ data.monit.regno }}</div>
      </div>
      <div class="vehicle-stats">
          <div v-if="data.monit.v !== 'undefined'" class="vehicle-stat--speed" data-name="speed"><i class="mdi mdi-speedometer"></i>{{ data.monit.v }} km/h</div>
      </div>

  </li>
</template>
<script>
export default {
    props:{
        data: Object
    },
    computed:{
        classes (){
           return {
               'vehicle--selected': this.data.selected
           }
        }
    }
}
</script>

推荐答案

在VueJS 2.0中不建议使用双向组件数据绑定,而采用了更多的事件驱动模型:

Two-way component data binding was deprecated in VueJS 2.0 for a more event-driven model: https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow

这意味着,在父级中所做的更改仍将传播到子级组件(单向).您需要在子组件内部进行的更改通过自定义事件显式发送回父组件:

This means, that changes made in the parent are still propagated to the child component (one-way). Changes you make inside the child component need to be explicitly send back to the parent via custom events: https://vuejs.org/v2/guide/components.html#Custom-Events or in 2.3.0+ the sync keyword: https://vuejs.org/v2/guide/components.html#sync-Modifier

EDIT替代方法(可能更好):

Monit.vue:

<template>
  <div class="module-container">
      <div class="module-container-widgets">
        <vehicles-list :vehicles="vehicles" v-on:vehicleSelected="onVehicleSelected"></vehicles-list>
      </div>
  </div>
</template>
<script>
import VehiclesList from '@/components/modules/monit/VehiclesList.vue';

export default {
  name: "Monit",
  data (){
      return {
          vehicles: null
      }
  },
  components: {
    VehiclesList
  },

  methods: {
      onVehicleSelected: function (id) {
          _.forEach((v, id) => {
              v.selected = false;
          });
          this.vehicles[id].selected = true;
      }
      ...other methods
  },
  mounted: function(){
     this.getMonitData();
  }
};
</script>

VehicleList.vue:

methods:{
    select(vehicle){
        this.$emit('vehicleSelected', vehicle.monit.id)
    }
},

原始帖子: 对于您的示例,这可能意味着您需要在select方法内部发出更改,并且需要在VehicleList.vue中使用某种可变对象:

Original post: For your example this would probably mean that you need to emit changes inside the select method and you need to use some sort of mutable object inside the VehicleList.vue:

export default {
    data: function(){
        return {
            isShort: true,
            mutableVehicles: {}
        }
    }, 
    props:{
        vehicles: {}
    },
    methods:{
        select(vehicle){
            let id = vehicle.monit.id;
            console.log("Select vehicle: " + id);
            _.forEach((v, id) => {
                v.selected = false;
            });
            this.mutableVehicles[id].selected = true;
            this.$emit('update:vehicles', this.mutableVehicles);
        },
        vehilcesLoaded () {
            // Call this function from the parent once the data was loaded from the api. 
            // This ensures that we don't overwrite the child data with data from the parent when something changes.
            // But still have the up-to-date data from the api
            this.mutableVehilces = this.vehicles
        }
    },
    components:{
        Vehicle
    }
}

Monit.vue

<template>
  <div class="module-container">
      <div class="module-container-widgets">
        <vehicles-list :vehicles.sync="vehicles"></vehicles-list>
      </div>
  </div>
</template>
<script>

您仍然应该更多地考虑责任. VehicleList.vue组件不应该负责车辆的装载和管理吗?这可能会使思考变得容易一些.

You still should maybe think more about responsibilities. Shouldn't the VehicleList.vue component be responsible for loading and managing the vehicles? This probably would make thinks a bit easier.

尝试$set内部对象,看看是否有帮助:

EDIT 2: Try to $set the inner object and see if this helps:

self.$set(self.vehicles, v.id, {
    monit: v,
    no: Object.keys(self.vehicles).length + 1,
    selected: false
});

这篇关于VueJs观察物体的深层变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆