Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Advanced Dependency Injection in Angular

Advanced Dependency Injection (DI) in Angular involves more complex scenarios such as using multi-providers, creating injectors dynamically, and understanding the intricacies of hierarchical injectors. This section explores these advanced concepts.

Multi-Providers

Multi-providers allow you to provide multiple values for a single token. This can be useful for creating extensible features like middleware or plugins:

// logging.service.ts
import { InjectionToken } from '@angular/core';

export const LOGGING_TOKEN = new InjectionToken<string[]>('LoggingToken');

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { LOGGING_TOKEN } from './logging.service';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [
    { provide: LOGGING_TOKEN, useValue: 'LogEntry1', multi: true },
    { provide: LOGGING_TOKEN, useValue: 'LogEntry2', multi: true }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

// my.component.ts
import { Component, Inject } from '@angular/core';
import { LOGGING_TOKEN } from './logging.service';

@Component({
  selector: 'app-my',
  template: '
{{ log }}
', }) export class MyComponent { constructor(@Inject(LOGGING_TOKEN) public logs: string[]) { } }

In this example, LOGGING_TOKEN is provided multiple values using the multi: true option. The injected values are available as an array in the component.

Creating Injectors Dynamically

Sometimes, you might need to create injectors dynamically, especially when working with dynamically loaded components:

// dynamic-injector.service.ts
import { Injectable, Injector, ReflectiveInjector } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class DynamicInjectorService {
  constructor(private injector: Injector) { }

  createInjector() {
    return ReflectiveInjector.resolveAndCreate([], this.injector);
  }
}

Here, a dynamic injector is created using the ReflectiveInjector. You can use this custom injector to resolve dependencies dynamically at runtime.

Hierarchical Injectors

Angular uses a hierarchical dependency injection system. Providers can be configured at different levels, such as component and module levels, influencing the scope and lifecycle of the services:

// parent.component.ts
import { Component } from '@angular/core';
import { MyService } from '../my-service.service';

@Component({
  selector: 'app-parent',
  template: '',
  providers: [MyService]
})
export class ParentComponent {
  constructor(private myService: MyService) { }
}

// child.component.ts
import { Component } from '@angular/core';
import { MyService } from '../my-service.service';

@Component({
  selector: 'app-child',
  template: '
{{ message }}
', }) export class ChildComponent { message: string; constructor(private myService: MyService) { this.message = myService.getMessage(); } }

In this example, MyService is provided at the ParentComponent level. Therefore, the ChildComponent receives the same instance of MyService as the parent.

Using @Optional and @Self

Angular provides additional decorators like @Optional and @Self to control the resolution of dependencies:

// optional.service.ts
import { Injectable, Optional } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class OptionalService {
  constructor(@Optional() private dependency?: MyService) {
    if (this.dependency) {
      console.log('Dependency provided');
    } else {
      console.log('Dependency not provided');
    }
  }
}

Using @Optional, you can handle cases where a dependency might not be provided. The service handles both scenarios gracefully.

Key Points

  • Multi-providers allow multiple values for a single token, enabling extensible patterns.
  • Dynamic injectors can be created to resolve dependencies at runtime, useful for dynamic component loading.
  • Hierarchical injectors determine the scope and lifecycle of services, influenced by where providers are configured.
  • @Optional and @Self provide more control over dependency resolution, allowing for optional and self-restricted injections.

Conclusion

Advanced Dependency Injection in Angular provides powerful tools to manage dependencies in complex scenarios. By leveraging these advanced features, you can build more flexible and scalable Angular applications.