Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic Components with Slots

Tags:

vue.js

vuejs2

How can I use named slots from dynamic components in the parent component?

A slider component takes an array of dynamic slide components:

<slider :slides="slides" />

Each slide has named slots with content to be using by the slider:

<template>
<div class="slide">
  <div slot="main">Slide 1 Main</div>
  <div slot="meta">Slide 1 Meta</div>
</div>
</template>

The slider should now use these slots, like so:

<template>
<div class="slider">
  <div class="slider__slide" v-for="slide in slides">
    <component :is="slide">
      <div class="slider__slide__main">
        <slot name="main" /><!-- show content from child's slot "main" -->
      </div>
      <div class="slider__slide__meta">
        <slot name="meta" /><!-- show content from child's slot "meta" -->
      </div>
    </component>
  </div>
</div>
</template>

But <component> ignores its inner content, so the slots are ignored.

Example:
https://codepen.io/anon/pen/WZjENK?editors=1010

If this isn't possible, is there another way to create a slider that takes HTML content from slide components without caring about their content?

like image 377
bernhardw Avatar asked Oct 17 '22 05:10

bernhardw


1 Answers

By splitting the main/meta sections into their own components, you can relatively easily use a render function to split them into the sections you want.

console.clear()

const slide1Meta = {
  template:`<div>Slide 1 Meta</div>` 
}
const slide1Main = {
  template: `<div>Slide 1 Main</div>`
}
const slide2Meta = {
  template:`<div>Slide 2 Meta</div>` 
}
const slide2Main = {
  template: `<div>Slide 2 Main</div>`
}

Vue.component('slider', {
  props: {
    slides: {
      type: Array,
      required: true
    }    
  },
  render(h){
    let children = this.slides.map(slide => {
      let main = h('div', {class: "slider__slide__main"}, [h(slide.main)])
      let meta = h('div', {class: "slider_slide_meta"}, [h(slide.meta)])
      return h('div', {class: "slider__slide"}, [main, meta])
    })
    return h('div', {class: "slider"}, children)
  }
});


new Vue({
  el: '#app',
  data: {
    slides: [
      {meta: slide1Meta, main: slide1Main}, 
      {meta: slide1Meta, main: slide2Main}
    ]
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
  <slider :slides="slides" />
</div>

<script type="text/x-template" id="slide1-template">
  <div class="slide">
    <div slot="main">Slide 1 Main</div>
    <div slot="meta">Slide 1 Meta</div>
  </div>
</script>

<script type="text/x-template" id="slide2-template">
  <div class="slide">
    <div slot="main">Slide 2 Main</div>
    <div slot="meta">Slide 2 Meta</div>
  </div>
</script>
like image 104
Bert Avatar answered Oct 21 '22 05:10

Bert