Angular Dependency Injection


What is Dependency Injection in Angular?

Dependency Injection (DI) in Angular is a design pattern where objects or services are provided to a class, rather than the class creating the objects itself. Angular's DI system is used to inject services, dependencies, or other components into Angular classes (such as components, directives, and other services) to promote modularity, testability, and maintainability.


How does Dependency Injection work in Angular?

In Angular, services or dependencies are provided to a class (like a component) via its constructor. The Angular DI system manages the creation and injection of these services when the component or other class is instantiated. The services are provided using the @Injectable decorator and are made available at different levels (root, module, component) using the providers array.

Example of dependency injection:

import { Component } from '@angular/core';
import { ExampleService } from './example.service';

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html'
})
export class ExampleComponent {
  message: string;

  constructor(private exampleService: ExampleService) {
    this.message = this.exampleService.getData();
  }
}

In this example, the ExampleService is injected into the ExampleComponent using the constructor, allowing the component to use the service's methods and properties.


What is the @Injectable decorator in Angular?

The @Injectable decorator in Angular is used to declare that a class can be injected as a service. It tells Angular's dependency injection system that the class can be instantiated and provided as a dependency to other classes, such as components or other services.

Example of using @Injectable:

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

@Injectable({
  providedIn: 'root'
})
export class ExampleService {
  constructor() { }

  getData() {
    return 'Hello from Example Service!';
  }
}

In this example, the @Injectable decorator with providedIn: 'root' ensures that the ExampleService is available globally in the application and is a singleton.


What are the different levels at which a service can be provided in Angular?

Services in Angular can be provided at different levels, determining their scope and lifetime:

  • Root level: When a service is provided using providedIn: 'root' in the @Injectable decorator, it is available application-wide as a singleton.
  • Module level: A service can be provided in a specific module by adding it to the module's providers array. The service will be scoped to that module.
  • Component level: A service can be provided at the component level by adding it to the providers array of a component. The service will be unique to that component and its children.

Example of providing a service at the root level:

@Injectable({
  providedIn: 'root'
})
export class ExampleService {
  constructor() { }
}

In this example, the service is provided at the root level and is available application-wide as a singleton.


What is the providedIn property in the @Injectable decorator?

The providedIn property in the @Injectable decorator specifies the scope or level at which the service is provided. By setting providedIn: 'root', the service is registered at the root level and is a singleton across the entire application. You can also provide services in specific modules or components by setting providedIn to other values or using the providers array in modules or components.

Example:

@Injectable({
  providedIn: 'root'
})
export class ExampleService {
  constructor() { }
}

In this example, providedIn: 'root' makes the service available globally in the application, ensuring that only one instance of the service is created.


What is the difference between providedIn: 'root' and providing a service in the module's providers array?

The key difference is the scope and lifetime of the service:

  • providedIn: 'root': The service is available application-wide as a singleton and will be lazy-loaded when first used.
  • providers array in module: The service is scoped to the module and will be available only to components and services within that module. Each module can create its own instance of the service.

Example of providing a service in a module:

import { NgModule } from '@angular/core';
import { ExampleService } from './example.service';

@NgModule({
  providers: [ExampleService]
})
export class ExampleModule { }

In this example, the ExampleService is provided only to the components in the ExampleModule.


What is hierarchical dependency injection in Angular?

Hierarchical dependency injection in Angular refers to the way Angular manages service instances based on where they are provided in the module or component hierarchy. Services provided at a higher level (e.g., root or module level) are shared across components and modules, while services provided at a lower level (e.g., component level) create new instances specific to that component and its children.

Example of hierarchical DI:

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

@Injectable({
  providedIn: 'root'
})
export class SharedService {
  constructor() { }
}

@Component({
  selector: 'app-parent',
  template: '<app-child></app-child>',
  providers: [SharedService]
})
export class ParentComponent {
  constructor(private sharedService: SharedService) { }
}

@Component({
  selector: 'app-child',
  template: '<p>Child Component</p>',
})
export class ChildComponent {
  constructor(private sharedService: SharedService) { }
}

In this example, the SharedService is provided at the ParentComponent level, so a new instance of the service is created for the parent and all its child components.


How do you inject one service into another in Angular?

You can inject one service into another by specifying the dependency in the constructor of the service that requires it. This is similar to injecting a service into a component. Angular's DI system will resolve the dependency and inject the required service.

Example of injecting one service into another:

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

@Injectable({
  providedIn: 'root'
})
export class LoggerService {
  log(message: string) {
    console.log(message);
  }
}

@Injectable({
  providedIn: 'root'
})
export class DataService {
  constructor(private logger: LoggerService) {}

  fetchData() {
    this.logger.log('Fetching data');
  }
}

In this example, the LoggerService is injected into the DataService via the constructor, allowing the data service to use the logger service's functionality.


What is the @Inject decorator, and when would you use it?

The @Inject decorator is used to explicitly specify a dependency for injection. This is especially useful when the dependency being injected is not a class (e.g., a string, token, or value). The @Inject decorator tells Angular what to inject, even when the type cannot be inferred from the constructor parameter.

Example of using @Inject:

import { Injectable, Inject } from '@angular/core';
import { APP_CONFIG, AppConfig } from './app.config';

@Injectable({
  providedIn: 'root'
})
export class ConfigService {
  constructor(@Inject(APP_CONFIG) private config: AppConfig) {
    console.log(config.apiUrl);
  }
}

In this example, the @Inject decorator is used to inject the APP_CONFIG token, which provides configuration data for the application.


What is multi-provider token in Angular?

A multi-provider token in Angular allows multiple providers to be associated with a single token. This is useful when you need to inject an array of values or services under a common token. Multi-providers are declared by setting multi: true in the provider definition.

Example of a multi-provider token:

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

export const MULTI_SERVICE_TOKEN = new InjectionToken<string[]>('MultiServiceToken');

@NgModule({
  providers: [
    { provide: MULTI_SERVICE_TOKEN, useValue: 'Service 1', multi: true },
    { provide: MULTI_SERVICE_TOKEN, useValue: 'Service 2', multi: true }
  ]
})
export class AppModule { }

In this example, the MULTI_SERVICE_TOKEN is associated with multiple values, and Angular will inject an array containing both values wherever the token is injected.


What is an Injection Token in Angular?

An Injection Token in Angular is used to create a token that allows you to inject non-class dependencies (e.g., configurations, values, or multi-providers). It is created using the InjectionToken class and can be used in conjunction with the @Inject decorator to inject the dependency.

Example of creating and using an Injection Token:

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

export const APP_CONFIG = new InjectionToken<string>('AppConfig');

@NgModule({
  providers: [
    { provide: APP_CONFIG, useValue: 'https://api.example.com' }
  ]
})
export class AppModule { }

In this example, the APP_CONFIG token is created and associated with a configuration value (a URL). This token can then be injected into services or components using the @Inject decorator.

Ads