开始使用-安装 顶

Dependency Injection指南中你学会了基础的Angular依赖注入. Angular有一个层级依赖注入 系统. 实际上是一个与组件树相平行的注入器树. 你可以在组件树的任意层级重新配置注入器. 此指南探索此系统并使用它带来的好处. 尝试live example(view source).

注入器树

依赖注入指南中, 学会了如何配置依赖注入器和在需要时如何重新获取依赖对象. 事实上,这里没有像注入器这样的东西. 应用程序可能有多个注入器.Angular应用程序是一个组件树.每一个组件实例有它自己的注入器.组件树与注入器树相平行.

组件的注入器可能是组件树中更高层级的祖先注入器的一个代理 . 这是改善效率的具体实现.你不必关心注入器的不同并且你的脑模型应该是每一个组件有它自己的注入器.

思考Tour of Heroes应用程序中指南的变化. 顶层是有若干子组件的AppComponent. 其中一个是HeroesListComponent. HeroesListComponent保留和管理HeroTaxReturnComponent的多个实例. 下面的图表表示当同时打开HeroTaxReturnComponent的三个实例时指南中组件树的第三层的状态 .

注入器冒泡

当一个组件请求依赖时, Angular尝试使用组件自己的注入器中的注册过的提供者满足依赖. 如果组件的注入器没有提供者, 它将向上传递请求到父组件的注入器.如果此组件无法满足请求, 它继续沿着此组件自己的父注入器传递. 此请求保持向上冒泡直到Angular发现一个注入器能处理此请求或在祖先注入器之外运行. 如果它在祖先注入器之外运行, Angular将抛一个错误.

你可以抑制冒泡. 一个媒介组件可以声明它是“host” 组件.此组件将比注入器搜寻提供者更高效.这是以后的主题.

在不同的层级再供给

您可以在注入器树的多个级别重新注册特定依赖性令牌的提供者。 您不必重新注册供应商。 除非你有充分的理由,否则你不应该这样做。但是你可以。 随着解决方案逻辑向上发展,第一个提供商遇到了胜利。 因此,中间注射器中的提供者从树中较低的东西拦截对服务的请求。 它有效地“重新配置”和“隐藏”树中较高级别的提供者。 如果您只指定顶级供应商(通常是根AppComponent),则注入器树看起来是平坦的。 所有请求都会冒泡到您使用bootstrap方法配置的根注入器。

组件注入器

能够在不同级别配置一个或多个提供商开辟了有趣和有用的可能性。

场景:服务

隔离建筑学的思路引导你限制访问应用程序的服务所属的域名.

指南简单引入了显示反叛角色列表的VillainsListComponent. 它从VillainsService中获得反派角色列表.

虽然你可能在根组件AppComponent(就是HeroesService的地方)中提供 VillainsService , 使得VillainsService在应用程序的任何地方都可以获得, 包括Hero工作流.

如果在今后VillainsService发生更改, 你可能需要在hero组件的某个地方中断某些操作. 这不仅发生在想象中以致提供服务的AppComponent将产生风险.

代替方案, 在VillainsListComponent组件元数据providers里提供VillainsService, 例如:

lib/src/villains_list_component.dart (metadata)

@Component(
  selector: 'villains-list',
  template: '''
      <div>
        <h3>Villains</h3>
        <ul>
          <li *ngFor="let villain of villains | async">{{villain.name}}</li>
        </ul>
      </div>
    ''',
  directives: const [CORE_DIRECTIVES],
  providers: const [VillainsService],
  pipes: const [COMMON_PIPES],
)

通过只在VillainsListComponent元数据中提供VillainsService, 服务将仅仅在VillainsListComponent和其子组件树中可用. 它是一个单例,但它是仅在villain域中存在的一个单例. 现在你知道在hero组件中不能使用它.你减少了错误的风险.

场景:多个编辑会话

许多应用程序允许用户同时打开多个任务工作.例如, 在一个预税程序中, 填表人可能操作多个税单,始终由一个值转换到另一个值.

指南在Tour of Heroes主题中以一个简单的例子示范了这个案例. 想象在HeroListComponent之外显示一个超级英雄列表.

打开一个英雄的税单, 填表人单击一个英雄的名字, 打开一个组件编辑收入. 每一个选择的英雄税单都在他自己的组件中打开并且多个返回值能同时被展现    `.

每一个税单都有如下特征:

  • 属于它自己的税单编辑会话.
  • 能改变一个税单不影响另一个组件的返回值.
  • 拥有保存和取消更改税单的能力.

一种可能的假设HeroTaxReturnComponent有管理和恢复更改的逻辑. 那对于一个简单的英雄税单来说是非常棒的.在真实世界中, 使用了详尽的税单数据模型, 编辑将会很棘手. 你可能为管理人员委派一个助手服务, 如此例子所示.

这是HeroTaxReturnService. 它缓存了一个单独的HeroTaxReturn,跟踪返回值的变化, 且能保存和恢复其值. 它也为应用程序范围委派了一个单实例的HeroService, 通过注入获得.

lib/src/hero_tax_return_service.dart

import 'dart:async';
import 'package:angular/angular.dart';
import 'hero.dart';
import 'heroes_service.dart';
@Injectable()
class HeroTaxReturnService {
  final HeroesService _heroService;
  HeroTaxReturn _currentTR, _originalTR;
  HeroTaxReturnService(this._heroService);
  void set taxReturn(HeroTaxReturn htr) {
    _originalTR = htr;
    _currentTR = new HeroTaxReturn.copy(htr);
  }
  HeroTaxReturn get taxReturn => _currentTR;
  void restoreTaxReturn() {
    taxReturn = _originalTR;
  }
  Future<Null> saveTaxReturn() async {
    taxReturn = _currentTR;
    await _heroService.saveTaxReturn(_currentTR);
  }
}

这是使用它的HeroTaxReturnComponent。

lib/src/hero_tax_return_component.dart

import 'dart:async';
import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
import 'hero.dart';
import 'hero_tax_return_service.dart';
@Component(
    selector: 'hero-tax-return',
    template: '''
      <div class="tax-return">
        <div class="msg" [class.canceled]="message==='Canceled'">{{message}}</div>
        <fieldset>
          <span id="name">{{taxReturn.name}}</span>
          <label id="tid">TID: {{taxReturn.taxId}}</label>
        </fieldset>
        <fieldset>
          <label>
            Income: <input type="number" [(ngModel)]="taxReturn.income" class="num">
          </label>
        </fieldset>
        <fieldset>
          <label>Tax: {{taxReturn.tax}}</label>
        </fieldset>
        <fieldset>
          <button (click)="onSaved()">Save</button>
          <button (click)="onCanceled()">Cancel</button>
          <button (click)="onClose()">Close</button>
        </fieldset>
      </div>
    ''',
    styleUrls: const ['hero_tax_return_component.css'],
    directives: const [CORE_DIRECTIVES, formDirectives],
    providers: const [HeroTaxReturnService])
class HeroTaxReturnComponent {
  final HeroTaxReturnService _heroTaxReturnService;
  String message = '';
  HeroTaxReturnComponent(this._heroTaxReturnService);
  final _close = new StreamController<Null>();
  @Output()
  Stream<Null> get close => _close.stream;
  HeroTaxReturn get taxReturn => _heroTaxReturnService.taxReturn;
  @Input()
  void set taxReturn(HeroTaxReturn htr) {
    _heroTaxReturnService.taxReturn = htr;
  }
  Future<Null> onCanceled() async {
    _heroTaxReturnService.restoreTaxReturn();
    await flashMessage('Canceled');
  }
  void onClose() => _close.add(null);
  Future<Null> onSaved() async {
    await _heroTaxReturnService.saveTaxReturn();
    await flashMessage('Saved');
  }
  Future<Null> flashMessage(String msg) async {
    message = msg;
    await new Future.delayed(const Duration(milliseconds: 500));
    message = '';
  }
}

凭借实现了Getter和Setter方法的输入属性达成tax-return-to-edit . setter使用收入返回值初始化 HeroTaxReturnService的实例. getter始终返回服务中hero的当前状态.组件也向服务发出请求保存和恢复此税单.

这里有一个问题:如果此服务是应用程序范围的单实例.所有组件都需要共享同一个服务实例.每个组件都可能覆盖另一个hero的税单.多么混乱!

观察靠近HeroTaxReturnComponent的元数据.注意 providers 属性.

providers: const [HeroTaxReturnService])

HeroTaxReturnComponent有它自己的供给器HeroTaxReturnService. 回想每一个组件实例有它自己的注入器.在组件级别提供服务以确保每一个组件获取到它自己的实例, 服务的私有实例.没有税单被覆盖. 不混乱.

方案需要依赖Angular 的其它特性和技术,你可以在文档的其它地方学到. 你可以在live example (view source)预览和下载.

场景景:专业提供商

另一个说法是再供给替代 服务的更多专有实现,在组件树的更深处.

再次思考依赖注入 指南中的例子Car. 建议为CarService, EngineService 和 TiresService用一般的供给器配置根注入器(标记为 A).

创建一个Car组件 (A) 用于显示来自这三个一般服务的汽车的结构汽车的结构.

然后创建一个子组件(B), 定义自己专有的 供给器 CarService 和 EngineService 拥有特殊能力适合在组件(B)中无论发生什么.

组件 (B)是另一个组件 (C)的父组件, 为CarService定义更多特殊的供给器.

此种场景之后,每一个组件建立自己的注入器定义0, 1,或更多供给器 .

当你转变最深层组件(C) Car的实例时, 它的注入器生产一个Car实例通过注入器转变(C) Engine 通过注入器 (B)转变 和 Tires通过根注入器(A)转变.

以下是这款汽车方案的代码:

lib/src/car_components.dart

import 'package:angular/angular.dart';
import 'car_services.dart';
@Component(
    selector: 'c-car',
    template: '<div>C: {{description}}</div>',
    providers: const [const Provider(CarService, useClass: CarService3)])
class CCarComponent {
  String description;
  CCarComponent(CarService carService) {
    this.description =
        '${carService.getCar().description} (${carService.name})';
  }
}
@Component(
    selector: 'b-car',
    template: '''
      <div>B: {{description}}</div>
      <c-car></c-car>
    ''',
    directives: const [
      CCarComponent
    ],
    providers: const [
      const Provider(CarService, useClass: CarService2),
      const Provider(EngineService, useClass: EngineService2)
    ])
class BCarComponent {
  String description;
  BCarComponent(CarService carService) {
    this.description =
        '${carService.getCar().description} (${carService.name})';
  }
}
@Component(
    selector: 'a-car',
    template: '''
      <div>A: {{description}}</div>
      <b-car></b-car>
    ''',
    directives: const [BCarComponent])
class ACarComponent {
  String description;
  ACarComponent(CarService carService) {
    this.description =
        '${carService.getCar().description} (${carService.name})';
  }
}
@Component(
    selector: 'my-cars',
    template: '''
      <h3>Cars</h3>
      <a-car></a-car>
    ''',
    directives: const [ACarComponent])
class CarsComponent {}
const carComponents = const [
  CarsComponent,
  ACarComponent,
  BCarComponent,
  CCarComponent
];
// generic car-related services
const carServices = const [CarService, EngineService, TiresService];

lib/src/car_services.dart

import 'package:angular/angular.dart';
/// Model
class Car {
  String name = 'Avocado Motors';
  Engine engine;
  Tires tires;
  Car(this.engine, this.tires);
  String get description => '$name car with '
      '${engine.cylinders} cylinders and '
      '${tires.make} tires.';
}
class Engine {
  int cylinders = 4;
}
class Tires {
  String make = 'Flintstone';
  String model = 'Square';
}
//// Engine services ///
@Injectable()
class EngineService {
  String id;
  EngineService() : id = 'E1';
  Engine getEngine() => new Engine();
}
@Injectable()
class EngineService2 extends EngineService {
  EngineService2() {
    id = 'E2';
  }
  @override
  Engine getEngine() => new Engine()..cylinders = 8;
}
//// Tire services ///
@Injectable()
class TiresService {
  final id = 'T1';
  Tires getTires() => new Tires();
}
/// Car Services ///
@Injectable()
class CarService {
  EngineService engineService;
  TiresService tiresService;
  String id;
  CarService(this.engineService, this.tiresService) : id = 'C1';
  Car getCar() => new Car(engineService.getEngine(), tiresService.getTires());
  String get name => '$id-${engineService.id}-${tiresService.id}';
}
@Injectable()
class CarService2 extends CarService {
  CarService2(EngineService engineService, TiresService tiresService)
      : super(engineService, tiresService) {
    id = 'C2';
  }
  @override
  Car getCar() => super.getCar()..name = 'BamBam Motors, BroVan 2000';
}
@Injectable()
class CarService3 extends CarService2 {
  CarService3(EngineService engineService, TiresService tiresService)
      : super(engineService, tiresService) {
    id = 'C3';
  }
  @override
  Car getCar() =>
      super.getCar()..name = 'Chizzamm Motors, Calico UltraMax Supreme';
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏HaHack

comment.js:一个纯JS实现的静态站点评论系统

2414
来自专栏前端小吉米

域名发散--域的故事(三)

1563
来自专栏北京马哥教育

性能调优攻略

关于性能优化这是一个比较大的话题,在《由12306.cn谈谈网站性能技术》中我从业务和设计上说过一些可用的技术以及那些技术的优缺点,今天,想从一些技术细节上谈...

3854
来自专栏Java后端技术栈

Redis有哪些开发设计规范值得我们注意的!

redis不是垃圾桶也不是 SUPER MAN,能力和资源都有限,不合理的使用会降低它的健康度,严重时甚至会引起redis抖动、阻塞等进而导致服务不可用,每一个...

1761
来自专栏王清培的专栏

.NET应用架构设计—服务端开发多线程使用小结(多线程使用常识)

有一段时间没有更新博客了,最近半年都在着写书《.NET框架设计—大型企业级框架设计艺术》,很高兴这本书将于今年的10月份由图灵出版社出版,有关本书的具体介绍等书...

1965
来自专栏web编程技术分享

【手把手】JavaWeb 入门级项目实战 -- 文章发布系统 (第十二节)1.评论功能实现2.评论加载

80312
来自专栏C#

NodeJS异步I/O解析

    在现在的项目开发中,任何一个大型项目绝对不是简简单单的采用一个种语言和一种框架,因为每种语言和框架各有优势,与其死守一个,不与取各家之所长,依次得到一个...

2829
来自专栏京东技术

多级缓存设计详解 | 给数据库减负,刻不容缓!

物流研发部架构师,GIS技术部负责人,2012年加入京东,多年一线团队大促备战经验,负责物流研发一些部门的架构工作,专注于低延迟系统设计与海量数据处理。曾负责青...

3645
来自专栏崔庆才的专栏

为什么分布式一定要有redis?

2803
来自专栏Golang语言社区

使用Redis之前5个必须了解的事情

使用Redis开发应用程序是一个很愉快的过程,但是就像其他技术一样,基于Redis的应用程序设计你同样需要牢记几点。在之前,你可能已经对关系型数据库开发的那一整...

37010

扫码关注云+社区

领取腾讯云代金券