I am Working on creating a tab component with vue and noticed the following behavior.
If I create my component like this:
<tabs-component>
<tab-component :title="1">
<h1>1</h1>
<p>Lorem ipsum dolor sit amet.</p>
</tab-component>
<tab-component :title="2">
<h1>2</h1>
<p>Lorem ipsum dolor sit amet.</p>
</tab-component>
</tabs-component>
Life hooks are triggered in this order:
- Tabs component created
- Tab component created
- Tab component mounted
- Tabs component mounted
Which seems to be the correct behavior however, if instead of declaring the slots manually I use a v-for
directive assuming I am dealing with some data from a service like this:
<tabs-component>
<tab-component v-for="(type, index) in beerTypes" :key="type.slug" :title="type.slug">
<h1>{{ index }}</h1>
<p>Lorem ipsum dolor sit amet.</p>
</tab-component>
</tabs-component>
Life hooks are triggered in this order:
- Tabs component created
- Tabs component mounted
- Tab component created
- Tab component mounted
The v-for
directive seems to be messing with the order life hooks are triggered. And this is causing me trouble since I am firing the selectTab
method on the mounted life hook to pick the default tab selected, but when is triggered tabs have not been mounted.
Does any one know how I can correctly listen for the events in the right order, or wait for the child components to be ready so I can pick the default panel?
Here is the rest of my code for reference:
App.vue
<template>
<main>
<h1>Discover beer styles</h1>
<tabs-component>
<tab-component v-for="(type, index) in beerTypes" :key="type.slug" :title="type.slug">
<h1>{{ index }}</h1>
<p>Lorem ipsum dolor sit amet.</p>
</tab-component>
</tabs-component>
</main>
</template>
<script>
import axios from 'axios';
import TabsComponent from './components/tabs/Tabs.vue';
import TabComponent from './components/tabs/Tab.vue';
export default {
components: {
'tabs-component': TabsComponent,
'tab-component': TabComponent,
},
data() {
return {
beerTypes: null,
selectedType: null
}
},
filters: {
capitalize: function(value) {
if (!value) return '';
return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
}
},
mounted() {
axios.get('/data/data.json')
.then((response) => {
this.beerTypes = response.data;
this.selectedType = this.beerTypes[0].slug;
})
.catch((error) => console.log(error));
},
methods: {
selectActiveType: function(selected) {
this.selectedType = selected;
}
}
}
</script>
Tabs.vue
<template>
<div class="tabs-component">
<div>
<ul>
<li v-for="(tab, index) in tabs" :class="{'is-active': tab.isActive}" :key="index">
<a href="#" @click="selectTab(index)">{{ tab.title }}</a>
</li>
</ul>
</div>
<div>
<slot/>
</div>
</div>
</template>
<script>
export default {
data() {
return {
tabs: [],
activeTabIndex: 0
}
},
created() {
this.tabs = this.$children;
},
mounted() {
this.selectTab(this.activeTabIndex);
},
methods: {
selectTab(index) {
this.tabs.forEach(function(tab) {
tab.isActive = (this.tabs.indexOf(tab) === index);
}.bind(this));
}
}
};
</script>
Tab.vue
<template>
<section v-show="isActive">
<slot/>
</section>
</template>
I appreciate if someone could take the time to help me.
Thanks!