Dependency Injection

in Vue.js

Who Am I?

  • Alex Riviere
  • Web Developer
  • Co-Organizer for Atlanta Vue.js Meetup
  • Unicorn Enthusiast

Methods of Passing State Between Components

  • Props/Emit
  • External Store (Vuex)
  • $root/$parent (Not Advised)
  • Provide/Inject

Props Down, Events Up

<parent-component />
<child-component />
<grand-child-component />

$props

$props

$emit

$emit

Props Down, Events Up

  • Creates reusable components
  • Doesn't rely on outside data structures
  • Easily testable

Global Store

<parent-component />
<child-component />
<grand-child-component />

 Vuex $store

state

state

state

mutation

mutation

mutation

Global Store

 Vuex $store

  • Useful for large applications with shared state
  • Can be used with other state management strategies
  • Relies on external dependency
  • Not ideal for publishing modules

$parent Knows Best

<parent-component />
<child-component />
<grand-child-component />

$parent.knowledge

$parent.$parent.knowledge

$parent Knows Best ?

<parent-component />
<grand-child-component />

$parent.$parent.knowledge?

$parent Knows Best ?

  • Good for quick experiments
  • Relies way too heavily on very specific structure
  • Not ideal for production

$root of All Evil

<parent-component />
<child-component />
<grand-child-component />

$root.evil

$root of All Evil

<parent-component />
<child-component />
<grand-child-component />

$root.evil

<grand-parent-component />

$root of All Evil

  • Single Source of truth
  • Relies on single source of truth to exist in root component
  • Can break if you change root structure
  • Good for experimenting, Not advised for production

Dependency Injection

<parent-component />
<child-component />
<grand-child-component />
provide
inject
<child-component />
<child-component />

Dependency Injection

  • Great for encapsulated state
  • Provided data is not reactive
  • Extremely useful in very specific cases

SHOW ME

HOW ALREADY!

<div>
  <p>This is the count:</p>
  <p>{{count}}</p>
  <simple-inject/>
  <button @click="count++"
          class="button">
    Increment!
  </button>
</div>
SimpleProvide.vue
import SimpleInject from './SimpleInject';

export default {
  name: 'simple-provide',
  data(){
    return {
      count: 1,
    };
  },
  provide(){
    return {'count': this.count};
  },
  components: {
    SimpleInject,
  },
}
Template
Script
<div>
    <p>This is the injected count:</p>
    <p>{{count}}</p>
</div>
SimpleInject.vue
export default {
  name: "simple-inject",
  inject: ['count'],
}
Template
Script

Simple Demo!

Recap

  • provide should be a function similar to data
  • inject can be an array of strings
  • Values returned from provide are not reactive by default
<div>
  <p>{{message}}</p>
  <p>^ This is the message</p>
  <p><input type="text"
            v-model="message"></p>
  <button class="button"
          @click="buttonClick">
    Modify Nested Component
  </button>
  <promise-inject/>
</div>
PromiseProvide.vue
import PromiseInject from "./PromiseInject"

export default {
  data() {
    return {
      message: "Hello World",
    }
  },
  methods: {
    buttonClick() {
      this.$emit('button-push', this.message)
    }
  },
  components: {
    PromiseInject
  },
  provide() {
    const hp = (res,rej) =>{
      this.$on('button-push', res)
    }
    return {
      providedPromise: new Promise(hp),
    }
  }
}
Template
Script
<div>
  <p>I'm a nested component!</p>
  <p>{{childMessage}}</p>
</div>
PromiseInject.vue
export default {
  name: "promise-inject",
  data: function () {
    return {
      childMessage: 'I am waiting on a promise!'
    }
  },
  methods: {
    changeChildMessage(response) {
      this.childMessage = response
    }
  },
  inject: {
    providedPromise:{
      type:Promise,
    }
  },
  mounted: function () {
    this.providedPromise
        .then(this.changeChildMessage)
  }
}
Template
Script

Promise Demo!

Recap

  • By providing a promise, we can give data later
  • inject can be formatted similar to the way props can be
  • Promises only resolve once. So data is still not reactive.

How do we make this Reactive?

RxJS!!

NO.

<div>
  <p>{{message}}</p>
  <p>^ This is message</p>
  <input type="text"
         v-model="message">
  <reactive-inject/>
</div>
ReactiveProvide.vue
import ReactiveInject from "./ReactiveInject"

export default {
  data() {
    return {
      message: "Hello World",
    }
  },
  provide() {
    const get = ()=>this.message
    const set = (v)=>this.message=v
    return {
      getParentMessage: get,
      setParentMessage: set,
    }
  },
  components: {
    ReactiveInject
  },
}
Template
Script
<div>
  <p>I'm a nested component!</p>
  <p>
    <code>injectedMessage</code>
    being reactive after injection:
  </p>
  <p>{{injectedMessage}}</p>
  <p>
    We can also set
    <code>injectedMessage</code>
    from this component
  </p>
  <p>
    <input type="text"
           class="input"
           v-model="injectedMessage">
  </p>
</div>
ReactiveInject.vue
export default {
  name:"reactive-inject",
  inject: [
    "getParentMessage", 
    "setParentMessage",
  ],
  computed: {
    injectedMessage: {
      get() {
        return this.getParentMessage()
      },
      set(d) {
        this.setParentMessage(d)
      },
    }
  },
}
Template
Script

Reactive Demo

Recap

  • By providing a scoped function, we can get updated injected values
  • We can also update injected values with a provided function to set them in the parent
  • This can be a headache if you don't know what is setting the value

Where should I use this?

  • When you need internal state encapsulated from everything else
  • Potentially if you are creating a series of components for handling known outcomes
  • Themes!
  • Maps!

Questions?

Dependency Injection in Vue.js - First Draft

By Alex Riviere

Dependency Injection in Vue.js - First Draft

  • 575