The Magic of Provide/Inject in Vue.js

Dependency Injection in Vue.js will be a breeze with Provide and Inject duo

Sandip Shrestha
22 September, 2019 | 3 minutes read
The Magic of Provide/Inject in Vue.js

Often times is overwhelming to look at a larger component as a whole because there may be too much going on, breaking things down allows us to simplify a complex problem and focus our attention in more manageable pieces. One obvious complication of breaking down a large component is data flow and management.

Provide and Inject are used together to allow an ancestor component to serve as a dependency injector for all its descendants, regardless of how deep the component hierarchy is, as long as they are in the same parent chain.

Example:

For example, a parent component can inject validation messages in input components. Below is an example where the VueForm the component is passing down an errors variable down to TextInput component.

// A basic example of provide/inject

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,
  },
})

You can also provide and inject a function. The below example demonstrates passing getError() and setError() functions using provide/inject.

// provide and inject functions

const VueForm = {
	provide: function() {
  	return {
    	getError: this.getError,
      setError: this.setError
    }
  },
  data(){
  	return {
    	errors: {
      	name: "The name field is required."
      }
    }
  },
  methods: {
  	getError: function(name){
    	return this.errors.hasOwnProperty(name) ? this.errors.name : null;
    },
    setError: function(name, value){
    	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,
  },
})

Injecting functions using provide/inject

Note: the provide and inject bindings are NOT reactive. This is intentional. However, if you pass down an observed object, properties on that object do remain reactive.

// Reactive binding in provide/inject

const VueMenu = {
	inject: ['menu'],
  created(){
  	console.log(this.menu)
  },
  data(){
  	return {
    newItem:"",
    }
  },
  methods:{
  	addMenuItem: function(){
    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="text" placeholder="Enter name" v-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'] }),
})

Injections are available in props and data. So, you could set prop defaults to injected data or you can use injections as initial data.

More on provide/inject on the official documentation here.

Copyright © 2020, Sandip Shrestha. All rights reserved.