angular2 的依赖注入包含了太多的内容,其中的一个重点就是注入器,而注入器又非常难理解,今天我们不深入介绍注入器的内容,可以参考官方文档,我们今天来说注入器的层级。
也就是组件获取服务的容器会选择具体哪一个。
先简单介绍一个背景:有3个组件AppComponent 根组件、DetailList组件 ( 日志列表组件)、Detail组件( 日志组件)。
这三个组件会形成一个组件树,对应的我们也可以认为每个组件都会有一个独立的注入器(有时候不会出现,但是可以这么认为)。
加入一个日志服务LoggerService,如果按照我们普通的入门方式,在根模块providers 中提供LoggerService。那么在整个应用程序中,LoggerService只有一个实例,什么意思呢?就是说无论在哪个组件,获取到的都是首次创建的LoggerService,所有组件共用一个服务实例,这有时候会是一个有用的特性,比如我们使用的全局配置。
全局唯一不是我们这次要验证的重点,因为这个太普通,我们这次要说明的是我们如何在每个组件中都获取单独的LoggerService实例,即每个组件的实例都不同。这个就需要对ng2的依赖注入有所了解才可以。
我们逐步来说明如何实现?
为了便于看到这篇短文的同学有所了解,我加入一些基础代码。
1.app.module.ts 应用程序根模块。注意此处我们没有在Providers中注册loggerService。当然注册了通过后面的方法也可以达到我们的目的。
import { NgModule, Optional, SkipSelf, ReflectiveInjector} from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; /* App Root */ 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="郭志奇"; @NgModule({ imports: [ BrowserModule, MessagesModule, GrowlModule, ButtonModule ], declarations: [AppComponent, AppDetailComponent, AppDetailListComponent],//声明当前模块需要的指定 组件信息 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 应用程序根组件
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(); } }
请注意,我们在跟组件中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。
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(); } }
现在我们通过chrome来看一下 LoggerService的层级关系。
通过查看依赖关系图,我们可以看到AppComponent组件使用了单独的LoggerService,DetailList组件也使用单独的LoggerService 实例,而Detail组件使用的是父组件DetailList的LoggerService实例。
目前来看没有达到我们的要求,我们的要求是每个组件都有单独的LoggerService实例,那么我们假设Detail组件的providers是我们忘记输入的,很难测试出原因所在。那么我们加入一个@Host()来限制注入器的查找范围。
对于注入器的向上查找方式,请参考官方文档。
为了便于调试,我们加入@Host().
@Host 装饰器将把往上搜索的行为截止在 宿主组件
detail.ts 提示detail组件加入@Host()装饰器
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(); } }
会提示找不到LoggerService的实例,@Host()的作用就是限制注入器查找到当前组件就停止,不会继续往上查找。所以会出现找不到Providers的错误。
加上providers 的结果就是我们想要的了。
完美的解决了多组件使用单独服务实例的问题。
总结:
1.如果要使组件单独使用服务,那么首先要在providers 中单独注册该服务。很容易理解
2.为了更好的检测可能出现的问题,在组件服务上加入@Host()装饰器,可以尽量早的抛出错误信息
3.使用ng2的debug工具
4.要明确各组件之间的关系,因为不同的组件关系会导致服务的实例的不同
5.服务尽量是模块级,不是应用级。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
本文向大家介绍java @interface 注解详解及实例,包括了java @interface 注解详解及实例的使用技巧和注意事项,需要的朋友参考一下 java @interface 注解详解及实例 1 简介 在Java中,定义注解其实和定义接口差多不,只需要在interface前添加一个@符号就可以,即 @interface Zhujie{ },这就表明我们定义了一个名为 @Zhujie 的
本文向大家介绍Java 线程优先级详解及实例,包括了Java 线程优先级详解及实例的使用技巧和注意事项,需要的朋友参考一下 Java 线程优先级详解及实例 操作系统基本采用时分的调度运行线程,操作系统会分出一个个时间片,线程会被分配到若干个时间片,当线程的时间片用完了就会发生线程调度,并且等待着下次调度,线程被分配到的时间片多少也就决定了线程使用处理器资源的多少,而线程优先级就是决定线程能够分配多
本文向大家介绍java 同步器SynchronousQueue详解及实例,包括了java 同步器SynchronousQueue详解及实例的使用技巧和注意事项,需要的朋友参考一下 同步器简介 学习以来对线程的操作有很大的改观,从c/c++的mutex到java的各种锁(当然不是嫌麻烦,java读写锁的实现还是带来不少好处的,但是sokcet的设计我就不敢恭维了,tcp和udp是两个类
本文向大家介绍java 多态性详解及简单实例,包括了java 多态性详解及简单实例的使用技巧和注意事项,需要的朋友参考一下 Java中多态性的实现 什么是多态 面向对象的三大特性:封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。 多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。
本文向大家介绍Spring @Conditional注解讲解及示例详解,包括了Spring @Conditional注解讲解及示例详解的使用技巧和注意事项,需要的朋友参考一下 前言: @Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。 @Conditional的定义: 从代码中可以看到,需要传入一个Class数组,并且需要继承Con
本文向大家介绍Java 多线程优先级实例详解,包括了Java 多线程优先级实例详解的使用技巧和注意事项,需要的朋友参考一下 Java 多线程优先级实例详解 线程的优先级将该线程的重要性传递给调度器。尽管CPU处理现有线程集的顺序是不确定的,但是调度器将倾向于让优先权最高的线程先执行。 你可以用getPriority()来读取现有线程的优先级,并且在任何时刻都可以通过setPriority()来修改