I have to admit it (and this is my personal point of view). Dependency Injection is not popular in the VueJS Ecosystem and, dare I say, in the JS ecosystem. People want to get stuff done quickly and nicely and that is absolutely fine but I feel that most of the SOLID and OOP principles are lost in the JS ecosystem but let’s talk about that in another post 😛
To understand Dependency Injection and its benefits, I absolutely recommend you to read this post first:
Dependency Injection was kind of hard to implement in a VueJS application since it required to do a couple of tweaks to the codebase. One of the DI (Dependency Injection) solutions for JS and TS out there is InversifyJS which is my favorite DI container but still hard to use with VueJS. Fortunately, that is no more and one person give us, which is to me, the solution to implementing DI in our VueJS Apps.
In this guide/practice, I want you to learn about how to implement Dependency Injection easily into your VueJS project with TypeScript using the library Inversify Props
If you want to take a look into the complete example, you can go directly to this link!
- Install the latest version of yarn andVue CLI
- An editor compatible with TypeScript (I love WebStorm for this)
- Clone the base repo for the exercise.
A super simple app that:
- First, will use a service that connects to https://api.kanye.rest through a component
- Then, the same behaviour but through a Vuex Module
Clone the next repo in your machine:
and then instal the dependencies using
$ yarn install
In the /src folder, create a new folder folder called services and inside the new folder, create a folder called impl
- The /services root will be used to expose easily the Service Interfaces
- /services/impl will be used to add the concrete implementations of our interfaces/services
In the /services folder create the file IKanyeWestService.ts with this shape:
We return a promise since we will retrieve the quote from an external service.
It’s time to do the concrete implementation! Create a file called KanyeWestService.ts in the /services/impl folder. It should have this shape:
In this class service, what basically does is a wrapper of our axios HTTP call which will return the object data that corresponds to the quote. You might ask:
Why didn’t you do the http request inside the required component?
The answer is easy! It’s easier to maintain. By creating a decoupled service from our codebase and injecting it in the whole codebase, let us have our code in only and only one place. If for some reason the library changes in the future (maybe they change from HTTP to Protobufs) we need to only do the change in one place and will take effect basically in every part where the dependency is injected. We will be using the abstraction over the concrete implementation.
In the /src folder, create a file called app.container.ts which will be the file where we are going to register all of the dependencies with its abstractions. the file should look like this:
Let’s bind our dependencies!
In the same file, import container from inversify-props and both files that we did for the KanyeWest service. Inside of the function we will use container to register our service in this way:
To this to take effect in our codebase, in the main.ts you will import the function from app.container and use it inside the loadDependencyContainer
Our dependency container ir ready! Now it’s time to use it inside a Vue Component.
Go to to /App.vue and start by importing the IKanyeWestService and then a decorator from inversify-props called Inject
And to inject this service, just simply add this piece of code in your Component class:
and… That’s it! It literally take a few lines of code to use a dependency by using DI with this library. The only things left are:
- Fetch the quote when you click the button.
- Fetch the quote when the component is loaded.
To tackle both cases, in the method getQuote use the injected dependency and set the quote text into the data which is called quote
and to execute this when the component is loaded, just put it in the mounted hook method. At the end, your script should look like this:
Now that we have a fully functional component with an injected dependency, we can do the same for Vuex using vuex-class and vuex-module-decorators
Let’s first remove the content of the method getQuote, remove the injected dependency from the class and then remove the imports associated to the injected dependency.
Now import the Action and Getter decorators from vuex-class and use Getter above the quote data to bind it to the quote getter in the store:
For the Action, since we are binding functions, we will need to match the function type in the next way:
And use it inside the getQuote method. The component script should look like this:
The last part of this guide is doing DI in a Vuex module using vuex-module-decorators and inversify-props which is as easy as the component approach and, dare I say, the same code.
go to /store/modules/KanyeWest.ts and import the same inject and Interface service that we used in the component into this file
And using the same approach as the component, Inject the dependency:
and that’s pretty much it! Let’s us it in our fetchKanyeQuote action:
And voila! Our project is finished and using DI!
And that’s it for today! I hope you learned a little bit about Dependency Injection on VueJS and TypeScript. Feedback is welcomed!