Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Simplifying Different Types of Providers in Angular

DZone's Guide to

Simplifying Different Types of Providers in Angular

In this tutorial, we go over the several different providers that are available to developers in the Angular framework, and how to use them in your TypeScript code.

· Web Dev Zone ·
Free Resource

Jumpstart your Angular applications with Indigo.Design, a unified platform for visual design, UX prototyping, code generation, and app development.

An Angular Service provider delivers a runtime version of a dependency value. Therefore, when you inject a service, the Angular injector looks at the providers to create the instance of the service. It is the provider that determines which instance or value should be injected at the runtime in components, pipes, or directives. There is a lot of jargon involved here, so to understand the purpose of the types of providers, let us start with creating a service. Let's say we have a service called ErrorService, which is just logging the error message.

import { Injectable } from '@angular/core';

@Injectable()
export class ErrorService {

    logError(message: string) {
        console.log(message);
    }
}


Now, we can use this service in a component, as shown in the listing below:

import { Component } from '@angular/core';
import { ErrorService } from './errormessage.service';

@Component({
    selector: 'app-root',
    template: `
  <input [(ngModel)]='err' type='text'/>
  <button (click)='setError()'>set error</button>
  `,
    providers: [ErrorService]
})
export class AppComponent {
    constructor(private errorservice: ErrorService) { }
    err: string;
    setError() {
        this.errorservice.logError(this.err);
    }

}


We are importing the service, passing it into the providers array, and injecting it in the constructor of the component. We are calling the service method on the click of the button, and you can see the error message passed in the console. Very simple, right?

Here, Angular will rely on the values passed in the providers array of the component (or module) to find which instance should be injected at runtime.

A provider determines how an object of a certain token can be created. So, when you pass a service name into the 

providers

 array of either component or module, like below:

providers: [ErrorService]


Angular is going to use the token value of ErrorServiceand, for the token ErrorService, it will create an object of the ErrorService class. The above syntax is a shortcut of the below syntax:

providers: [{
    provide: ErrorService, useClass: ErrorService
}]


The provide property holds the token that serves as the key for:

  1. Locating the dependency value.
  2. Registering the dependency.

The second property (of four types) is used to create the dependency value. There are four possible values of the second parameter, as follows:

  1. useClass
  2. useExisting
  3. useValue
  4. useFactory

We just saw an example of useClass. Now, consider a scenario that you have a new class for better error logging called NewErrorService.

import { Injectable } from '@angular/core';

@Injectable()
export class NewErrorService {

    logError(message: string) {
        console.log(message);
        console.log('logged by DJ');
    }

}


useExisting

Now, we want the instance of NewErrorService to be injected rather than the instance of ErrorService. Also, ideally, both classes will be implementing the same interface, which means they will have the same method signatures with different implementations. So now, for the token ErrorService, we want the instance of NewErrorService to be injected. This can be done by using useClass,as shown below:

providers: [
    NewErrorService,
    { provide: ErrorService, useClass: NewErrorService }
]


The problem with the above approach is that there will be two instances of NewErrorService. This can be resolved by the use of useExisting.

providers: [
    NewErrorService,
    { provide: ErrorService, useExisting: NewErrorService }
]


Now there will be only one instance of NewErrorService and the token ErrorService   instance of NewErrorService will be created.

Let us modify the component to use NewErrorService.

mport { Component } from '@angular/core';
import { ErrorService } from './errormessage.service';
import { NewErrorService } from './newerrormessage.service';

@Component({
    selector: 'app-root',
    template: `
  <input [(ngModel)]='err' type='text'/>
  <button (click)='setError()'>set error</button>
  <button (click)='setnewError()'>Set New eroor</button>
  `,
    providers: [
        NewErrorService,
        { provide: ErrorService, useExisting: NewErrorService }
    ]
})
export class AppComponent {
    constructor(private errorservice: ErrorService, private newerrorservice: NewErrorService) { }
    err: string;
    setError() {
        this.errorservice.logError(this.err);
    }
    setnewError() {
        this.newerrorservice.logError(this.err);
    }

}


Here, for demonstration purposes, I am injecting a service into the component, however, to use the service at the module level, you can inject in the module itself. Now, when the 'set error' button is clicked, NewErrorService will be called.

useValue

Both useClass and useExisting create an instance of a service class to inject for a particular token, but sometimes you want to pass a value directly, instead of creating an instance. So, if you want to pass a readymade object instead of an instance of a class, you can use useValue as shown in the below listing:

providers: [
    {
        provide: ErrorService, useValue: {
            logError: function (err) {
                console.log('inhjected directly ' + err);
            }
        }
    }


So here, we are injecting a readymade object using useValue. So for the token ErrorService, Angular will inject the object.

useFactory

There could be a scenario where, until runtime, you don't have any idea about what instance is needed. You need to create a dependency based on information you don't have until the last moment. Let us consider, for example, that you may need to create an instance of ServiceA if the user is logged in or ServiceB if the user is not logged in. Information about the logged in user may not available until the last minute or may change during the use of the application.

We need to use useFactory when information about the dependency is dynamic. Let us consider our two services used in the previous examples:

  1. ErrorMessageService
  2. NewErrorMessageService

For the token ErrorService on a particular condition, we want either instance of ErrorMessageService or newErrorMessageService. We can achieve that using useFactory as shown in the listing below:

providers: [
    {
        provide: ErrorService, useFactory: () => {
            let m = 'old'; // this value can change
            if (m === 'old') {
                return new ErrorService();
            } else {
                return new NewErrorService();
            }
        }
    }
]


Here, we have taken a very hardcoded condition. You may have complex conditions to dynamically create an instance for a particular token. Also, keep in mind that in useFactory, you can pass a dependency. So assume that you have a dependency on LoginService to create an instance for the ErrorService token. For that pass, LoginService is  deps array as shown in the listing below:

providers: [
    {
        provide: ErrorService, useFactory: () => {
            let m = 'old'; // this value can change
            if (m === 'old') {
                return new ErrorService();
            } else {
                return new NewErrorService();
            }
        },
        deps: [LoginService]
    }
]


These are fours ways of working with providers in Angular. I hope you find this post useful. Thanks for reading. If you like this post, please share it.

Take a look at the Indigo.Design sample applications to learn more about how apps are created with design to code software.

Topics:
angular ,dependency injection ,web dev ,tutorial ,web application development

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}