Я знакомлюсь с Vue.js (версия 2.5.16), но столкнулся с проблемой понимания того, что выглядит как проблема с реактивностью:
У меня есть два списка компонентов с циклами v-for
, которые большую часть времени отображаются корректно — я могу взаимодействовать с компонентами и добавлять новые компоненты. Компоненты взаимодействуют с REST API, представляющим «зоны», и чья функция, как я надеюсь, довольно очевидна — в основном она возвращает JSON-представления либо одной, либо всех «зон», например:
{
"state": "off",
"pin": 24,
"uri": "/zones/6",
"name": "extra"
}
Всякий раз, когда зона добавляется или удаляется, приложение перезагружает весь список зон. Однако по какой-то причине, когда я удаляю зону и запускаю перезагрузку зон, список отображается с неправильными данными! Независимо от того, какую зону я удаляю, последний элемент списка кажется «выскочившим», а не ожидаемой зоной, которая не отображается в новом списке! Когда я проверяю данные app.zones, в данных Javascript все выглядит правильно, но они просто не отображаются прямо в браузере.
Вот соответствующий код:
...
<div class="tab-content">
<div id="control" class="tab-pane fade in active">
<ul>
<zone-control
v-for="zone in zones"
v-bind:init-zone="zone"
v-bind:key="zone.id">
</zone-control>
</ul>
</div>
<div id="setup" class="tab-pane fade">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Pin</th>
<th></th>
</tr>
</thead>
<tbody>
<tr is="zone-setup"
v-for="zone in zones"
v-bind:init-zone="zone"
v-bind:key="zone.id">
</tr>
<tr is="zone-add"></tr>
</tbody>
</table>
</div>
</div>
...
<script>
Vue.component('zone-control', {
props: ['initZone'],
template:
`<li>
<div class="btn btn-default" v-on:click="toggleState">
<span v-if="zone.state == 'on'" class="glyphicon glyphicon-ok-circle text-success"></span>
<span v-else class="glyphicon glyphicon-remove-sign text-danger"></span>
Zone: {{ zone.name }}
</div>
</li>`,
data: function() {
return {
zone: this.initZone
};
},
methods: {
toggleState: function() {
var state = (this.zone.state == 'on' ? 'off' : 'on');
console.log('Turning zone ' + this.zone.name + ' ' + state);
var comp = this
fetch(
this.zone.uri,
{method: 'PUT',
headers: new Headers({
'Content-Type': 'application/json'
}),
body: JSON.stringify({state: state})
}).then( function(result) {
return result.json()
}).then( function(data) {
comp.zone = data;
});
}
}
})
Vue.component('zone-setup', {
props: ['initZone'],
template:
`<tr>
<td>{{ zone.name }}</td>
<td>{{ zone.pin }}</td>
<td><div v-on:click="deleteZone" class="btn btn-danger btn-small"></div></td>
</tr>`,
data: function() {
return {
zone: this.initZone
};
},
methods: {
deleteZone: function() {
fetch(
this.zone.uri,
{method: 'DELETE'}
).then( function(result) {
app.load_zones();
});
}
}
})
Vue.component('zone-add', {
template:
`<tr>
<td><input v-model="zone.name" type="text" class="form-control"></input></td>
<td><input v-model="zone.pin" type="text" class="form-control"></input></td>
<td><div v-on:click="addZone" class="btn btn-success btn-small"></div></td>
</tr>`,
data: function() {
return {
zone: {
name: '',
pin: ''
}
};
},
methods: {
addZone: function() {
console.log('Adding zone ' + this.zone.name);
var comp = this
fetch(
"/zones",
{
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json'
}),
body: JSON.stringify({
name: comp.zone.name,
pin: comp.zone.pin})
}
).then( function(result) {
app.load_zones()
comp.zone = {}
});
}
}
})
var app = new Vue({
el: '#app',
data: {
zones: []
},
methods: {
load_zones: function() {
fetch("zones")
.then(function(result){
return result.json()
}).then(function(data){
app.zones = data;
});
}
},
created: function() {
this.load_zones();
}
})
</script>
Я прочитал несколько ссылок, и, похоже, есть некоторые пограничные случаи или другие «ошибки», но это, похоже, не попадает ни в одно из них. Например, другой вопрос, по-видимому, согласен с тем, что замена Весь массив — довольно надежный способ изменения массивов в рамках ограничений системы реактивности Vue.js.
Некоторые другие ссылки на документацию Vue.js, которые я проверил и которые имеют отношение к моей проблеме:
Полный код в контексте см. в репозитории github здесь.
ОБНОВИТЬ
Согласно комментариям, я воспроизвел симптомы в упрощенном JSBin. Нажмите на любую кнопку, чтобы увидеть, что при удалении второго элемента вместо него удаляется последний!
Vue.set(app, zones, data);
вместоapp.zones = data
? - person Randy Casburn   schedule 19.04.2018Vue.set(app, 'zones', data);
. Никаких изменений в поведении. - person Justin Frahm   schedule 19.04.2018