In-depth Guides
Dependency Injection

DI in action

This guide explores additional features of dependency injection in Angular.

Custom providers with @Inject

Using a custom provider allows you to provide a concrete implementation for implicit dependencies, such as built-in browser APIs. The following example uses an InjectionToken to provide the localStorage browser API as a dependency in the BrowserStorageService:

src/app/storage.service.ts

      
import { Inject, Injectable, InjectionToken } from '@angular/core';
export const BROWSER_STORAGE = new InjectionToken<Storage>('Browser Storage', {
providedIn: 'root',
factory: () => localStorage
});
@Injectable({
providedIn: 'root'
})
export class BrowserStorageService {
constructor(@Inject(BROWSER_STORAGE) public storage: Storage) {}
get(key: string) {
return this.storage.getItem(key);
}
set(key: string, value: string) {
this.storage.setItem(key, value);
}
}

The factory function returns the localStorage property that is attached to the browser's window object. The Inject decorator is applied to the storage constructor parameter and specifies a custom provider of the dependency.

This custom provider can now be overridden during testing with a mock API of localStorage instead of interacting with real browser APIs.

Inject the component's DOM element

Although developers strive to avoid it, some visual effects and third-party tools require direct DOM access. As a result, you might need to access a component's DOM element.

Angular exposes the underlying element of a @Component or @Directive via injection using the ElementRef injection token:

      
import { Directive, ElementRef } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private element: ElementRef) {}
update() {
this.element.nativeElement.style.color = 'red';
}
}

Resolve circular dependencies with a forward reference

The order of class declaration matters in TypeScript. You can't refer directly to a class until it's been defined.

This isn't usually a problem, especially if you adhere to the recommended one class per file rule. But sometimes circular references are unavoidable. For example, when class 'A' refers to class 'B' and 'B' refers to 'A', one of them has to be defined first.

The Angular forwardRef() function creates an indirect reference that Angular can resolve later.

You face a similar problem when a class makes a reference to itself. For example, in its providers array. The providers array is a property of the @Component() decorator function, which must appear before the class definition. You can break such circular references by using forwardRef.

app.component.ts

      
providers: [
{
provide: PARENT_MENU_ITEM,
useExisting: forwardRef(() => MenuItem),
},
],