Tabs don't update when array order changes
See original GitHub issueDescribe the bug
<b-tabs>
doesn’t update reliably when the array changes.
- If the number of elements changes, the update seems to work.
- If the order of elements changes, it doesn’t seem to update.
#1781 is (almost) a duplicate of this. In that issue, the user used a
<b-tab v-for="tab in tabs" :title="tab.title" :key="`key-${tab.id}`">
The bug was closed when @emanuelmutschlechner says:
You must use the index of tabs array. By using the id as key, shuffling the tabs array will not change the underlying HTML at all.
As in:
<b-tab v-for="(tab, index) in tabs" :title="tab.title" :key="`key-${index}`">
I think that reasoning is wrong. Forcing me to use index as key means that something is broken.
Superficially it seems to work. But as the Vue documentation points out:
This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM state (e.g. form input values).
(highlight theirs)
So if the different tabs have e.g. <input>
s then Vue will get confused. That is exactly what key
is for. If you use index as key, you might as well omit it altogether as using the index is the default behavior
The Vue documentation explicitly uses item.id
in several places. I don’t find any index
used for key anywhere.
And using :key="item.id"
, <b-tabs>
doesn’t update when the sequence changes.
In my opinion, there is a bug here. :key="item.id"
should work.
Steps to reproduce the bug
See this codepen. It has inputs on the tabs and you can see that the inputs end up in the wrong tabs when the array order changes.
Expected behavior
I expect:
<b-tab v-for="tab in tabs" :title="tab.title" :key="`key-${tab.id}`">
to work whenever tabs changes - also if just the order changes.
Versions
Libraries:
- BootstrapVue: 2.0.0-rc.22
- Bootstrap: 4.3.1
- Vue: 2.6.10
Demo link
See this codepen
Issue Analytics
- State:
- Created 4 years ago
- Comments:27 (18 by maintainers)
@tmorehouse: I just built dev from the current git repo and hosted it myself somewhere and it seems to work now. What made it work was commit 130f8ffe85305a4f6eb9f38e199562edeabf72ea.
I now get it.
<b-tabs>
is letting Vue do the ordering of the<b-tab>
instances that contain the tab content in the default slot. And they’re ordered correctly because of thekey
on<b-tab>
. But BootstrapVue isn’t involved with any of that. In tabs.js, this.getTabs() aligns the order of the buttons with DOM order of tabs previously set up by Vue in the default slot.What I was struggling with was that searching for
key
insrc/components/tabs/*
turned up so precious little, as I was assuming (erroneously), that somehow the key attribute needed to be passed on somewhere. So I was thinking “the key attribute I supply isn’t consumed/handled anywhere”. But no. default slot and Vue takes care of all that perfectly. Once that is done, BTabs/tabs.js just mirrors the DOM order in getTabs().The fact that the order of buttons was wrong led me to believe that the
key
attribute “wasn’t working”. What I now understand is that thekey
attribute the entire time correctly determined the order inside.tab-content
, but that theul.nav-tabs
didn’t reliably mirror that (correct) DOM ordering.And so now with 130f8ffe85305a4f6eb9f38e199562edeabf72ea it (looks like) it observes/watches
this.$refs.tabsContainer
and callsthis.updateTabs.bind(this)
when that changes. (Essentially the same fix as @emanuelmutschlechner put in manually.)My faith in the universe is restored.
I hope I have not done irreparable damage here. I apologize for being too stubborn and just generally a pain the behind. I agree it looks like this will be fixed in 2.0.0-rc.24.
BootstrapVue 2.0.0-rc.24 has been published about an hour ago: https://www.npmjs.com/package/bootstrap-vue