providers: [
        {
          provide: CheckoutDeliveryService,
          useClass: MockCheckoutDeliveryService,
        },
Copy the code

A dependency provider configures an injector with a DI token, which that injector uses to provide the concrete, runtime version of a dependency value. The injector relies on the provider configuration to create instances of the dependencies that it injects into components, directives, pipes, and other services.

Provider configures the Injector with a DI token. The latter uses this token to create a specific runtime instance that can be injected into components, directives, pipes, and other services.

The following two forms are equivalent:

  • providers: [Logger]
  • [{ provide: Logger, useClass: Logger }]

Provide fields: holds the token that serves as the key for both Locating a dependency value and configuring the Injector.

A token was stored as the key for locating a dependency value and configuring the Injector.

The second value is provider Definition Object. The provider’s definition object tells the Injector how to create the dependent value. In addition to useClass, there are useExisting, useValue, useFactory, etc.

The second property is a provider definition object, which tells the injector how to create the dependency value. The provider-definition key can be useClass, as in the example. It can also be useExisting, useValue, or useFactory. Each of these keys provides a different type of dependency, as discussed below.

Different classes can provide the same service.

The implication of the following code is that if there is code that needs a Logger, return a BetterLogger instance.

[{ provide: Logger, useClass: BetterLogger }]
Copy the code

Take a look at this code:

@Injectable(a)export class EvenBetterLogger extends Logger {
  constructor(private userService: UserService) { super(a); }log(message: string) {
    const name = this.userService.user.name;
    super.log(`Message to ${name}: ${message}`); }}Copy the code

The correct Injector configuration method:

[ UserService,
  { provide: Logger, useClass: EvenBetterLogger }]
Copy the code

Value providers

Sometimes we can also have the Injector return a prepared object, the value Provider configuration.

For example, the following object:

// An object in the shape of the logger service
function silentLoggerFn() {}

export const SilentLogger = {
  logs: ['Silent logger says "Shhhhh!" . Provided via "useValue"'].log: silentLoggerFn
};
Copy the code

Use useValue to return this object:

[{ provide: Logger, useValue: SilentLogger }]
Copy the code

How do you just inject a simple string?

export const HERO_DI_CONFIG: AppConfig = {
  apiEndpoint: 'api.heroes.com'.title: 'Dependency Injection'
};
Copy the code

The HERO_DI_CONFIG constant conforms to the AppConfig interface. Unfortunately, you cannot use a TypeScript interface as a token. In TypeScript, an interface is a design-time artifact, and doesn’t have a runtime representation (token) that the DI framework can use.

A TypeScript interface is a design time concept, and the DI framework requires a runtime carrier called a DI token, so interfaces don’t directly participate in Angular DI.

Solution 1

Provide and inject configuration objects in ngModules:

providers: [
  UserService,
  { provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
],
Copy the code

Solution 2

Using InjectionToken object.

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

export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
Copy the code

Now you can register dependencies:

providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]
Copy the code

We can use this dependency in any constructor:

constructor(@Inject(APP_CONFIG) config: AppConfig) {
  this.title = config.title;
}
Copy the code