Nesting a slot in a slot for vue

Viewed 6662

Update: Here's a simplified version of what I'm trying to achieve here (from the threaded conversation below):

Accept Component A - Accept Component B - Accept a condition - if condition is true : wrap Component B with Component A [and render]- else only render component B.

I'm interested in creating a component that renders a wrapper conditionally. I figured a theoretical approach like this would probably be best**:**

<template>
    <div>
        <slot v-if="wrapIf" name="wrapper">
            <slot name="content"></slot>
        </slot>

        <slot v-else name="content"></slot>
    </div>
</template>
<script>
    export default {
        props: {
            wrapIf: Boolean,
        }
    }
</script>

Then when we implement, it would look something like this:

...
<wrapper-if :wrap-if="!!link">
    <a :href="link" slot="wrapper"><slot></slot></a>

    <template slot="content">
         content
    </template>
</wrapper-if>

The idea being that, in this case, if there is a link, then let's wrap the content with the wrapper slot (which can be any component/element). If there isn't, then let's just render the content without the wrapped link. Pretty simple logic, but it seems that I'm misunderstanding some basic vue functionality because this particular example does not work.

What is wrong with my code or is there some kind of native api that already achieves this or perhaps a dependency that does this sort of thing already?

The output should look like this:

wrapIf === true

<a href="some.link">
    content
</a>

wrapIf === false

content
1 Answers

Just focus on the content itself, and let the component worry about whether or not to wrap the default or named content slot.

If you need the wrapper to be dynamic, a dynamic component should solve that. I've updated my solution accordingly. So if you need the wrapper to be a label element, just set the tag property to it, and so on and so forth.

const WrapperIf = Vue.extend({
  template: `
    <div>
      <component :is="tag" v-if="wrapIf" class="wrapper">
        <slot name="content"></slot>
      </component>

      <slot v-else name="content"></slot>
    </div>
  `,

  props: ['wrapIf', 'tag']
});


new Vue({
  el: '#app',

  data() {
    return {
      link: 'https://stackoverflow.com/company',

      tagList: ['p', 'label'],
      tag: 'p',
      wrap: true
    }
  },

  components: {
    WrapperIf
  }
})
.wrapper {
  display: block;
  padding: 10px;
}

p.wrapper {
  background-color: lightgray;
}

label.wrapper {
  background-color: lavender;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <wrapper-if :wrap-if="wrap" :tag="tag">
    <a :href="link" slot="content">
       content
    </a>
  </wrapper-if>

  <div>
    Change wrapper type:
    <select v-model="tag">
      <option v-for="tag in tagList">{{tag}}</option>
    </select>
  </div>

  <button @click="wrap = !wrap">Toggle wrapper</button>
</div>

Related