Solving this.$children removal in Vue3 using provide/inject mechanism
Tags: vue
Published: 2020-12-07 11:45:12

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](https://epsi-rns.gitlab.io/frontend/2020/11/15/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:

```html
<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>
```

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

```html
<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>
```

The children component is using this line of code.

```javascript
export default {
  name: 'TabListSimple',
  data() {
    return {
      selected: 'news',
      tabs: this.$children
    };
  }
}
```

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.

```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 };
```

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.

```html
<template>
  <tabs />
</template>
```

The data can be provided in parent.

```javascript
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)
  }
}
```

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.

```html
<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>
```

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

```javascript
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 }
  }
}
```

That simple. And that is all.

## Preview

![Tabs Component: Mobile](https://dev-to-uploads.s3.amazonaws.com/i/u9pwfh66e30vc6diu364.png)

## 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.