Dependency management is one of critical points while developing applications. In the back-end world, there are many IoC container libraries that we can make use of, like Autofac, Ninject, 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 consumes 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 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 is 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 Warrior, Weapon 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 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.

Category:
Application Development and Integration
Tags:
, , , , ,

Join the conversation! 8 Comments

  1. […] this previous post, in order to use dependency injection (DI), we create a Symbols instance and use it. axios is […]

    Reply
  2. Thanks for the article. I’m used to the greatly simplified DI in Angular, so this seems complicated and verbose to me by comparison. Is there a reason I should do it this way versus using vue-inject or just creating a new instance of a service class directly on Vue’s provide and inject?

    Thanks!

    Reply
    • @Chris Thanks for the comment!

      The main purpose of this post is to provide developers having a background from back-end side with similar development experiences to the existing IoC containers when developing a back-end API applications.

      You can use vue-inject, if you like. But I’m not quite sure if it would be convenient when I’m with TypeScript. Its usage looks quite similar to Angular, based on my understanding.

      However, when I read its usage, it doesn’t seem to resolve cross-dependencies like how Autofac or Ninject does as an IoC container. It also doesn’t follow the official way of DI in Vue.js, either.

      Therefore, if we just focus on the provide/inject pair part, not the service locator part, it’s not that complicating. If you’re mentioning on the container bits, it maybe looks verbose or complicating, comparing to the one in Angular. However, using InversifyJS brings us with several benefits – it supports TypeScript OOTB, lazy bindings (or resolutions) and, more importantly, same development experiences.

      Reply
  3. Thanks for the article, it’s very helpful. How does one mock out dependencies for unit tests when using this approach?

    Reply
  4. In your example for “Applying provide/inject Pair” you are still using location, the only IOC you are doing is the container itself, from then on you are still doing service location when you do get. You should fix that to use real IOC by using provide inject with resolutions from the container at the root component.

    Reply
    • @inputFlip Thanks for the comment.

      In the provider/inject pair section, it injects the IoC container itself and that container resolves dependencies within a component. I couldn’t find a better way to inject dependencies other than this approach that I’ve written in this post. And I don’t think it’s the same service locator pattern as its previous section either. If you find a better way, please share with me. 😉

      Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: