Dependency Providers

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.

You must configure an injector with a provider, or it won't know how to create the dependency. The most obvious way for an injector to create an instance of a service class is with the class itself. If you specify the service class itself as the provider token, the default behavior is for the injector to instantiate that class with new keyword.

In the following typical example, the Logger class itself provides a Logger instance.

providers: [Logger]

You can, however, configure an injector with an alternative provider, in order to deliver some other object that provides the needed logging functionality. For instance:

  • You can provide a substitute class.
  • You can provide a logger-like object.
  • Your provider can call a logger factory function.

The Provider object literal

The class-provider syntax is a shorthand expression that expands into a provider configuration, defined by the Provider interface.

The code snippet below shows how a class that is given as the providers value is expanded into a full provider object.

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

The expanded provider configuration is an object literal with two properties.

  • The provide property holds the token that serves as the key for both locating a dependency value and configuring the injector.
  • 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 above. It can also be useExisting, useValue, or useFactory. Each of these keys provides a different type of dependency, as discussed below.

Alternative class providers

Different classes can provide the same service. For example, the following code tells the injector to return a BetterLogger instance when the component asks for a logger using the Logger token.

[{ provide: Logger, useClass: BetterLogger }]

Aliased class providers

Suppose an old component depends upon the OldLogger class. OldLogger has the same interface as NewLogger, but for some reason you can't update the old component to use it.

When the old component logs a message with OldLogger, you want the singleton instance of NewLogger to handle it instead. In this case, the dependency injector should inject that singleton instance when a component asks for either the new or the old logger. OldLogger should be an alias for NewLogger.

[ NewLogger,
  // Not aliased! Creates two instances of `NewLogger`
  { provide: OldLogger, useClass: NewLogger}]

To make sure there is only one instance of NewLogger, alias OldLogger with the useExisting option.

[ NewLogger,
  // Alias OldLogger w/ reference to NewLogger
  { provide: OldLogger, useExisting: NewLogger}]

Value providers

Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class. To inject an object you have already created, configure the injector with the useValue option like so:

```[{ provide: Logger, useValue: silentLogger }]```

Non-class dependencies

Not all dependencies are classes. Sometimes you want to inject a string, function, or object.

Apps often define configuration objects with lots of small facts, like the title of the application or the address of a web API endpoint. These configuration objects aren't always instances of a class. They can be object literals, as shown in the following example.

export const HERO_DI_CONFIG: AppConfig = {
  apiEndpoint: 'api.heroes.com',
  title: 'Dependency Injection'

Factory providers

Sometimes you need to create a dependent value dynamically, based on the information you won't have until run time. For example, you might need information that changes repeatedly in the course of the browser session. Also, your injectable service might not have independent access to the source of the information.

In cases like this, you can use a factory provider. Factory providers can also be useful when creating an instance of a dependency from a third-party library that wasn't designed to work with DI.

Previous: Dependency Injection in Angular
Next: Hierarchical dependency injectors

Follow us on Facebook and Twitter for latest update.