ArtemD
ArtemD

Reputation: 61

Vue 3: access VueComponent object placed in slots

I'm working on tab component and I want to render tab labels in parent component by getting child's slot, named 'label' In Vue 2.x I could approach that, by referring to $slots property of tab component, in Tabs.vue:

<template>
  <section class="tabs">
    <ul class="tabs-labels">
      <li
        v-for="tab in tabs"
        :key="tab._uid"
        :class="[{'active': tab.isActive}, 'tab-label']"
        @click="selectTab(tab);"
      >
        {{ tab.$slots.label }}
      </li>
    </ul>

    <div class="tabs-content">
      <slot/>
    </div>
  </section>
</template>

<script>
  export default {
    name: 'Tabs',

    data () {
      return {
        tabs: [],
      };
    },

    mounted () {
      // filter tabs in case there were additional vue components placed in slots
      this.tabs = this.$children.filter(tab => tab.$options.name === 'TabContent');
    },

    methods: {
      selectTab (selectedTab) {
         // set isActive property of the tab by comparing their uids
        this.tabs.forEach(tab => {
          tab.isActive = (tab._uid === selectedTab._uid);
        });
      },
    },
  };
</script>

TabsContent.vue:

<template>
  <div v-show="isActive" class="single-tab-content">
    <slot/>
  </div>
</template>

<script>
  export default {
    name: 'TabContent',
    data () {
      return {
        isActive: false
      };
    },
  };
</script>

Here, when the tab label clicked, in Tabs.vue I iterate through tabs array and setting their isActive property, comparing their uid and uid of selectedTab

But in Vue 3.x API of slots has changed, so I changed the way of getting tab contents:

from

this.tabs = this.$children.filter(tab => tab.$options.name === 'TabContent');

to

this.tabs = this.$slots.default().filter(tab => tab.type.name === 'TabContent');

but as I understand, it getting only vNodes, not actual VueComponent that rendered, so when I'm executing selectTab method

tab.isActive = (tab._uid === selectedTab._uid);

it updates only isActive properties for tabs, that were saved in tabs array, not for actual tab contents, so v-show never changes.

Is there any way to get actual rendered VueComponents from <slots>? Or maybe this approach is wrong from the beginning and I should try something else?

Edit

CodeSandboxes for both versions:

Upvotes: 0

Views: 1849

Answers (1)

Michael Mano
Michael Mano

Reputation: 3440

Its a bit more complicated with Vue 3. You will want to look into using provide and inject. here is a good example. https://gist.github.com/cathrinevaage/4eed410b31826ce390153d6834909436

sandbox - https://codesandbox.io/s/happy-rubin-z414h?file=/src/App.vue

The example above is using typescript however you get the idea.

Upvotes: 1

Related Questions