Building Apps with Aurelia: #7 Dependency Injection in Aurelia - Part 1

Building Apps with Aurelia: #7 Dependency Injection in Aurelia - Part 1

Previously we talked about MVVM in Aurelia. Like MVVM, Dependency Injection is a pattern that is used within Aurelia to have loose coupling between shared/dependent components. There may be services, shared components that your application wants in multiple modules and dependency injection can be used to easily use these services between modules by injecting them in and use the shared APIs. Let’s look at how we can implement dependency injection in Aurelia.

First thing to do is to make the type that your module is dependent on accessible to the module. Basically you need to import the type in to your module. This is done by ES2015 import statements. We need to implement the classes that becomes the objects that our modules depend upon and import them in. We also use a feature of ES7 (aka ES2016), that is decorators to decorate the class that needs the dependency with the inject decorator. Note that the decorators are a pre-release feature of ES2016 but Aurelia can use it with the help of bable to transpile it in to ES2015 code.

Note: To use ES7 decorators you need to explicitly enable it in the config.js file for the project. Open up the config.js file and inside bableOptions node, in the optional array, make sure es7.decorators are available. If not add it. While we are at it, add es7.classProperties as well. The bableOptions should now look like this.

babelOptions: {
  "optional": [
    "runtime",
    "optimisation.modules.system",
    "es7.decorators",
    "es7.classProperties"
  ]
},

So to start we need some kind of service to share between our modules. If you remember the last post, we had some hard coded profile information that we displayed in the About section of the app. We can actually use this as a service that holds on to this profile information. And let’s imagine that we also need to display the same profile information on the home section as well.

So first thing to do is to create a new file called profileData.js in our src folder. (You can also create a services folder inside src folder and create the profileData.js in the services folder to organize the project a little bit. But for this we’ll ignore it). The add the following code in to the file.

export class ProfileData {
  constructor() {
    this.profiles = [
      {
        id: 1,
        name: 'John Doe',
        role: 'Lead Developer',
        image: 'https://api.adorable.io/avatars/120/john-doe'
      },
      {
        id: 2,
        name: 'Jason Smith',
        role: 'Lead Designer',
        image: 'https://api.adorable.io/avatars/120/jason-smith'
      },
      {
        id: 3,
        name: 'Jane Doe',
        role: 'Designer',
        image: 'https://api.adorable.io/avatars/120/jane-doe'
      }
    ];
  }
}

This simply create a class called ProfileData and exports it. In it we have an array called profiles and we moved the profile array that was in about.js in to this profileData.js class. (Delete the lines of code where we have this profiles array defined in the about.js file) Now we have the profile data in a class that we can import in. Next we need to import profileData in to about viewmodel. To do that add the following import line to the top of the about.js file.

import { ProfileData } from 'profileData';

This will import the profileData in to our about viewmodel. Next we need to decorate the About class with the inject decorator. To do this we also need to import in inject decorator from the Aurelia framework as well. To do this add the following code line after the import statement for the profileData import.

import { inject } from 'aurelia-framework';

Now we can inject the profileData service in to the About viewmodel. To do that decorate the viewmolde put in the following line just above the About class definition. (Make sure you don’t add a semicolon at the end of the decorator. That will cause an error)

@inject(ProfileData)
export class About { ...

When we inject a service in to a class, the injected service will be passed to the constructor as a parameter. And if you have multiple injects they will appear in the order that you injected in to the class using the inject decorator. So after adding the parameter to hold the injected service in to the class the entire about viewmodel should look like this.

import { ProfileData } from 'profileData';
import { inject } from 'aurelia-framework';

@inject(ProfileData)
export class About {
  constructor(profileData) {
    this.aboutMessage = 'This is the Hello Aurelia app used in Building Apps with Aurelia blog series by The KVK Blog';
  }
}

Now if we run the application you will see that you cannot see the profiles displayed in the about section of the app. Have a look the screen shot, you should see something like this.

01-no-profile-data-shown

Why is this? Coz we still did not use the injected profileData J

Now we need to use the injected service in the about viewmodel. What we need is to populate the profiles property in our viewmodel with the profile data coming from the profileData service. We can directly assign the data to a class property from the injected service itself or we can also save the injected service in a class property to use it later in the viewmodel. What our view is looking for is an array named profiles with profile objects. Let’s use the injected profileData service to populate this array. Add the following code line to your constructor.

this.profiles = profileData.profiles;

Remember, our profileData service has its own profiles array that contains the profile data. What we are doing is just assigning it to the profiles property in our About ViewModel. So now, the complete viewmodel should look like this.

import {ProfileData} from 'profileData';
import {inject} from 'aurelia-framework';

@inject(ProfileData)
export class About {
  constructor(profileData) {
    this.aboutMessage = 'This is the Hello Aurelia app used in Building Apps with Aurelia blog series by The KVK Blog';
    this.profiles = profileData.profiles;
  }
}

And if you now run your application you should see an output like this.

02-injected-profile-data-used-to-display-the-profiles

Ok. Let’s try using the profileData service in another viewmodel. Well we only have one other viewmodel we can use this. That is home viewmodel. Let’s think that we need to show the Employee of the month in the home section of the app. And this employee of the month is the first profile in the profiles array. Simple enough example :D

So we have the profile view that we can use. Let’s add some HTML to home.html to do this. Add the following to the home.html after the paragraph tag.

<hr>
<h2>Employee of the Month</h2>
<compose model.bind="profile" view-model="viewmodels/profile"></compose>

Now the entire home.html view should look like this.

<template>
  <h1>Home</h1>
  <hr>

  <div class="form-group">
    <input type="text" class="form-control" value.bind="message">
  </div>
  <p class="lead">${message}</p>
  <hr>

  <h2>Employee of the Month</h2>
  <compose model.bind="profile" view-model="viewmodels/profile"></compose>
</template>

Now let’s inject the profileData service in to the home.js viewmodel and save it for later use in a property in the viewmodel. (In this scenario, we don’t have a later use for the service. This is done only for demo purposes). And finally use the saved service to get the first profile from the profiles list and show the employee of the month. :) Since we already know how to import and inject the profileData service, I will show the completed viewmodel code here.

import { inject } from 'aurelia-framework';
import { ProfileData } from 'profileData';

@inject(ProfileData)
export class Home {
  constructor(profileData) {
    this.message = '';
    this.profileData = profileData; // Saving the injected service for later use.

    this.profile = this.profileData.profiles[0];
  }
}

Now if you run your application you will see the first profile of the array is shown as the employee of the month. Look at the image bellow.

03-employee-of-the-month-is-shown

Now, I think you can understand how easy to inject different services in to viewmodels using Dependency Injection in Aurelia and how you can share services, data and anything else you wanna share through Dependency Injection.

One more thing I want you to know

If you don’t want to use ES7(ES2016) decorators for the dependency injection and use the inject decorator to inject the services, there is another way to do that. We can use a static property called inject inside the viewmodel and pass in an array of services you wanna inject into the viewmodel. The rest is the same. You need to import the service first, and pass the injected service as a parameter in to the constructor as well. But now you don’t need to import inject service from Aurelia framework since we are not using the inject decorator for dependency injection.

Let’s see this in action. Change your home.js viewmodel to look like the code bellow.

import {ProfileData} from 'profileData';

export class Home {
  static inject = [ProfileData];
  constructor(profileData) {
    this.message = '';
    this.profileData = profileData; // Saving the injected service for later use.

    this.profile = this.profileData.profiles[0];
  }
}

Now if you run the app, it works fine as it was used to work. J Soo guys, we are done with the part 1 of the dependency Injection in Aurelia framework. There are more concepts we need to talk about before we move ahead. So let talk more about dependency injection in the next post.

You can Download the source code from here.  

Building Apps with Aurelia:  All Articles

You Might Also Like
Comments