Molfar
Molfar

Reputation: 1491

Provide slots to Vue instance

Is there any way to provide html for Vue instance? With default, named and scoped slots? Just like with components, but instead to Vue instance.

I've create a simple demo here https://codesandbox.io/s/frosty-pine-v9dkn

main.js

const compTemplate =
  '<p>main content</p><template v-slot:header="coolName">{{ coolName }}</template><template v-slot:footer>footer contnet</template>';

// How to send compTemplate to App?
new Vue({
  render: (h) => h(App)
}).$mount("#app");

App.vue

<template>
  <div id="app">
    <header><slot name="header" :coolName="coolName"></slot></header>

    <slot></slot>

    <footer><slot name="footer"></slot></footer>
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld";

export default {
  name: "App",
  data() {
    return {
      coolName: "Some Cool Name",
    };
  },
  components: {
    HelloWorld,
  },
};
</script>

Upvotes: 0

Views: 1040

Answers (1)

muka.gergely
muka.gergely

Reputation: 8329

If you put HTML in a slot, then it's going to appear as plain text. You might have some close options though, depending on your use-case:

1. Provide the HTML as a simple component:

Vue.component('SimpleComponent', {
  template: `
    <div
      style="font-weight: 700"
    >
      I'm formatted with HTML.
    </div>
  `
})

Vue.component('SlotContainer', {
  template: `
    <div>
      <slot name="header"></slot><br />
      <slot></slot><br />
      <slot name="footer"></slot>
    </div>
  `
})

new Vue({
  el: "#app",
  template: `
    <slot-container>
      <template slot="header">
        header
      </template>
      <template slot="default">
        <simple-component></simple-component>
      </template>
      <template slot="footer">
        footer
      </template>
    </slot-container>
  `
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

2. Provide the HTML as a prop, not a slot:

Vue.component('PropsContainer', {
  props: ['header', 'body', 'footer'],
  template: `
    <div>
      <header v-html="header"></header><br />
      <div v-html="body"></div><br />
      <footer v-html="footer"></footer>
    </div>
  `
})

new Vue({
  el: "#app",
  data() {
    return {
      bodyHTML: "<strong>I'm formatted with HTML</strong>"
    }
  },
  template: `
    <props-container
      :header="'header'"
      :body="bodyHTML"
      :footer="'footer'"
    >
    </props-container>
  `
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

This one would use v-html, that requires extra attention because of security issues (especially, if you want to put out user input).

3. Compile the template on the fly

What you provided as an example is NOT a simple HTML string, it's a Vue template:

const compTemplate = '<p>main content</p><template v-slot:header="coolName">{{ coolName }}</template><template v-slot:footer>footer contnet</template>'

If you want that, then you might need a more advanced approach, you need to compile these templates runtime.

const compTemplate =
  `
<div>
  <p>main content</p>
  <template v-slot:header="coolName">{{ coolName }} header content</template><br />
  <template v-slot:footer>footer content</template>
</div>
`

Vue.component('RenderComponent', {
  props: ['html'],
  render(h) {
    return h({
      template: `<div>${ this.html }</div>`
    })
  }
})

new Vue({
  el: "#app",
  data() {
    return {
      compTemplate
    }
  },
  template: `
    <render-component
      :html="compTemplate"
    >
    </render-component>
  `
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Upvotes: 2

Related Questions