“This is the second day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”
Hello, 🙎🏻♀️🙋🏻♀ 🙆🏻♀
I love knowledge transfer and am learning to write, ClyingDeng stool!
The problem this paper
This problem, encountered is really very strange.
Here’s the thing: This is actually a requirement for my project. Click more buttons on the right of the navigation, and a dialog box appears. You can drag and drop the navigation list to change the order of the navigation bar. It’s easy to look at, but when the drag is complete, you’ll notice that the navigation bar on the page hasn’t changed at all.
About the project
App parent component
<van-tabs
v-model="active"
swipe-threshold="4"
class="tab"
title-active-color="#3693FF"
color="#3693FF"
title-inactive-color="#4A4A4A"
@click-tab="chooseNav"
>
<van-tab v-for="item in navList" :key="item.name" :name="item.name" :title="item.title">
<component :is="item.component" class="tabPanel"></component>
</van-tab>
</van-tabs>
</div>
<sort-nav
:isSortNav="isSortNav"
:list="navListClone"
@newList="newList"
@noSortshow="noSortshow"
></sort-nav>// When clicking on the upper right corner to trigger the popbox subcomponent, pass 'isSortNav' and 'navListClone' to the subcomponent.Copy the code
Child components
<van-popup
v-model="isSortNav"
round
position="bottom"
:style="{ height: '70%' }"
:overlay-style="{opacity: 0.5}"
:close-on-click-overlay="false"
@click-overlay="noSortNav"
>
<div class="sortContent">
<div class="packUp">
<img src="@/assets/arrowDown.png" alt="" />
</div>
<div class="dataTittle">
<div>All data items</div>
<div class="rightTittle">Long press and drag to the right to sort</div>
</div>
<div class="dataOption">
<vue-draggable
:list="list"
class="list-group"
ghost-class="ghost"
@start="dragging = true"
@end="dragging = false"
>
<div v-for="item in list" :key="item.name" class="list-group-item">
<div class="list-item-left">
<div class="list-item-icon">
<img :src="require('@/assets/drag/' + item.name + '.png')" alt="" />
</div>
<div class="list-item-title">
{{ item.title }}
</div>
<div class="list-item-annotation">
{{ item.annotation }}
</div>
</div>
<div class="list-item-drag">
<van-icon name="wap-nav" />
</div>
</div>
</vue-draggable>
</div>
</div>
</van-popup>
Copy the code
// Click exit to pass in the latest sorted data to the parent component
noSortNav() {
this.$emit('noSortshow',!this.isSortNav)
this.$emit('newList'.this.list)
},
Copy the code
The solution
The project is based on vue2 + vant + vuedraggable.
screening
Component communication
To ensure that the communication between the parent components is successful, print or breakpoint the parameters received and passed by the parent component:
An inspection revealed that there was no problem.
TAB data binding
Next, check that the TAB navigation data is bound successfully. Add data to the NAV navigation to see if the navigation bound data has been successfully dragged and changed. The diagram below:
You can see that the data has changed, but the navigation TAB has not.
So my component and drag use is ok. It could be a vant Tabs navigation component rendering problem.
To solve
Since it is the component that does not refresh bind new data. So we quickly think, let’s now empty the bound data and then assign. Emmm…
Practice has proved that this method is not feasible!
We can then re-render the updated component data by forcing the tabs component to refresh.
Component force rendering can be done in a number of ways:
$forceUpdate
Force the Vue instance to re-render. Note that it affects only the instance itself and the children that insert slot content, not all children.
In this case, using $forceUpdate is not actually effective. This is because the re-rendering is itself and the child component that inserts the contents of the slot. The Tabs component is a built-in slot in vant that doesn’t belong to us.
Render failed!
Change the key
Those of us who have looked inside a VUE know that when a VUE rerenders, it compares its own key to see if it has changed. The idea is to bind a dynamic key property to the tabs component and change the key value after the drag is complete.
Try again:
// The van-tabs component is bound with a key of index, which is manually updated after the parent component returns from the child component.
newList(val) {
this.navList = val
this.index++
},
Copy the code
Ok, it works!
Changing bound data
It doesn’t work at first, but what happens when I finish rendering the DOM? Since we want to change the key, is it feasible to add a nextTick after the drag is completed? After the drag is complete, empty the bound data and add a nextTick inside it to reassign the bound data.
newList(val) {
this.navList = []
this.$nextTick(() = > {
this.navList = val
console.log(this.active)
})
}
Copy the code
Ok, that works too!
TAB circularly binds index to key
If I need to change the key, can I also match the tabs by index? When the data changes
<van-tabs
v-model="active"
swipe-threshold="4"
class="tab"
title-active-color="#3693FF"
color="#3693FF"
title-inactive-color="#4A4A4A"
@click-tab="chooseNav"
>
<van-tab
v-for="(item, index) in navList"
:key="index"
:name="item.name"
:title="item.title"
>
<component :is="item.component" class="tabPanel"></component>
</van-tab>
</van-tabs>
<script>
newList(val) {
this.navList = val
},
</script>
Copy the code
Ok, that works too!
conclusion
The van-tab component internally identifies the (item.name) key of the JSON array navigation data. By default, the key property of the old and new dom-diff nodes is the same. The navigation bar will not be updated because it will be reused if the key value is the same.
Project address: github.com/ClyingDeng/…