Dependency management is one of the critical points while developing applications. In the back-end world, there are many IoC container libraries that we can make use of, like AutofacNinject, etc. Similarly, many modern front-end frameworks also provide DI features. However, those features work way differently from how back-end libraries do. In this post, we’re going to use TypeScript and Vue.js for development and apply an IoC container library called InversifyJS that offers very similar development experiences to back-end application development.

The code samples used in this post can be found here.

provide/inject Pair in VueJs

According to the official document, vue@2.2.0 supports DI feature using the provide/inject pair. Here’s how DI works in VueJs. First of all, declare dependency, MyDependency in the parent component like:

 

Then its child component consumes the dependency like: 

Maybe someone from the back-end development got a question. Child components only consume dependencies that are declared from their parent component. In other words, in order for all components to consume all dependencies, this declaration MUST be done at the top-level component of its hierarchy. That’s the main difference between VueJs and other back-end IoC containers. There’s another question – VueJs doesn’t provide a solution for inter-dependency issue. This inter-dependency should be solved by a third-party library. But that’s fine. We’re going to use TypeScript anyway, which has a solution for the inter-dependency issue.

DI in VueJs and TypeScript

Evan You, the creator of VueJs, has recently left a comment about his design philosophy on VueJs framework.

While using a class-based API by default may make it more “friendly” to devs used to classes, it also makes it more hostile to a large group of users who use Vue without build tools or transpilers. When you are advocating your preference, you might be missing some nuance we have to take into account as a framework.
This is why we offer the object-based API as the baseline and the class-based API as an opt-in. This allows us to cater to both groups of users.

Therefore, we need to sort out either using the provide/inject pair or using another approach, ie. service locator pattern. In order to use the provide/inject pair, as we found above, we need to put an IoC container instance at the top-level of the component. On the other hand, we can simply use the container as a service locator. Before applying either approach, let’s implement the IoC container.

Building IoC Container using InversifyJS

InversifyJS is a TypeScript library for IoC container, which is heavily influenced from Ninject. Therefore syntax is very similar to each other. Interface and class samples used here are merely modified from both libraries’ conventions – yeah, the ninja stuff!

Defining Interfaces

Let’s define Weapon and Warrior interfaces like below:

 

Defining Models

InversifyJS uses Symbol to resolve instances. This is a sample code to define multiple symbols in one object. This object contains multiple symbols for WarriorWeapon and Container.

 

The @injectable decorator provided by InversifyJS defines classes that are bound into an IoC container.

The @inject decorator goes to constructor parameters. Make sure that those parameters require the Symbol objects defined earlier.

 

Make sure that we should use the same Symbol object defined earlier. If we simply use Symbol("Weapon") here, it wouldn’t be working as each Symbol object is immutable.

Implementing IoC Container

Let’s implement the IoC container using the interfaces and models above.

The last part of the code snippet above, container.bind(...).to(...), is very similar to how IoC container works in C#. Now we’re ready for use of this container.

Attaching Child Component

Unlike the Previous Posts, We’re adding a new child Vue component, Ninja.vue to Hello.vue for dependency injection.

Hello.vue has got the Ninja.vue component as its child. Let’s have a look at the Ninja.vue component.

Now, let’s apply both service locator and provide/inject pair.

Applying Service Locator

We’re updating the Ninja.vue to use service locator:

As we can see above, the IoC container instance, container is directly consumed within the Ninja.vue component. When we run the application, the result might be looking like:

As some of us might uncomfortable to use the service locator pattern, now we’re applying the built-in provide/inject pair.

Applying provide/inject Pair

As we identified above, in order to consume all dependencies at all Vue components, we should declare IoC container as a dependency at the top-level of the component, ie) App.vue.

We can see that the container instance is provided with the symbol, SERVICE_IDENTIFIER.CONTAINER defined earlier. Now let’s modify the Ninja.vue component:

The @Inject decorator takes care of injecting the container instance from the App.vue component. Make sure that the same symbol, SERVICE_IDENTIFIER.CONTAINER is used. All good! Now we can see the same result like the picture above.

So far, we’ve had an overview of how to use DI in VueJs & TypeScript app in two different approaches – service locator or provide/inject pair. Which one to choose? It’s all up to you.