angular2 的依赖注入包含了太多的内容,其中的一个重点就是注入器,而注入器又很是难理解,今天咱们不深刻介绍注入器的内容,能够参考官方文档,咱们今天来讲注入器的层级。html
也就是组件获取服务的容器会选择具体哪个。chrome
先简单介绍一个背景:有3个组件AppComponent 根组件、DetailList组件 ( 日志列表组件)、Detail组件( 日志组件)。bootstrap
这三个组件会造成一个组件树,对应的咱们也能够认为每一个组件都会有一个独立的注入器(有时候不会出现,可是能够这么认为)。angular2
加入一个日志服务LoggerService,若是按照咱们普通的入门方式,在根模块providers 中提供LoggerService。那么在整个应用程序中,LoggerService只有一个实例,什么意思呢?就是说不管在哪一个组件,获取到的都是首次建立的LoggerService,全部组件共用一个服务实例,这有时候会是一个有用的特性,好比咱们使用的全局配置。app
全局惟一不是咱们此次要验证的重点,由于这个太普通,咱们此次要说明的是咱们如何在每一个组件中都获取单独的LoggerService实例,即每一个组件的实例都不一样。这个就须要对ng2的依赖注入有所了解才能够。ide
咱们逐步来讲明如何实现?工具
为了便于看到这篇短文的同窗有所了解,我加入一些基础代码。学习
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 应用程序根组件this
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,一个值得学习的东西。