How to change VueJS's <slot> content before component creation

Viewed 2153

I have a VueJS component,

comp.vue:

<template>
  <div>
    <slot></slot>
  </div>
</template>

<script>
  export default {
    data () {
      return {

      }
    },
  }
</script>

And I call this Vue component just like any other component:

...
<comp>as a title</comp>
<comp>as a paragraph</comp>
...

I would like to change comp.vue's slot before it is rendered so that if the slot contains the word "title" then the slot will be enclosed into an <h1>, resulting in

<h1>as a title</h1>

And if the slot contains "paragraph" then the slot will be enclosed in <p>, resulting in

<p>as a paragraph</p>

How do I change the component slot content before it is rendered?

2 Answers

This is easier to achieve if you use a string prop instead of a slot, but then using the component in a template can become messy if the content is long.

If you write the render function by hand then you have more control over how the component should be rendered:

export default {
  render(h) {
    const slot = this.$slots.default[0]

    return /title/i.test(slot.text)
      ? h('h1', [slot])
      : /paragraph/i.test(slot.text)
        ? h('p', [slot])
        : slot
  }
}

The above render function only works provided that the default slot has only one text child (I don't know what your requirements are outside of what was presented in the question).

You can use $slots(https://v2.vuejs.org/v2/api/#vm-slots):

export default {
    methods: {
        changeSlotStructure() {
            let slot = this.$slots.default;
            slot.map((x, i) => {
                if(x.text.includes('title')) {
                    this.$slots.default[i].tag = "h1"
                } else if(x.text.includes('paragraph')) {
                    this.$slots.default[i].tag = "p"
                }
            })
        }
    },
    created() {
        this.changeSlotStructure()
    }
}
Related