In Angular, dependency injection (DI) is a powerful pattern that allows you to manage the dependencies of your components and services efficiently. Hierarchical injectors are a key concept in Angular's DI system, enabling you to control the scope and lifetime of dependencies in a hierarchical manner.

Key Concepts

  1. Injector Hierarchy: Angular creates a tree of injectors that parallels the component tree. Each component has its own injector, and these injectors form a hierarchy.
  2. Provider Scope: Providers can be registered at different levels of the injector hierarchy, affecting their scope and lifetime.
  3. Singleton Services: Services provided at the root level are singletons, meaning only one instance of the service exists for the entire application.
  4. Component-level Providers: Services provided at the component level are unique to that component and its children.

Understanding Injector Hierarchy

Root Injector

The root injector is created when the application starts and is shared across the entire application. Services provided in the root injector are singletons.

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

Component-level Injector

Each component has its own injector. You can provide services at the component level, making them available only to that component and its children.

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

Child Component Injector

Child components inherit the injector from their parent unless they have their own providers.

@Component({
  selector: 'app-child',
  template: `<p>Child works!</p>`,
  providers: [ChildService]
})
export class ChildComponent {
  constructor(private childService: ChildService) { }
}

Practical Example

Let's create a simple example to demonstrate hierarchical injectors.

Step 1: Create Services

Create two services: ParentService and ChildService.

// parent.service.ts
@Injectable()
export class ParentService {
  constructor() { }
  getMessage() {
    return 'Message from Parent Service';
  }
}

// child.service.ts
@Injectable()
export class ChildService {
  constructor() { }
  getMessage() {
    return 'Message from Child Service';
  }
}

Step 2: Create Components

Create a parent component and a child component.

// parent.component.ts
@Component({
  selector: 'app-parent',
  template: `
    <p>{{ parentMessage }}</p>
    <app-child></app-child>
  `,
  providers: [ParentService]
})
export class ParentComponent {
  parentMessage: string;
  constructor(private parentService: ParentService) {
    this.parentMessage = this.parentService.getMessage();
  }
}

// child.component.ts
@Component({
  selector: 'app-child',
  template: `<p>{{ childMessage }}</p>`,
  providers: [ChildService]
})
export class ChildComponent {
  childMessage: string;
  constructor(private childService: ChildService) {
    this.childMessage = this.childService.getMessage();
  }
}

Step 3: Add Components to Module

Ensure that the components and services are declared and provided in the module.

@NgModule({
  declarations: [
    ParentComponent,
    ChildComponent
  ],
  providers: [],
  bootstrap: [ParentComponent]
})
export class AppModule { }

Step 4: Run the Application

When you run the application, you will see the messages from both the parent and child services displayed.

Common Mistakes and Tips

  • Singleton Services: Be cautious when providing services at the component level if you intend them to be singletons. Providing them at the root level ensures a single instance.
  • Service Scope: Understand the scope of your services. Services provided at the component level are not shared with other components unless they are children of the component.
  • Performance: Overusing component-level providers can lead to performance issues due to the creation of multiple instances of services.

Conclusion

Hierarchical injectors in Angular provide a flexible and powerful way to manage the scope and lifetime of your services. By understanding and utilizing the injector hierarchy, you can create efficient and maintainable Angular applications. In the next module, we will explore routing and navigation, which will help you build complex, multi-page applications with ease.

© Copyright 2024. All rights reserved