To register a dependency we have to bind it to something that will identify that dependency. This identification is called the dependency token. For instance, if we want to register the URL of an API, we can use the string API_URL as the token. Similarly, if we’re registering a class, we can use the class itself as its token as we’ll see below. Dependency injection in Angular has three pieces:
1) The Provider (also often referred to as a binding) maps a token (that can be a string or a class) to a list of dependencies. It tells Angular how to create an object, given a token.
2) The Injector that holds a set of bindings and is responsible for resolving dependencies and injecting them when creating objects
3) The Dependency that is what’s being injected
We can think of the role of each piece as illustrated below:
A way of thinking about this is that when we configure DI we specify what is being injected and how it will be resolved.
Playing with an Injector
Above with our Product and PriceService we manually created the PriceService using the new operator. This mimics what Angular itself does. Angular uses an injector to resolve a dependency and create the instance. This is done for us behind the scenes, but as an exercise, it’s useful to explore what’s happening. It can be enlightening to use the injector manually, because we can see what Angular does for us behind the scenes. Let’s manually use the injector in our component to resolve and create a service. (After we’ve resolved a dependency manually, we’ll show the typical, easy way of injecting dependencies. ) One of the common use-cases for services is to have a ‘global’ Singleton object. For instance, we might have a UserService which contains the information for the currently logged in user. Many
different components will want to have logic based on the current user, so this is a good case for a service.
Here’s a basic UserService that stores the user object as a property:
import { Injectable } from '@angular/core'; @Injectable() export class UserService { user: any; setUser(newUser) { this.user = newUser; } getUser(): any { return this.user; } } Say we want to create a toy sign-in form: <div> <p *ngIf="userName" class="welcome"> Welcome: {{ userName }}! </p> <button (click)="signIn()" class="ui button">Sign In </button> </div>
Above, we click the “Sign In” button to call the signIn() function (which we’ll define in a moment). If we have a userName, we’ll display a greeting.
Now let’s implement this functionality in our component by using the injector directly.
import { Component, ReflectiveInjector } from '@angular/core'; import { UserService } from '../services/user.service'; @Component({ selector: 'app-injector-demo', templateUrl: './user-demo.component.html', styleUrls: ['./user-demo.component.css'] }) export class UserDemoInjectorComponent { userName: string; userService: UserService; constructor() { // Create an _injector_ and ask for it to resolve and create a UserService const injector: any = ReflectiveInjector.resolveAndCreate([UserService]); // use the injector to **get the instance** of the UserService this.userService = injector.get(UserService); } signIn(): void { // when we sign in, set the user // this mimics filling out a login form this.userService.setUser({ name: 'Nate Murray' }); // now **read** the user name from the service this.userName = this.userService.getUser().name; console.log('User name is: ', this.userName); } }
This starts as a basic component: we have a selector, template, and CSS. Note that we have two properties: userName, which holds the currently logged-in user’s name and userService, which holds a reference to the UserService.
In our component’s constructor we are using a static method from ReflectiveInjector called resolveAndCreate. That method is responsible for creating a new injector. The parameter we pass in is an array with all the injectable things we want this new injector to know. In our case, we just wanted it to know about the UserService injectable. The ReflectiveInjector is a concrete implementation of Injector that uses reflection to look up the proper parameter types. While there are other injectors that are possible ReflectiveInjector is the “normal” injector we’ll be using in most apps.
For any query regarding Dependency Injection Parts in Angular, drop a comment below.