Search code examples
javascriptvuejs2vue-cli

How to use slots in the HTML with Single File Components


I want to use slots in Vue to create a dynamic modal component.

I already tried a lot of tutorials of Vue / slots, but none of them is exactly what i'm looking for.

This is a piece of my modal.vue:

<template>
  ...
    <slot name="modal-body"></slot>
  ...
</template>
<script>
</script>
<style>
</style>

This is my javascript compiled file:

import Vue from 'vue';
import modal from './modal.vue';

new Vue({
  el: '#modal',
  render: r => r(modal)
});

This is piece of my HTML file:

...
<div id="modal">
  <template v-slot="modal-body">
    <input type="text" id="dynamic-input">
  </template>
</div>
...

I was expecting that all elements present inside #modal (#dynamic-input in this case), were inserted into the slot named modal-body, inside my Vue element. Is it possible to do it? Am i missing something?


Solution

  • Check what version of Vue you are using. The named slot syntax changed in 2.6.0. Consider the differences below. One uses render functions and the other template Strings.

    Vue@2.6.10

    // Example using template String
    const modalTemplateString = {
      template: "<div><div>above</div><slot name=\"modal-body\"></slot><div>below</div></div>"
    };
    
    const appTemplateString = new Vue({
      el: "#appTemplateString",
      components: {
        modal: modalTemplateString
      },
      template: "<div><modal><template v-slot:modal-body><div>foobar</div></template></modal></div>"
    });
    
    // Example using render function
    const modalRenderFunc = {
      render(h) {
        return h("div", [
          h("div", "above"),
          h("div", this.$slots["modal-body"]),
          h("div", "below")
        ]);
      }
    }
    
    const appRenderFunc = new Vue({
      el: "#appRenderFunc",
      components: {
        modal: modalRenderFunc
      },
      render(h) {
        return h("div", [
          h("modal", [
            h("div", {
              slot: "modal-body"
            }, "foobar")
          ])
        ]);
      }
    });
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
    
    <h2>Template String</h2>
    <div id="appTemplateString"></div>
    <hr/>
    <h2>Render Function</h2>
    <div id="appRenderFunc"></div>

    Vue@2.5.22

    // Example using template String
    const modalTemplateString = {
      template: "<div><div>above</div><slot name=\"modal-body\"></slot><div>below</div></div>"
    };
    
    const appTemplateString = new Vue({
      el: "#appTemplateString",
      components: {
        modal: modalTemplateString
      },
      template: "<div><modal><template slot=\"modal-body\"><div>foobar</div></template></modal></div>"
    });
    
    // Example using render function
    const modalRenderFunc = {
      render(h) {
        return h("div", [
          h("div", "above"),
          h("div", this.$slots["modal-body"]),
          h("div", "below")
        ]);
      }
    }
    
    const appRenderFunc = new Vue({
      el: "#appRenderFunc",
      components: {
        modal: modalRenderFunc
      },
      render(h) {
        return h("div", [
          h("modal", [
            h("div", {
              slot: "modal-body"
            }, "foobar")
          ])
        ]);
      }
    });
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>
    
    <h2>Template String</h2>
    <div id="appTemplateString"></div>
    <hr/>
    <h2>Render Function</h2>
    <div id="appRenderFunc"></div>