• 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,一个值得学习的东西。

  • 相关阅读:
    英语语法入门十五(名词所有格)
    在线调试Arduino
    CANopen和Canfestival
    嵌入式系统中的printf
    云原生爱好者周刊:Lens 5.0 发布,更炫、更快、更强!
    基于 KubeSphere 的 Nebula Graph 多云架构管理实践
    KubeSphere Meetup 北京站火热报名中 | 搭载 CIC 2021 云计算峰会
    KubeSphere Helm 应用仓库源码分析
    开启 Calico eBPF 数据平面实践
    KubeSphere 在直播应用中的实践
  • 原文地址:https://www.cnblogs.com/zhangxiaolei521/p/6011311.html
Copyright © 2020-2023  润新知