Providers and Dependency Injection

In this lesson, we will learn about providers and hierarchical dependency injection in Angular.,

To understand the role of providers in the dependency injection of services, let’s look at the following example:

constructor(childService: ChildService) {
  logger.logInfo('Creating HeroBiosComponent');
}

In this case, Angular asks for the injector associated with the service and assigns the returned value to the childService parameter.

In case the injector is not able to find the associated token, it goes to ask the parent injector for the same, and the process repeats until an injector is found.

The way different providers are defined are:

 providers: [
    { provide: Hero, useValue: someHero },
    { provide: TITLE, useValue: 'Hero of the 
      Month' },
    { provide: HeroService, useClass: 
      HeroService },
    { provide: LoggerService, useClass: 
      DateLoggerService },
    { provide: MinimalLogger, useExisting: 
      LoggerService },
    { provide: RUNNERS_UP, 
      useFactory:runnersUpFactory(2), deps: 
      [Hero, HeroService] }
  ]

Let’s understand these providers one by one:

  1. Value Providers: useValue
  2. Class Providers: useClass
  3. Alias Providers: useExisting
  4. Factory providers: useFactory

Value providers: useValue

This allows you to add a fixed value as a dependency injection token. We can add runtime config here, such as some feature flags or the base addresses for websites.

The important thing to note here is that we can only provide the properties that have already defined values. This is different for all the other providers as we can provide values lazily as in when they are needed for injection.

Example:

{ provide: User, useValue: oneUser}

Class providers: useClass

We can create and return a new instance of a class by using the useClass provider. This helps us use a default class and perform an alternate implementation of it as required.

{ provide: DateService, useClass: CalendarService}

In this particular scenario, it would receive the instance of CalendarService when trying to access the DateService.

This helps in emulating or extending the behavior of a class.

Alias provider: useExisting

This helps us get two ways to access the same service by allowing us to map one token to another, and there they would point to the same instance of that service.

This is best used when you want to capture only a part of one service instead of calling one whole API, so you can keep one token to access that minimized version of your service.

Like so:

{ provide: MinimalService, useExisting: MainService}
export abstract class MainService{
  logs: string[];
  answers: string[], 
  getVal(){
     return val;
  }
}
export abstract class MinimalService {
  logs: string[];
}

Factory providers: useFactory

We can also create a dependency value by calling a factory functioning and dynamically creating the dependencies object that would specify the value.

The object can return a class instance or a string, etc.

We need to provide two inputs to our factory function - injected service and the local state.

Example:

{ provide: USERS, useFactory:  usersFactory(2), deps: [User, UserService] }

The most important ones to know providers are these, and they come in really handy when dealing with different ways to provide tokens and improving the way dependency injection goes in your application.

In the next section, we will look at how to deal with AJAX operations using Angular and HttpClient in Angular.

In the code example below, have a look at the app.module.ts and see how these are wired together. you do not need to run the application but play around with the different DI providers and techniques.

Note: The following code does not compile. 2 relevant files here are: app.module.ts, and app.component.ts

Only have a look at the sytax of the providers in the aforementioned files, below code does not compile! ❌

Get hands-on with 1000+ tech skills courses.