DEV Community

Cover image for How to use Angular Provider
Abhishek Keshri for Incubyte

Posted on • Updated on • Originally published at blog.incubyte.co

How to use Angular Provider

Angular Provider is like a command to Angular Dependency Injection system regarding how to get an object/instance for a particular dependency.

This helps us to register dependencies, like classes or functions or even values with the Angular Dependency Injection system.

Now you must be wondering what on earth is the Angular Dependency Injection system?

Dependency Injection

Let's first see what is a Dependency.

Dependency is a service or an object that a class needs to perform its function.

Dependency injection, or DI, is a design pattern in which a class requests dependencies from external sources rather than creating them.

Example

We have a CarFactory in which Car object is created.

class CarFactory {
  car: Car = new Car(); 
}
Enter fullscreen mode Exit fullscreen mode

And here is our Car

export class Car {
  engine: Engine;

  constructor() {
    this.engine = new Engine();
  }
}

export class Engine {}
Enter fullscreen mode Exit fullscreen mode

In the above example, in order to create a Car, we first need to create an Engine.

Because we are instantiating the Engine class inside the constructor of the Car class with new keyword, there is a tight coupling between the Car class and the Engine class.

And this is a Problem.

Actually there are many types of Engine

Like a Flat Engine

export class FlatEngine {}
Enter fullscreen mode Exit fullscreen mode

or an Inline Engine

export class InlineEngine {}
Enter fullscreen mode Exit fullscreen mode

The problem here is that if a Car class requires a specific type of Engine it can't have one.

This Car can only have a generic Engine because of the tight coupling.

export class Car {
    engine: Engine;

    constructor() {
        this.engine = new Engine();
    }
}
Enter fullscreen mode Exit fullscreen mode

This problem can be resolved by making both the class loosely coupled with each other.
For that, first we will turn our Engine class to an interface like this

interface class Engine {}
Enter fullscreen mode Exit fullscreen mode

and let our FlatEngine and InlineEngine implement it.

export class FlatEngine implements Engine {}

export class InlineEngine implements Engine {}
Enter fullscreen mode Exit fullscreen mode

With that our Car class will look like this

export class Car {
    constructor(private engine: Engine) {}
}
Enter fullscreen mode Exit fullscreen mode

The responsibility of object creation of Engine is no more with the Car class. And the dependency will be directly injected through the constructor of the Car class.

The Car Factory can now have Car with different Engine types

class CarFactory {
    flatEngine: Engine = new FlatEngine();
    porsche911: Car = new Car(this.flatEngine);

    inlineEngine: Engine = new InlineEngine();
    bmwM88: Car = new Car(this.inlineEngine);
}
Enter fullscreen mode Exit fullscreen mode

That was just a basic example of dependency injection.

Now let's get back to Angular Provider.

Configuring the Angular Provider

To Provide an instance of the dependency, we need to register it in the Providers metadata.

 providers: [CarService]
Enter fullscreen mode Exit fullscreen mode

The above is an actual shorthand notation for the following syntax:

 providers: [{ provide: CarService, useClass: CarService }]
Enter fullscreen mode Exit fullscreen mode

As you can see in the above example, the object has 2 properties. They are provide and provider.

1. Provide

The first property is Provide holds the Token or DI Token
The Injector uses the token to locate the provider in the Providers array

Provide in Angular

Type Token

Here the type being injected is used as the token.
For Example, we would like to inject the instance of the CarService, we will use the CarService as the token as shown below

 providers: [{ provide: CarService, useClass: CarService }]
Enter fullscreen mode Exit fullscreen mode

The CarService is then injected to the component by using the following code.

export class CarComponent {
    constructor(private carService: CarService) { }
}
Enter fullscreen mode Exit fullscreen mode

String Token

We can use a string literal to register the dependency. This is useful in scenarios where the dependency is a value or object etc., which is not represented by a class.

{ provide: 'CAR_SERVICE', useClass: CarService },
{ provide: 'API_URL', useValue: 'https://randomcar.com/api' }
Enter fullscreen mode Exit fullscreen mode

It is then injected using the @Inject in the constructor of the service/component.

constructor(
    @Inject('CAR_SERVICE') private carService: CarService,
    @Inject('API_URL') private apiURL: string
) {}
Enter fullscreen mode Exit fullscreen mode

Injection Token

The Angular provides InjectionToken class to ensure that the Unique tokens are created.
The Injection Token is created by creating a new instance of the InjectionToken class.

  export const API_URL = new InjectionToken<string>('api.url');
Enter fullscreen mode Exit fullscreen mode

Register the token in the providers array.

   providers: [{ provide: API_URL, useValue: 'https://randomcar.com/api' }]
Enter fullscreen mode Exit fullscreen mode

It is then injected using the @Inject in the constructor of the service/component.

  constructor(@Inject(API_URL) private apiURL: string) {}
Enter fullscreen mode Exit fullscreen mode

2. Provider

The second property is the Provider definition object.
It tells Angular how to create the instance of the dependency.
The Angular can create the instance of the dependency in four different ways.

Angular Provider

Class Provider

useClass is used when you want to provide an instance of the provided class.
It expects us to provide a type. The Injector creates a new instance from the type and injects it. It is like calling the new operator and returning instance. If the type requires any constructor parameters, the injector will resolve that also.

  { provide: CarService, useClass: CarService }
Enter fullscreen mode Exit fullscreen mode

We can also switch dependencies easily. You can provide a mock class for Testing purposes as shown below.

  { provide: CarService, useClass: MockCarService }
Enter fullscreen mode Exit fullscreen mode

Value Provider

useValue is used when you want to provide a simple value.
The Angular will inject whatever provided in the useValue as it is.
It is useful in scenarios like, where you want to provide API URL, application-wide configuration etc

export const APP_CONFIG = Object.freeze({
  devUrl: 'https://dev.randomcar.com/api',
  IsDevelopmentMode: true
});
Enter fullscreen mode Exit fullscreen mode

And we can use the APP_CONFIG as provided in the providers array.

  providers: [{ provide: 'API_CONFIG', useValue: APP_CONFIG }]
Enter fullscreen mode Exit fullscreen mode

Factory Provider

useFactory expects us to provide a function.
It invokes the function and injects the returned value. We can also add optional arguments to the factory function using the deps array. The deps array specifies how to inject the arguments.
We usually use the useFactory when we want to return an object based on a certain condition.

{
   provide: CarService,
   useFactory: (config: any, loggerService: LoggerService) => return config.IsDevelopmentMode 
        ? new MockCarService(loggerService) 
        : new CarService(),
   deps: [APP_CONFIG, LoggerService]
}
Enter fullscreen mode Exit fullscreen mode

Aliased Provider

The useExisting provider key lets you map one token to another.
In effect, the first token is an alias for the service associated with the second token, creating two ways to access the same service object.

  { provide: CarService, useExisting: 'MOCK_CAR_SERVICE' },
  { provide: 'MOCK_CAR_SERVICE', useClass: MockCarService },
Enter fullscreen mode Exit fullscreen mode

Like what you read? then visit our site to learn more.

We are also looking for awesome people to work with, if you'd like to be a part of our team join us!

Top comments (0)