我有好几个手风琴(每个手风琴都是一个Vue组件),默认情况下会展开它们。还有一个“复制”功能,可以复制每个组件。
Vue.component("Accordion", {
template: "#accordion-template",
data: function () {
return {
open: true
}
},
methods: {
toggle: function () {
this.open = !this.open;
}
}
});
new Vue({
el: '#vue-root',
data: {
devices: [
{
name: "a", description: "first"
},
{
name: "b", description: "second"
},
{
name: "c", description: "third"
}
]
},
methods: {
copy: function (device) {
var index = this.devices.indexOf(device) + 1;
var copy = {
name: device.name + "_copy",
description: device.description + "_copy"
};
this.devices.splice(index, 0, copy);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.js"></script>
<div id="vue-root">
<div class="device" v-for="device in devices">
<accordion>
<div slot="acc-head">
<span>{{ device.name }}</span><br/>
<button @click="copy(device)">copy</button>
</div>
<div slot="acc-body">
{{ device.description }}
</div>
</accordion>
</div>
</div>
<script type="text/x-template" id="accordion-template">
<div>
<slot name="acc-head"></slot>
<button @click="toggle">Open: {{ open }}</button>
<div :class="open ? 'active' : 'hidden'">
<slot name="acc-body"></slot>
</div>
</div>
</script>
当所有手风琴都折叠起来(换句话说,“ open:false”)并且我尝试从列表中间复制一个手风琴(例如b)时,我希望出现一个名为“ name”的新组件_copy,它必须默认扩展。但是,与此相反,新组件的所有属性值都与被复制的组件相同,并且列表中的最后一个组件被扩展。
我该如何解决这个问题?
答案 0 :(得分:3)
使用Vue和列表时,应使用v-for向元素添加key prop。使用这样的键,让Vue知道您的意思是特定元素。
<div class="device" v-for="device in devices" :key="device.name">
我相信原因是由于性能原因,Vue默认情况下将新元素添加为最后一个元素,然后在其他节点中更新数据。因此,您添加的新元素实际上是列表中将open设置为true的最后一个元素。
答案 1 :(得分:2)
在您的v-for
循环中添加一个密钥:v-for="device in devices" :key="{something here}"
。您的密钥必须是唯一的,并且即使在复制设备之后也要标识每台设备
请检查:https://jsfiddle.net/Al_un/9cradxvp/。出于调试目的,我更改了几件事:
device
设为props
的{{1}},以便可以使用<accordion>
中的设备属性console.log
发出复制设备。 Vue doc on listening to child component events <accordion>
和mounted()
钩子。有关Lifecycle hooks 如果updated()
循环中未提供密钥,则Vue不知道如何更新列表。来自Vue documentation:
要给Vue一个提示,使其可以跟踪每个节点的身份,从而重用和重新排列现有元素,您需要为每个项目提供唯一的key属性。
让我们考虑一下您的列表(我添加了一个元素)
v-for
现在,让我们复制节点“ b”。如果没有[
{id: 1, name: "a"},
{id: 2, name: "b"},
{id: 3, name: "c"},
{id: 4, name: "d"},
]
,则控制台输出为
:key="device.id"
对于4: d is mounted
3: c is updated
5: b_copy is updated
,控制台输出仅为:
:key="device.id"
基本上,没有按键,有:
5: b_copy is mounted
变成c
,b_copy
变成d
c
已创建因此,每次进行复制时都会重新创建最后一个元素。由于d
的默认值为open
,因此显然true
具有d
。
如果每个元素都有一个open = true
,则仅创建元素:key="device.id"
要进行检查,请从我的小提琴中删除b_copy
,然后查看控制台中发生的情况
由于密钥必须唯一地标识您的设备,因此您不应在复制设备时使用index作为密钥,因为阵列中的设备索引会随时更改
此外,最好使用ID字段,因为不能保证设备名称唯一。如果您使用
初始化列表怎么办:key="device.id"
从功能的角度来看,这是正确的。
答案 2 :(得分:0)
代码的一些补充:
您可以直接传递索引,而不是提供“设备”对象并搜索其索引。
这是我的意思:jsFiddle
Vue.component("Accordion", {
template: "#accordion-template",
data: function() {
return {
open: true
}
},
methods: {
toggle: function() {
this.open = !this.open;
}
}
});
new Vue({
el: '#vue-root',
data: {
devices: [{
name: "a",
description: "first"
},
{
name: "b",
description: "second"
},
{
name: "c",
description: "third"
},
]
},
methods: {
copy: function(index) {
var device = this.devices[index];
var copy = {
name: device.name + "_copy",
description: device.description + "_copy"
};
this.devices.splice(index + 1, 0, copy);
},
remove: function(index) {
this.devices.splice(index, 1);
}
}
});
.device {
margin: 10px 0;
}
.active {
display: block;
}
.hidden {
display: none;
}
div.device {
border: 1px solid #000000;
box-shadow: 1px 1px 2px 1px #a3a3a3;
width: 300px;
padding: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="vue-root">
<div class="device" v-for="(device, index) in devices" :key="device.name">
<accordion>
<div slot="acc-head">
<span>{{ device.name }}</span><br/>
<button @click="copy(index)">copy</button>
<button @click="remove(index)">remove</button>
</div>
<div slot="acc-body">
{{ device.description }}
</div>
</accordion>
</div>
</div>
<script type="text/x-template" id="accordion-template">
<div>
<slot name="acc-head"></slot>
<button @click="toggle">Open: {{ open }}</button>
<div :class="open ? 'active' : 'hidden'">
<slot name="acc-body"></slot>
</div>
</div>
</script>