Reputation: 21
I have my app.js to generate tabs as below:
Input:
<tabs>
<div>
<a slot="header">Tab 1</a>
<article slot="content">
<h2>Tab 1 Content</h2>
<p>Lorem ipsum dolor sit amet</p>
</article>
</div>
<div>
<a slot="header">Tab 2</a>
<article slot="content">
<h2>Tab 2 Content</h2>
<p>Sed ut perspiciatis unde</p>
</article>
</div>
<div>
<a slot="header">Tab 3</a>
<article slot="content">
<h2>Tab 3 Content</h2>
<p>Neque porro quisquam est</p>
</article>
</div>
</tabs>
Each div
above specifies one tab
. header
slot being title of the tab nav and the content slot is tab content for respective tab.
With the above input to my tabs
component I have to generate below output:
Output:
<section class="tabs">
<nav class="tabs-nav">
<ul class="tabs-list">
<li class="tabs-listitem">
<a class="tabs-trigger">Tab 1</a>
</li>
<li class="tabs-listitem">
<a class="tabs-trigger">Tab 2</a>
</li>
<li class="tabs-listitem">
<a class="tabs-trigger">Tab 3</a>
</li>
</ul>
</nav>
<div class="tabs-body">
<article>
<h2>Tab 1 Content</h2>
<p>Lorem ipsum dolor sit amet</p>
</article>
<article>
<h2>Tab 2 Content</h2>
<p>Sed ut perspiciatis unde</p>
</article>
<article>
<h2>Tab 3 Content</h2>
<p>Lorem ipsum dolor sit amet</p>
</article>
</div>
</section>
With the above input to Tabs how can I get the output as specified above in my render function or Vue template.
If I try to iterate through slots by this.$slots.header
, I am getting undefined
Could someone please help me to achieve this
Upvotes: 0
Views: 75
Reputation: 138216
The $slots
property contains slots that are immediate children, so nested named slots (as you have in your example) would be excluded. In your case, you would only have the default
slot, containing the wrapper div
s (i.e., the tabs). In the render
function, you'd have to manually process the tab's children to get the inner slots in order to generate the HTML you described:
render(h) {
const tabs = this.$slots.default;
const headers = tabs && tabs.map(tab => tab.children.filter(x => x.data.attrs['slot'] === 'header'));
const bodies = tabs && tabs.map(tab => tab.children.filter(x => x.data.attrs['slot'] === 'content'));
if (!tabs || !bodies) {
return h('div', 'No tabs');
}
return h('section', [
h('nav', { class: 'tabs-nav' }, [
h('ul', { class: 'tabs-list' },
headers.map(header => h('li', { class: 'tabs-listitem' }, header)))
]),
h('div', { class: 'tabs-body' }, bodies)
]);
}
Upvotes: 1
Reputation: 349
This is basic loop in Vue. Hope this helps
Vue.component('tabs', {
props: ['tabs'],
template: `
<section class="tabs">
<nav class="tabs-nav">
<ul class="tabs-list">
<li v-for="item in tabs" :key="item" class="tabs-listitem">
<a class="tabs-trigger">{{item.title}}</a>
</li>
</ul>
</nav>
<div class="tabs-body">
<article v-for="item in tabs" v-html="item.content" :key="item"></article>
</div>
</section>`
})
var app = new Vue({
el: '#app',
data () {
return {
tabs: [
{
title: 'Tab 1',
content: '<h1>Tab 1 Content</h1>'
},
{
title: 'Tab 2',
content: '<h1>Tab 2 Content</h1>'
},
{
title: 'Tab 3',
content: '<h1>Tab 3 Content</h1>'
}
]
}
}
})
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id="app">
<tabs :tabs="tabs"></tabs>
</div>
Upvotes: 1