3
votes

I want my container component to render contents based on child's slot names. Here's an example markup:

<div id="app">
    <Container>
        <component :is="childComponent"></component>
    </Container>
</div>
<script>
import Container from './Container'
import someComponent from './someComponent'
export default {
    components: {
         Container,
         someComponent
    },
    data() {
        childComponent: 'someComponent'
    }
}
</script>

// Container.vue
<template>
    <div>
         <header>
             <slot name="head"></slot>
         </header>
         <div class="ContainerBody">
             <slot name="body"></slot>
         </div>
         <footer>
             <slot name="footer"></slot>
         </footer>
    </div>
</template>

// Some child component
<template>
    <div>
        <h1 slot="head">Child Title</h1>
        <div slot="body" class="body"><div>Child Body</div></div>
        <footer slot="footer">Child Footer</footer>
    </div>
</template>

How do I make it that Vue respects slot names and renders child contents in accordingly named slots, so the result would look like this:

<div>
     <header>
         Child Title
     </header>
     <div class="ContainerBody">
         <div>Child Body</div>
     </div>
     <footer>
         Child Footer
     </footer>
</div>

Right now it will only render my child component in an unnamed slot:

<slot></slot>

The idea is to render child component differently when it's loaded as a child of Container. I would like it to work with .Vue files and allow child components to still behave as usual when they're not a child of a container.

1

1 Answers

2
votes

I don't think you can do exactly what you want because each component has to have a single root element, which precludes it being plugged in as three separate slots.

What I was able to do was to turn the problem inside-out, making the top-level component the childComponent and having it take a container prop which it uses to set the :is of its root element.

// "importing" async component definitions
const vueContainerComponent = () => new Promise((resolve) => resolve({
  template: '#container-template'
}));

const vueChildComponent = () => new Promise((resolve) => resolve({
  template: '#child-template',
  props: ['container']
}));

new Vue({
  el: '#app',
  components: {
    someComponent:() => vueChildComponent().then((spec) => ({
      extends: spec,
      components: {
        Container: vueContainerComponent
      }
    }))
  },
  data: {
    container: 'Container',
    childComponent: 'someComponent'
  }
});
header {
  background-color: lightgray;
}

footer {
  background-color: darkslategray;
  color: white;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
  <component :is="childComponent" :container="container"></component>
</div>

<template id="container-template">
  <div>
    <header>
      <slot name="head"></slot>
    </header>
    <div class="ContainerBody">
      <slot name="body"></slot>
    </div>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

<template id="child-template">
  <div :is="container">
    <h1 slot="head">Child Title</h1>
    <div slot="body" class="body"><div>Child Body</div></div>
    <footer slot="footer">Child Footer</footer>
  </div>
</template>