provide/ inject

Pradipta Choudhury
Last Updated: May 13, 2022

Introduction:

Both the Provide and Inject are used to allow an ancestor component for serving as a dependency injector for all its descendants. As long as they are in the same parent chain, they don’t care about how deep the is component hierarchy. Assuming that you have already covered the component basics. If you are new to it, getting familiar with the basics is recommended by referring to our blog- Installation to vueJS.

In this article, we will be covering the magic of provide/ Inject in vue.js. So, let’s get started..

Getting started:

Generally, props are used for passing parent-to-child components. But you might be thinking why do we need these provide and inject components. For answering this question, just think of a scenario where you are having deeply nested components and you need some details from the parent component in the deeply nested child. The simple way of doing that is you will pass the prop down the whole component. But this process is a little annoying right?.

For overcoming this procedural format, the provide and inject come into play. Here,  the parent component act as a dependency provider for all the components. It doesn’t care how deep are the components. The interesting part is that the parent component contains the provide option for providing all the data and the child component contains the inject option for starting the data.

For example:

For input components, any parent component is able to inject validated messages. Let’s go through an example for better clarity. Here the vueForm component is passing all the errors variables to the TextInput component. 

vue

const VueForm = {
provide:{
   errors: {
       name: "The name field is required",
      },
  },
  template: `<form>
              <slot></slot> 
            </form>`
}

const TextInput = {
inject: ['errors'],
  created(){
   console.log(this.errors)
  },
  template: `
            <div>
             Name:<br>
   <input type="text" name="name"><br><br>
              <span v-if="this.errors.hasOwnProperty('name')" class="help-text danger" v-text="this.errors.name"></span>
            </div>
            `
}

new Vue({
  el: "#app",
  components: {
    'vue-form': VueForm,
    'text-input': TextInput,
  },
})

 

Html

<div id="app">
  <vue-form>
    <text-input></text-input>
  </vue-form>
</div>

 

CSS

body {
  background#20262E;
  padding20px;
  font-family: Helvetica;
}

#app {
  background#fff;
  border-radius4px;
  padding20px;
  transition: all 0.2s;
}

li {
  margin8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom15px;
}

del {
  colorrgba(0, 0, 0, 0.3);
}

.help-text.danger {
  color: red;
}

 

Output:

A function can be provided and injected within a function. Below example includes getError() and setError() using provide/ inject.

vue.js

const VueForm = {
providefunction() {
   return {
     getError: this.getError,
      setError: this.setError
    }
  },
  data(){
   return {
     errors: {
       name: "The name field is required."
      }
    }
  },
  methods: {
   getErrorfunction(name){
     return this.errors.hasOwnProperty(name) ? this.errors.name : null;
    },
    setErrorfunction(namevalue){
     this.errors.name = value;
    }
  },
  template: `<form>
              <slot></slot
            </form>`
}

const TextInput = {
inject: ['getError''setError'],
  template: `
            <div>
             Name:<br>
              <input type="text" name="name" @change="setError('name', 'This is a custom error message')"><br><br>
              <span class="help-text danger" v-text="getError('name')"></span>
            </div>
            `
}

new Vue({
  el"#app",
  components: {
    'vue-form': VueForm,
    'text-input': TextInput,
  },
})

 

Html

<div id="app">
  <vue-form>
    <text-input></text-input>
  </vue-form>
</div>

 

CSS

body {
  background#20262E;
  padding20px;
  font-family: Helvetica;
}

#app {
  background#fff;
  border-radius4px;
  padding20px;
  transition: all 0.2s;
}

li {
  margin8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom15px;
}

del {
  colorrgba(0, 0, 0, 0.3);
}

.help-text.danger {
  color: red;
}

 

Output:

Here, the provide and inject bindings are not reactive in nature. This is intentionally done. Now, if you pass a down object that will remain reactive.

Vue.js

const VueMenu = {
inject: ['menu'],
  created(){
   console.log(this.menu)
  },
  data(){
   return {
    newItem:"",
    }
  },
  methods:{
   addMenuItemfunction(){
    console.log(this.newItem)
     this.menu.menuOptions.push(this.newItem);
      this.newItem="";
    }
  },
  template: `
            <div>
              <ul id="example">   
                <li v-for="item in menu.menuOptions">     
                  {{ item }}   
                </li
              </ul>
              <input type="textplaceholder="Enter namev-model="newItem"/>
              <button type="submit" @click="addMenuItem">Add Menu Item</button>
            </div>
            `
}

new Vue({
  el: "#app",
  components: {
    'vue-menu': VueMenu,
  },
  provide () {
    const menu = {}
    Object.defineProperty(menu, 'menuOptions', {
      enumerable: true,
      get: () => this.menuOptions,
    })
    return { menu }
  },

  data: () => ({ menuOptions: ['Home''About','Contact'] }),
})

 

HTML:

<div id="app">
  <vue-menu>
  </vue-menu>
</div>

CSS:

body {
  background#20262E;
  padding20px;
  font-family: Helvetica;
}

#app {
  background#fff;
  border-radius4px;
  padding20px;
  transition: all 0.2s;
}

li {
  margin8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom15px;
}

del {
  colorrgba(0, 0, 0, 0.3);
}

 

Working with reactivity

On changing the list of todos, changes won’t get affected in the todoLength property. The reason behind this is that provide/ inject properties are not reactive by default. This behavior can be changed bypassing a ref property or reactive object to provide. And for reacting towards the changes in the ancestor component, a composition API needs to be assigned computed property to the todoLength

app.component('todo-list', {
  // ...
  provide() {
    return {
      todoLength: Vue.computed(() => this.todos.length)
    }
  }
})

app.component('todo-list-statistics', {
  inject: ['todoLength'],
  created() {
    console.log(`Injected property: ${this.todoLength.value}`// > Injected property: 5
  }
})

 

In this example, any changes in the todos.length will get reflected back correctly. 

Frequently asked questions

  1. Define dynamic components in Vue? 
    Dynamic components are the components in which we can switch between two or more components without switching.
     
  2. Mention the use of provide and inject?
    Both the Provide and Inject are used to allow an ancestor component for serving as a dependency injector for all its descendants. As long as they are in same; parent chain, their deepness in hierarchical order doesn’t affect at all.

Key takeaways

This article teaches us about provide/ inject. Keeping the theoretical knowledge at our fingertips helps us get about half the work done. To gain complete understanding, practice is a must. To achieve thorough knowledge on full-stack web development you may refer to our full-stack web development course.

Was this article helpful ?
0 upvotes

Comments

No comments yet

Be the first to share what you think