Angular2 小貼士-多級注入器


angular2 的依賴注入包含了太多的內容,其中的一個重點就是注入器,而注入器又非常難理解,今天我們不深入介紹注入器的內容,可以參考官方文檔,我們今天來說注入器的層級。

也就是組件獲取服務的容器會選擇具體哪一個。

先簡單介紹一個背景:有3個組件AppComponent 根組件、DetailList組件 ( 日志列表組件)、Detail組件( 日志組件)。

這三個組件會形成一個組件樹,對應的我們也可以認為每個組件都會有一個獨立的注入器(有時候不會出現,但是可以這么認為)。

加入一個日志服務LoggerService,如果按照我們普通的入門方式,在根模塊providers 中提供LoggerService。那么在整個應用程序中,LoggerService只有一個實例,什么意思呢?就是說無論在哪個組件,獲取到的都是首次創建的LoggerService,所有組件共用一個服務實例,這有時候會是一個有用的特性,比如我們使用的全局配置。

 

全局唯一不是我們這次要驗證的重點,因為這個太普通,我們這次要說明的是我們如何在每個組件中都獲取單獨的LoggerService實例,即每個組件的實例都不同。這個就需要對ng2的依賴注入有所了解才可以。

我們逐步來說明如何實現?

為了便於看到這篇短文的同學有所了解,我加入一些基礎代碼。

1.app.module.ts 應用程序根模塊。注意此處我們沒有在Providers中注冊loggerService。當然注冊了通過后面的方法也可以達到我們的目的。

 1 import { NgModule, Optional, SkipSelf, ReflectiveInjector} from '@angular/core';
 2 import { BrowserModule } from '@angular/platform-browser';
 3 
 4 /* App Root */
 5 import { AppComponent } from './app.component';
 6 import { routing } from './app.routing';
 7 import { Title } from '@angular/platform-browser';
 8 import {MessagesModule, GrowlModule, ButtonModule}from 'primeng/primeng';
 9 import {AppDetailComponent}from './app-detail.component';
10 import {AppDetailListComponent}from './app-detailList.component';
11 import {LoggerService}from './logger.service';
12 let allTitle:string="郭志奇";
13 
14 @NgModule({
15   imports: [
16     BrowserModule,
17     MessagesModule,
18     GrowlModule, ButtonModule
19   ],
20   declarations: [AppComponent, AppDetailComponent, AppDetailListComponent],//聲明當前模塊需要的指定 組件信息
21   exports: [],
22   providers: [Title],
23   bootstrap: [AppComponent]
24 })
25 export class AppModule {
26   constructor( @Optional() @SkipSelf() parentModule: AppModule) {
27     console.log(parentModule);
28     if (parentModule) {
29       throw new Error(
30         'AppModule is already loaded. Import it in the AppModule only');
31     }
32   }
33 }

2.app.component.ts  應用程序根組件

 1 import { Component, ViewEncapsulation, Host, ViewContainerRef, ReflectiveInjector } from '@angular/core';
 2 import { Title } from '@angular/platform-browser';
 3 import { Message } from 'primeng/primeng';
 4 import {LoggerService}from './logger.service';
 5 @Component({
 6     selector: 'my-app',
 7     moduleId: module.id,
 8     templateUrl: './app.component.html',
 9     providers: [
10         { provide: LoggerService, useClass: LoggerService }
11     ]
12 })
13 export class AppComponent {
14     subtitle = '(Final)';
15     private msgs: Message[];
16     constructor(private title: Title, @Host() private logger: LoggerService) {
17         this.title.setTitle("AppComponent");
18     }
19 
20     show(): void {
21         this.logger.Debug();
22     }
23 }

請注意,我們在跟組件中providers中注冊了LoggerService。

3.app.detailList.ts  日志列表中providers中也注冊了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  日志組件providers沒有注冊LoggerService。

 1 import {Component, Host}from '@angular/core';
 2 import {LoggerService}from './logger.service';
 3 @Component({
 4     selector: 'detail',
 5     moduleId: module.id,
 6     templateUrl: './app-detail.component.html',
 7     providers: [
 8       //  { provide: LoggerService, useClass: LoggerService }
 9     ]
10 })
11 
12 export class AppDetailComponent {
13     constructor(   private logger: LoggerService) {
14 
15     }
16     show(): void {
17         this.logger.Debug();
18     }
19 
20 }

 

現在我們通過chrome來看一下 LoggerService的層級關系。

 

通過查看依賴關系圖,我們可以看到AppComponent組件使用了單獨的LoggerService,DetailList組件也使用單獨的LoggerService 實例,而Detail組件使用的是父組件DetailList的LoggerService實例。

目前來看沒有達到我們的要求,我們的要求是每個組件都有單獨的LoggerService實例,那么我們假設Detail組件的providers是我們忘記輸入的,很難測試出原因所在。那么我們加入一個@Host()來限制注入器的查找范圍。

對於注入器的向上查找方式,請參考官方文檔。

為了便於調試,我們加入@Host().

@Host 裝飾器將把往上搜索的行為截止在 宿主組件

detail.ts 提示detail組件加入@Host()裝飾器

 1 import {Component, Host}from '@angular/core';
 2 import {LoggerService}from './logger.service';
 3 @Component({
 4     selector: 'detail',
 5     moduleId: module.id,
 6     templateUrl: './app-detail.component.html',
 7     providers: [
 8       //  { provide: LoggerService, useClass: LoggerService }
 9     ]
10 })
11 
12 export class AppDetailComponent {
13     constructor( @Host() private logger: LoggerService) {
14 
15     }
16     show(): void {
17         this.logger.Debug();
18     }
19 
20 }

會提示找不到LoggerService的實例,@Host()的作用就是限制注入器查找到當前組件就停止,不會繼續往上查找。所以會出現找不到Providers的錯誤。

加上providers 的結果就是我們想要的了。

 

完美的解決了多組件使用單獨服務實例的問題。

 

總結:

1.如果要使組件單獨使用服務,那么首先要在providers 中單獨注冊該服務。很容易理解

2.為了更好的檢測可能出現的問題,在組件服務上加入@Host()裝飾器,可以盡量早的拋出錯誤信息

3.使用ng2的debug工具

4.要明確各組件之間的關系,因為不同的組件關系會導致服務的實例的不同

5.服務盡量是模塊級,不是應用級。

 

angular2,一個值得學習的東西。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM