DEV Community

Cover image for Solving this.$children removal in Vue3 using provide/inject mechanism
E.R. Nurwijayadi
E.R. Nurwijayadi

Posted on

Solving this.$children removal in Vue3 using provide/inject mechanism

This post was originally posted on my personal blog 😀. The source code example also provided in github, given in that blog.

🕷 Tabs - JS - Vue3 Composition

I have been learning vue2 recently and make my own tabs component in with vue CLI. In part of my learning process, I also migrate to vue3 using composition API.

Goal

I need to pass all tabs data in parent component, along with the children data, to all child components.

In order to select tab in click event, all the children data should be available in all child components,

The Children Issue

My vue2 design pattern is using slot:

<template>
   <tabs>
      <tab name="home" title="Home" color="bg-blue-500">
        <h3>Home</h3>
        <p>Lorem ipsum dolor sit amet,
           consectetur adipiscing elit.
           Quisque in faucibus magna.</p>
      </tab>
      …
  </tabs>
</template>
Enter fullscreen mode Exit fullscreen mode

In children component, the data above will be processed with v-for:

<template>
  <main class="tabs">
    <div class="tab-headers">
      <div
         v-for="tab in tabs" v-bind:key="tab.name"
         v-on:click="selectTabByName(tab.name)"
         v-bind:class="[activeClass(tab), colorClass(tab)]"
        >{{ tab.title }}
      </div>
    </div>

    <div class="tab-spacer"></div>

    <div class="tab-contents">
      <slot></slot>
    </div>
  </main>
</template>
Enter fullscreen mode Exit fullscreen mode

The children component is using this line of code.

export default {
  name: 'TabListSimple',
  data() {
    return {
      selected: 'news',
      tabs: this.$children
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

As you might have known, the this.$children has already been deprecated in vue3. There is no good example,on how to migrate this using the design pattern above.

The Design Pattern

Affter a while, I realize that my problem is not in the this.$children itself. There must be another way to do this. Then in vue3, I start to move from data in template to plain javascript.

let tabsArray = [
  { name: 'home', title: 'Home', color: 'bg-blue-500',
    text: `Lorem ipsum dolor sit amet,
      consectetur adipiscing elit.
      Quisque in faucibus magna.` },
  …
];

export { tabsArray };
Enter fullscreen mode Exit fullscreen mode

At first, I think that this is not an elegant solution, but I continue anyway. My goal is to get the component done in vue3, so I can start migrate without obstacle. I can change the code later anyway.

Data Provided from Parent

Using provide and inject mechanism, preapring all tabs to be used in child component later.

Now I can rewrite the code from scratch using template without data.

<template>
  <tabs />
</template>
Enter fullscreen mode Exit fullscreen mode

The data can be provided in parent.

import { provide } from 'vue'
import { tabsArray } from '@/tabsArray.js'
import tabs from '@/components/mockup/TabsMockup.vue'

export default {
  name: 'MainMockup',
  components: { tabs },
  setup() {
    provide('tabsArray', tabsArray)
  }
}
Enter fullscreen mode Exit fullscreen mode

Composition API with vue3 is very helpful. Although actually you can do it with option API with vue2.

Data Injected to Children

The children component is still using v-for as usual, but this time I do not use the slot feature.

<template>
  <main class="tabs">
    <div class="tab-headers">
      <tabheader
        v-for="tab in tabs" v-bind:key="tab.name"
        :title="tab.title" :color="tab.color"
        :tabname="tab.name"
      />
    </div>

    <div class="tab-spacer"></div>

    <div class="tab-contents">
      <tabcontent
        v-for="tab in tabs" v-bind:key="tab.name"
        :text="tab.text" :title="tab.title" :color="tab.color"
        :tabname="tab.name"
      />
    </div>
  </main>
</template>
Enter fullscreen mode Exit fullscreen mode

The tabs data in children is available now, by injecting previosly provided tabsArray.

import { inject } from 'vue'
import tabheader  from '@/components/mockup/TabHeaderMockup.vue'
import tabcontent from '@/components/mockup/TabContentMockup.vue'

export default {
  name: 'TabsMockup',
  components: { tabheader, tabcontent },
  setup() {
    const tabs = inject('tabsArray')
    return { tabs }
  }
}
Enter fullscreen mode Exit fullscreen mode

That simple. And that is all.

Preview

Tabs Component: Mobile

Complete Code

You can have a thorough explanation with working code examples, in provided links above.

Closing This Article

This provide and inject mechanism is simpler. It is also make sense without magic. But sacrificing slot design pattern might not pleased everbody.

After all I still have so much to learn.

Top comments (0)