English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Angular2 Detailed explanation and examples of multi-level injector

angular2 Dependency injection contains too much content, one of the key points is the injector, which is very difficult to understand. Today we will not go into the content of the injector, you can refer to the official documentation. Today we will talk about the hierarchy of the injector.

That is, the container for the component to obtain the service will choose which one specifically.

First, let's briefly introduce a background: there is3The components include AppComponent (root component), DetailList component (log list component), and Detail component (log component).

These three components will form a component tree, and we can also consider that each component will have an independent injector (it may not appear, but we can think of it this way).

Add a logging service LoggerService. If we follow the usual entry-level method, we provide LoggerService in the root module providers. Then LoggerService will have only one instance in the entire application, what does this mean? It means that no matter which component, the obtained is the LoggerService created for the first time, and all components share a service instance. This can sometimes be a useful feature, such as the global configuration we use.

Global is unique and not the focus of this verification, because it is too common. What we want to explain this time is how we can obtain a separate LoggerService instance in each component, that is, each component's instance is different. This requires an understanding of ng2It is necessary to understand the dependency injection.

Let's explain step by step how to implement it?

To make it easier for those who read this short article to understand, I have added some basic code.

1.app.module.ts Application root module. Note that we have not registered loggerService in Providers here. Of course, registration can also achieve our purpose through the method behind.

import { NgModule, Optional, SkipSelf, ReflectiveInjector} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
/*  */
import { AppComponent } from '.',/app.component';
import { routing } from '.',/app.routing';
import { Title } from '@angular/platform-browser';
import {MessagesModule, GrowlModule, ButtonModule}from 'primeng/primeng';
import {AppDetailComponent}from '.',/app-detail.component';
import {AppDetailListComponent}from '.',/app-detailList.component';
import {LoggerService}from './logger.service';
let allTitle:string="Guo Zhiqi";
@NgModule({
 imports: [
 BrowserModule,
 MessagesModule,
 GrowlModule, ButtonModule
 ],
 declarations: [AppComponent, AppDetailComponent, AppDetailListComponent],//Declare the specific component information required by the current module
 exports: [],
 providers: [Title],
 bootstrap: [AppComponent]
}
export class AppModule {
 constructor( @Optional() @SkipSelf() parentModule: AppModule) {
 console.log(parentModule);
 if (parentModule) {
  throw new Error(
  'AppModule is already loaded. Import it in the AppModule only');
 }
 }
}

2.app.component.ts The root component of the application

import { Component, ViewEncapsulation, Host, ViewContainerRef, ReflectiveInjector } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Message } from 'primeng/primeng';
import {LoggerService}from './logger.service';
@Component({
 selector: 'my-app',
 moduleId: module.id,
 templateUrl: '.',/app.component.html',
 providers: [
  { provide: LoggerService, useClass: LoggerService }
 ]
}
export class AppComponent {}}
 subtitle = '(Final)';
 private msgs: Message[];
 constructor(private title: Title, @Host() private logger: LoggerService) {
  this.title.setTitle("AppComponent");
 }
 show(): void {
  this.logger.Debug();
 }
}

Note that we registered LoggerService in the providers of the root component.

3.app.detailList.ts Log list providers also registered LoggerService

import {Component, Host}from '@angular/core';
import {LoggerService}from './logger.service';
@Component({
 selector: 'my-detailList',
 templateUrl: '.',/app-detailList.component.html',
 moduleId: module.id,
 providers: [
  { provide: LoggerService, useClass: LoggerService }
 ]
}
export class AppDetailListComponent {
 constructor(private logger: LoggerService) {
 }
 show(): void {
  this.logger.Debug();
 }
}

 4.app.detail.ts Log component providers did not register LoggerService.

import {Component, Host}from '@angular/core';
import {LoggerService}from './logger.service';
@Component({
 selector: 'detail',
 moduleId: module.id,
 templateUrl: '.',/app-detail.component.html',
 providers: [
  // { provide: LoggerService, useClass: LoggerService }
 ]
}
export class AppDetailComponent {
 constructor(private logger: LoggerService) {
 }
 show(): void {
  this.logger.Debug();
 }
}

 Now let's take a look at the hierarchical relationship of LoggerService through chrome.

 

By viewing the dependency diagram, we can see that the AppComponent component uses a separate LoggerService, the DetailList component also uses a separate LoggerService instance, and the Detail component uses the LoggerService instance of the parent component DetailList.

Currently, it does not meet our requirements. Our requirement is that each component has a separate LoggerService instance. Then we assume that the providers of the Detail component are the ones we forgot to input, it is difficult to test out the cause. Then we add a @Host() to limit the search range of the injector.

For the upward lookup method of the injector, please refer to the official documentation.

To facilitate debugging, we add @Host().

The @Host decorator limits the search behavior to the host component

detail.ts prompts the detail component to add the @Host() decorator

import {Component, Host}from '@angular/core';
import {LoggerService}from './logger.service';
@Component({
 selector: 'detail',
 moduleId: module.id,
 templateUrl: '.',/app-detail.component.html',
 providers: [
  // { provide: LoggerService, useClass: LoggerService }
 ]
}
export class AppDetailComponent {
 constructor( @Host() private logger: LoggerService) {
 }
 show(): void {
  this.logger.Debug();
 }
}

It will prompt that the instance of LoggerService cannot be found, the role of @Host() is to limit the injector to stop searching at the current component and not continue to search upwards, so the error of not finding Providers will occur.

The result of adding providers is what we want.

 

Perfectly solves the problem of using separate service instances for multiple components.

 Summary:

1.If you want to use the component with the service separately, you first need to register the service separately in the providers. It is easy to understand

2.To better detect possible problems, add the @Host() decorator to the component service, which can throw error information as early as possible

3.Use ng2debug tool

4.It is necessary to clarify the relationships between each component, because different component relationships can lead to different instances of services

5.The service should be modular, not application level.

Thank you for reading, I hope it can help everyone, thank you for your support to this site!

You May Also Like