# Dependency Injection

{% embed url="<https://angular.dev/guide/di/dependency-injection>" %}
dependency-injection
{% endembed %}

## About

The two main roles of DI in Angular are:

* Dependency consumer;
* Dependency provider.

Angular facilitates the interaction between these two roles using an abstraction called `Injector`.

When a dependency is requested, the `Injector` checks its registry to see if there is an instance already available there.

If not, a new instance is created and stored in the registry.

Angular can create an application-wide injector known as `root` injector, during the bootstrap process.

## Providing a Dependency

### [Injection Context](https://angular.dev/guide/di/dependency-injection-context)

The DI system relies internally on a **runtime context** where the current injector is available. This means that injectors can only work when code is executed in such context.

{% hint style="danger" %}
This means in pratice that you cannot call `inject()` anywhere.

*Knowing when you are in an injection context will allow you to use the* `inject()`*.*

Check the docs to see specific places that have injection context.
{% endhint %}

Although there are specific places that have injection context, you can also create an injection context with [`runInInjectionContext`](https://angular.dev/guide/di/dependency-injection-context#run-within-an-injection-context).

### At application root level using `providedIn`

```typescript
@Injectable({
    providedIn: 'root'
})
```

Angular creates a **single, shared instance** of the service, and injects it into any class you ask for it.

Using `providedIn: 'root'` when creating a service, allows injecting the service into all other classes.

{% hint style="danger" %}
**You must not** declare the service in `@Component({ providers: [] })` array when using it, or it will create new instances.
{% endhint %}

{% hint style="info" %}
If you want to call a `Service A` inside another `Service B`, then `Service A` will have to be `providedIn: 'root'`.
{% endhint %}

{% hint style="success" %}
It enables Angular code optimizers to effectively remove services that are unused.&#x20;

Known as [#tree-shaking](#tree-shaking "mention").
{% endhint %}

{% hint style="info" %}
*Will use the* `root injector`*.*
{% endhint %}

### At component level

You can provide services at `@Component` level by using the `providers: []` property in the component decorator.

```typescript
@Component({
    providers: [SomeService]
})
```

*The default behavior is for the injector to instantiate that class using* `new` *operator.*

In this case, the service becomes available to all instances of this component and other components and directives used in the Template.

{% hint style="danger" %}
Declaring services like this causes the services to always be included in your application - even if the service is unused.

No [#tree-shaking](#tree-shaking "mention").
{% endhint %}

{% hint style="info" %}
*Will use* `component-specific injector`*.*
{% endhint %}

#### [Specifying a provider Token](https://angular.dev/guide/di/dependency-injection-providers#specifying-a-provider-token)

You can configure DI to associate a Service `provider token` with a different class or other values like.

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

* The `provide` property holds the token that serves as the key for consuming the dependency value.
* The second property is a provider definition object, which tells the injector **how** to create the dependency value. This can be one of the following:
  * `useClass`
  * `useExisting`
  * `useFactory`
  * `useValue`

#### `useClass`

```typescript
[{ provide: Logger, useClass: TestLogger }]
```

Tells Angular DI to instanciate a provided class when a dependency is injected.

{% hint style="success" %}
Useful to substitute/extend an alternative implementation for a common or default class.

Or even emulate the behavior of the real class in a test case.
{% endhint %}

{% hint style="warning" %}
If the alternative class providers have their own dependencies, specify both providers in the providers metadata property of the parent module or component.
{% endhint %}

```typescript
[
    UserService, // dependency needed in `EvenBetterLogger`.
    { provide: Logger, useClass: EvenBetterLogger }
]
```

#### `useExisting`

Allows you to mapo one token to another. *(Alias a token and reference any existing one)*

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.

```typescript
[
    NewLogger,
    // Alias OldLogger with reference to NewLogger
    { provide: OldLogger, useExisting: NewLogger},
]
```

#### `useFactory`

Allows you to create a dependency object by calling a factory function. With this approach, you can create a dynamic value based on information available in the DI and elsewhere in the app.

[Check the example in the docs.](https://angular.dev/guide/di/dependency-injection-providers#factory-providers-usefactory)

#### `useValue`

Lets you associate a static value with a DI token.

{% hint style="success" %}
Use this technique to provide runtime configuration constants such:

* Website base addresses.
* Feature flags.

Also use a value provider in a unit test to provide mock data in place of a production data service.
{% endhint %}

The following example show how to inject global data variables. *(*`APP_DATA_TOKEN` *and* `APP_DATA` *don't have to be created in the same file)*

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

interface AppData {
    ip: string;
    env: string;
    ...
}

// `InjectionToken` requires a parameter which is just a description for the Token
const APP_DATA_TOKEN = new InjectionToken<AppData>('app.data description');
const APP_DATA: AppData = { ip: '127.0.0.1', env: 'development' };

@Component({
    providers: [{ provide: APP_DATA_TOKEN, useValue: APP_DATA }]
})
export class Component {
    constructor(@Inject(APP_DATA_TOKEN) appData: AppData) {}
}
```

### At application root level using `ApplicationConfig`

```typescript
export const appConfig: ApplicationConfig = {
    providers: [{ provide: HeroService }],
};
```

The service will be available to all components, directives and pipes.

{% hint style="danger" %}
But the service will always be included in your application - even if the service is unused.

No [#tree-shaking](#tree-shaking "mention").
{% endhint %}

## [Hierarchical Injectors](https://angular.dev/guide/di/hierarchical-dependency-injection)

Injectors have rules that you can leverage to achieve the desired visibility of injectables in your application.

*There may be sections in your application that work completely independent, with its own local copies of the services and other dependencies that it needs.*

With hierarchical DI, you can:

* Isolate sections of the application and give them their own private dependencies.
* Have parent components share certain dependencies with its child components only.

### Resolution Rules (Sequence)

1. When a Component declares a dependency, Angular tries to satisfy the dependency with its own `ElementInjector`.
2. If the components lacks the provider, it passes the request up to its parent `ElementInjector`.
3. This request keeps forwarding up until Angular finds an injector that can handle the request or runs out of ancestors `ElementInjector`.
4. If it still could not find it, it goes back to the element where the request originated and looks in the `EnvironmentInjector` hierarchy.
5. If it still does not find it, it throws an error.

If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to resolve the dependency.

### Injector Hierarchies

Angular has two injector hierarchies:

#### `EnvironmentInjector`

Can be configured in one of two ways:

* The `@Injectable({ providedIn: root | plataform })` property to refer to `root` or `plataform`.
* The `ApplicationConfig` `providers` array.

#### Tree-Shaking

Only available with `@Injectable({ providedIn })` method.

{% hint style="success" %}
Is an optimization tool, which removes services that your application isn't using, resulting in smaller bundle sizes.
{% endhint %}

#### `ElementInjector`

Angular creates `ElementInjector` hierarchies implicitly for each DOM element.

Providing a service in the `@Component` using `providers: []` or `viewProviders` property configures an `ElementInjector`.

{% hint style="info" %}
[`providers` vs `viewProviders`.](https://angular.dev/guide/di/hierarchical-dependency-injection#providers-vs-viewproviders)
{% endhint %}

```typescript
@Component({
    providers: []
    // Or
    viewProviders: []
})
```

Services provided like this are available at that **component instance**, **and may be visible at child component/directives based on visibility rules**. *(Components and directive on the same element share an injector)*

When the component instance is destroy, so is that service instance.

### [Resolution Modifiers](https://angular.dev/guide/di/hierarchical-dependency-injection#resolution-modifiers)

Angular's resolution behavior can be modified.

Use them when injecting services, in:

* Component class constructor.
* Or the `inject()` configuration.

#### `@Optional()`

Allows Angular to consider a service you inject to be optional.

So if it cannot be resolved at runtime, Angular resolves the service as `null`, rather than throwing an error.

```typescript
export class Component {
    constructor(@Optional() private logger?: LoggerService) {}
}
```

#### `@Self()`

Use it so that Angular will only look at the `ElementInjector` for the current component or directive. *(It will not go up to it's ancestors)*

*A good case for* `@Self()` *is to inject a service but only if it is available on the current host element.*

{% hint style="info" %}
It's good practice to use `@Self()` with `@Optional()`.
{% endhint %}

In the following example, if `LoggerService` is provided inside `Component` class it will be availble, otherwise it will be `null`, because it won't check if the parent component of `Component` has it.

```typescript
export class Component {
    constructor(@Self() @Optional() private logger?: LoggerService) {}
}
```

#### `@SkipSelf()`

It is the opposite of `@Self()`, so Angular starts looking in the parent `ElementInjector`, rather than in the current one.

{% hint style="warning" %}
`@Self()` and `@SkipSelf()` are exclusive from one another.
{% endhint %}

{% hint style="info" %}
Also use `@SkipSelf()` with `@Optional()` to prevent an error if the value is `null`.
{% endhint %}

In the following example, any instance of `LoggerService` provided inside `Component` class will be ignored. Angular will try to get it from `Component` parent, and if the parent don't provide it then it will be `null`.

```typescript
export class Component {
    constructor(@SkipSelf() @Optional() private logger?: LoggerService) {}
}
```

#### `@Host()`

Lets you designate a component as the last stop in the injector tree when searching for providers.

Even if there is a service instance further up the tree, Angular won't continue looking.

{% hint style="warning" %}
`@Host()` and `@Self()` are exclusive from one another.
{% endhint %}
