VueJs观察物体的深层变化 [英] VueJs watching deep changes in object
问题描述
我在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
这意味着,在父级中所做的更改仍将传播到子级组件(单向).您需要在子组件内部进行的更改通过自定义事件显式发送回父组件: https://vuejs.org/v2/guide/components.html#sync-Modifier
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屋!