diff --git a/public/docs/ts/latest/guide/dependency-injection.jade b/public/docs/ts/latest/guide/dependency-injection.jade index 651ba8e406..765590ec65 100644 --- a/public/docs/ts/latest/guide/dependency-injection.jade +++ b/public/docs/ts/latest/guide/dependency-injection.jade @@ -8,26 +8,35 @@ block includes we really can't build an Angular application without it. It's used so widely that almost everyone just calls it _DI_. - **依赖注入**是一个很重要的程序设计模式。 - Angular有自己的依赖注入框架,离开了它,我们几乎没法构建Angular应用。 + **依赖注入**是重要的程序设计模式。 + Angular 有自己的依赖注入框架,离开了它,几乎没法构建Angular应用。 它使用得非常广泛,以至于几乎每个人都会把它简称为_DI_。 In this chapter we'll learn what DI is and why we want it. Then we'll learn [how to use it](#angular-di) in an Angular app. - 在本章中,我们将学习DI是什么,以及我们为什么需要它。 - 然后,我们将学习在Angular应用中该[如何使用它](#angular-di)。 + 本章将学习什么是 DI,以及为什么需要它。 + 然后,将学习在 Angular 应用中该[如何使用它](#angular-di)。 - [Why dependency injection?](#why-dependency-injection) - - [为什么依赖注入?](#why-dependency-injection) + + [为什么需要依赖注入?](#why-dependency-injection) + - [Angular dependency injection](#angular-dependency-injection) - - [Angular依赖注入](#angular-dependency-injection) + + [Angular 依赖注入](#angular-dependency-injection) + - [Injector providers](#injector-providers) - - [注入器提供商](#injector-providers) + + [注入器提供商](#injector-providers) + - [Dependency injection tokens](#dependency-injection-tokens) - - [依赖注入令牌](#dependency-injection-tokens) + + [依赖注入令牌](#dependency-injection-tokens) + - [Summary](#summary) - - [总结](#summary) + + [总结](#summary) Run the . @@ -48,19 +57,19 @@ block includes Our `Car` creates everything it needs inside its constructor. What's the problem? - 我们的`Car`类会在它的构造函数中亲自创建所需的每样东西。 + `Car`类会在它的构造函数中创建所需的每样东西。 问题何在? The problem is that our `Car` class is brittle, inflexible, and hard to test. - 问题在于,我们这个`Car`类过于脆弱、缺乏弹性并且难以测试。 + 问题在于,这个`Car`类过于脆弱、缺乏弹性并且难以测试。 Our `Car` needs an engine and tires. Instead of asking for them, the `Car` constructor instantiates its own copies from the very specific classes `Engine` and `Tires`. - 我们的`Car`类需要一个`Engine`和`Tires`,它没有去请求一个现成的实例, - 而是在构造函数中用具体的`Engine`和`Tires`类新创建了一份只供自己用的副本。 + `Car`类需要一个引擎 (engine) 和一些轮胎 (tire),它没有去请求现成的实例, + 而是在构造函数中用具体的`Engine`和`Tires`类实例化出自己的副本。 What if the `Engine` class evolves and its constructor requires a parameter? Our `Car` is broken and stays broken until we rewrite it along the lines of @@ -71,17 +80,17 @@ block includes when the definition of `Engine` changes, our `Car` class must change. That makes `Car` brittle. - 如果`Engine`类升级了,并且它的构造函数要求传入一个参数了,该怎么办? - 我们这个`Car`类就被破坏了,而且直到我们把创建引擎的代码重写为`#{prefix}engine = new Engine(theNewParameter)`之前,它都是坏的。 - 当我们首次写`Car`类时,我们不会在乎`Engine`构造函数的参数。现在我们也不想在乎。 - 但是当`Engine`类的定义发生变化时,我们就不得不在乎了,`Car`类也不得不跟着改变。 + 如果`Engine`类升级了,它的构造函数要求传入一个参数,这该怎么办? + 这个`Car`类就被破坏了,在把创建引擎的代码重写为`#{prefix}engine = new Engine(theNewParameter)`之前,它都是坏的。 + 当第一次写`Car`类时,我们不关心`Engine`构造函数的参数,现在也不想关心。 + 但是,当`Engine`类的定义发生变化时,就不得不在乎了,`Car`类也不得不跟着改变。 这就会让`Car`类过于脆弱。 What if we want to put a different brand of tires on our `Car`? Too bad. We're locked into whatever brand the `Tires` class creates. That makes our `Car` inflexible. - 如果我们想在我们的`Car`上用一个不同品牌的轮胎会怎样?太糟了。 - 我们被锁死在`Tires`类创建时使用的那个品牌上。这让我们的`Car`类缺乏弹性。 + 如果想在`Car`上使用不同品牌的轮胎会怎样?太糟了。 + 我们被锁定在`Tires`类创建时使用的那个品牌上。这让`Car`类缺乏弹性。 Right now each new car gets its own engine. It can't share an engine with other cars. While that makes sense for an automobile engine, @@ -90,8 +99,8 @@ block includes to share services that have been created previously for other consumers. 现在,每辆车都有它自己的引擎。它不能和其它车辆共享引擎。 - 虽然这对于汽车来说还算可以理解,但是我们设想一下那些应该被共享的依赖,比如用来联系厂家服务中心的车载无线。 - 我们的车缺乏必要的弹性,无法共享当初给其它消费者创建的车载无线。 + 虽然这对于汽车来说还算可以理解,但是设想一下那些应该被共享的依赖,比如用来联系厂家服务中心的车载无线电。 + 我们的车缺乏必要的弹性,无法共享当初给其它消费者创建的车载无线电。 When we write tests for our `Car` we're at the mercy of its hidden dependencies. Is it even possible to create a new `Engine` in a test environment? @@ -99,34 +108,34 @@ block includes Will a new instance of `Engine` make an asynchronous call to the server? We certainly don't want that going on during our tests. - 当我们给`Car`类写测试的时候,我们被它那些隐藏的依赖所摆布。 - 你以为能在测试环境中成功创建一个新的`Engine`吗? + 当给`Car`类写测试的时候,我们被它那些隐藏的依赖所摆布。 + 能在测试环境中成功创建新的`Engine`吗? `Engine`自己又依赖什么?那些依赖本身又依赖什么? - `Engine`的新实例会发起一个到服务器的异步调用吗? + `Engine`的新实例会发起到服务器的异步调用吗? 我们当然不想在测试期间这么一层层追下去。 What if our `Car` should flash a warning signal when tire pressure is low? How do we confirm that it actually does flash a warning if we can't swap in low-pressure tires during the test? - 如果我们的`Car`应该在轮胎气压低的时候闪动一个警示灯该怎么办? - 如果我们没法在测试期间换上一个低气压的轮胎,我们该如何确认它能正确的闪警示灯? + 如果`Car`应该在轮胎气压低的时候闪动警示灯该怎么办? + 如果没法在测试期间换上一个低气压的轮胎,那该如何确认它能正确的闪警示灯? We have no control over the car's hidden dependencies. When we can't control the dependencies, a class becomes difficult to test. 我们没法控制这辆车背后隐藏的依赖。 - 当我们不能控制依赖时,类就会变得难以测试。 + 当不能控制依赖时,类就会变得难以测试。 How can we make `Car` more robust, flexible, and testable? - 我们该如何让`Car`更强壮、有弹性以及可测试? + 该如何让`Car`更强壮、有弹性以及可测试? That's super easy. We change our `Car` constructor to a version with DI: - 答案超级简单。我们把`Car`的构造函数改造成使用DI的版本: + 答案超级简单。把`Car`的构造函数改造成使用DI的版本: +makeTabs( 'dependency-injection/ts/app/car/car.ts, dependency-injection/ts/app/car/car-no-di.ts', @@ -141,7 +150,7 @@ block includes It just consumes them. 发生了什么?我们把依赖的定义移到了构造函数中。 - 我们的`Car`类不再创建引擎或者轮胎。 + `Car`类不再创建引擎或者轮胎。 它仅仅“消费”它们。 block ctor-syntax @@ -150,12 +159,12 @@ block ctor-syntax We also leveraged TypeScript's constructor syntax for declaring parameters and properties simultaneously. - 我们再次借助TypeScript的构造器语法来同时定义参数和属性。 + 再次借助TypeScript的构造器语法来同时定义参数和属性。 :marked Now we create a car by passing the engine and tires to the constructor. - 现在,我们通过往构造函数中传入引擎和轮胎来创建一辆车。 + 现在,通过往构造函数中传入引擎和轮胎来创建一辆车。 +makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation', '')(format=".") @@ -166,8 +175,8 @@ block ctor-syntax We can pass in any kind of engine or tires we like, as long as they conform to the general API requirements of an engine or tires. - 酷!引擎和轮胎这两个“依赖”的定义从`Car`类本身解耦开了。 - 只要喜欢,我们就可以传入任何类型的引擎或轮胎,只要它们能满足引擎或轮胎的通用API需求。 + 酷!引擎和轮胎这两个依赖的定义与`Car`类本身解耦了。 + 只要喜欢,可以传入任何类型的引擎或轮胎,只要它们能满足引擎或轮胎的通用 API 需求。 If someone extends the `Engine` class, that is not `Car`'s problem. @@ -187,7 +196,7 @@ block ctor-syntax The critical point is this: `Car` itself did not have to change. We'll take care of the consumer's problem soon enough. - 这里的要点是:`Car`本身不必变化。我们很快就来解决消费者的问题。 + 这里的要点是:`Car`本身不必变化。下面就来解决消费者的问题。 :marked The `Car` class is much easier to test because we are in complete control @@ -195,8 +204,8 @@ block ctor-syntax We can pass mocks to the constructor that do exactly what we want them to do during each test: - `Car`类非常容易测试,因为我们现在对它的依赖有了完全的控制权。 - 在每个测试期间,我们可以往构造函数中传入mock对象,做到我们想让它们做的事: + `Car`类非常容易测试,因为现在我们对它的依赖有了完全的控制权。 + 在每个测试期间,我们可以往构造函数中传入mock对象,做想让它们做的事: - var stylePattern = { otl: /(new Car.*$)/gm }; +makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation-with-mocks', '', stylePattern)(format=".") @@ -204,12 +213,12 @@ block ctor-syntax :marked **We just learned what dependency injection is**. - **我们刚刚学到了什么是依赖注入** + **刚刚学习了什么是依赖注入** It's a coding pattern in which a class receives its dependencies from external sources rather than creating them itself. - 它是一种编程模式,该模式可以让一个类从外部源中获得它的依赖,而不必亲自创建它们。 + 它是一种编程模式,可以让类从外部源中获得它的依赖,而不必亲自创建它们。 Cool! But what about that poor consumer? Anyone who wants a `Car` must now @@ -220,11 +229,11 @@ block ctor-syntax 酷!但是,可怜的消费者怎么办? 那些希望得到一个`Car`的人们现在必须创建所有这三部分了:`Car`、`Engine`和`Tires`。 `Car`类把它的快乐建立在了消费者的痛苦之上。 - 我们需要某种机制把这三个部分装配好。 + 需要某种机制为我们把这三个部分装配好。 We could write a giant class to do that: - 我们可以写一个巨型类来做这件事(不好的模式): + 可以写一个巨型类来做这件事: +makeExample('dependency-injection/ts/app/car/car-factory.ts', null, 'app/car/car-factory.ts') @@ -236,24 +245,24 @@ block ctor-syntax 现在只需要三个创建方法,这还不算太坏。 但是当应用规模变大之后,维护它将变得惊险重重。 - 这个工厂类将变成一个由相互依赖的工厂方法构成的巨型蜘蛛网。 + 这个工厂类将变成由相互依赖的工厂方法构成的巨型蜘蛛网。 Wouldn't it be nice if we could simply list the things we want to build without having to define which dependency gets injected into what? - 如果我们能简单的列出我们想建造的东西,而不用定义该把哪些依赖注入到哪些对象中,那该多好! + 如果能简单的列出想建造的东西,而不用定义该把哪些依赖注入到哪些对象中,那该多好! This is where the dependency injection framework comes into play. Imagine the framework had something called an _injector_. We register some classes with this injector, and it figures out how to create them. - 到了让依赖注入框架一展身手的时候了! - 想象框架中有一个叫做_注入器Injector_的东西。 - 我们使用这个注入器注册一些类,它会指出该如何创建它们。 + 到了依赖注入框架一展身手的时候了! + 想象框架中有一个叫做_注入器 (injector)_的东西。 + 用这个注入器注册一些类,它会弄明白如何创建它们。 When we need a `Car`, we simply ask the injector to get it for us and we're good to go. - 当我们需要一个`Car`时,就简单的找注入器取车就可以了。 + 当需要一个`Car`时,就简单的找注入器取车就可以了。 +makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-call')(format=".") @@ -263,10 +272,10 @@ block ctor-syntax We don't have a gigantic factory class to maintain. Both `Car` and consumer simply ask for what they need and the injector delivers. - 多方皆赢。`Car`不需要知道如何创建`Engine`和`Tires`的任何事。 - 消费者不知道如何创建`Car`的任何事。 - 我们不需要一个巨大的工厂类来维护它们。 - `Car`和消费者只要简单的说出它们想要什么,注入器就会交给它们。 + 皆大欢喜。`Car`不需要知道如何创建`Engine`和`Tires`。 + 消费者不需要知道如何创建`Car`。 + 开发人员不需要维护巨大的工厂类。 + `Car`和消费者只要简单地请求想要什么,注入器就会交付它们。 This is what a **dependency injection framework** is all about. @@ -275,28 +284,28 @@ block ctor-syntax Now that we know what dependency injection is and appreciate its benefits, let's see how it is implemented in Angular. - 现在,我们知道了依赖注入是什么,以及它的优点是什么。我们再来看看它在Angular中是怎么实现的。 + 现在,我们知道了什么是依赖注入,以及它的优点。再来看看它在 Angular 中是怎么实现的。 .l-main-section#angular-di :marked ## Angular dependency injection - ## Angular依赖注入 + ## Angular 依赖注入 Angular ships with its own dependency injection framework. This framework can also be used as a standalone module by other applications and frameworks. - Angular自带了它自己的依赖注入框架。此框架也能被当做独立模块用于其它应用和框架中。 + Angular 附带了自己的依赖注入框架。此框架也能被当做独立模块用于其它应用和框架中。 That sounds nice. What does it do for us when building components in Angular? Let's see, one step at a time. - 听起来很好。当我们在Angular中构建组件的时候,它到底能为我们做什么? - 让我们一步一个脚印的看看。 + 听起来很好。当在 Angular 中构建组件的时候,它到底能为我们做了什么呢? + 来一点一点搞清楚吧。 We'll begin with a simplified version of the `HeroesComponent` that we built in the [The Tour of Heroes](../tutorial/). - 我们从当初在[英雄指南](../tutorial/)中构建过的`HeroesComponent`的一个简化版本开始。 + 从[英雄指南](../tutorial/)中构建的`HeroesComponent`的简化版本开始。 +makeTabs( `dependency-injection/ts/app/heroes/heroes.component.1.ts, @@ -315,8 +324,8 @@ block ctor-syntax Our stripped down version has only one child, `HeroListComponent`, which displays a list of heroes. - `HeroesComponent`是*英雄*特性区域的根组件。它管理本区的所有子组件。 - 我们简化后的版本只有一个子组件`HeroListComponent`,用来显示一个英雄列表。 + `HeroesComponent`是*英雄*特性区域的根组件。它管理区域内所有子组件。 + 简化后的版本只有一个子组件`HeroListComponent`,用来显示英雄列表。 :marked Right now `HeroListComponent` gets heroes from `HEROES`, an in-memory collection @@ -326,13 +335,14 @@ block ctor-syntax we'll have to change the implementation of `heroes` and fix every other use of the `HEROES` mock data. - 现在`HeroListComponent`从`HEROES`获得英雄数据,一个在另一个文件中定义的内存数据集。 + 现在`HeroListComponent`从`HEROES`获得英雄数据,是在另一个文件中定义的内存数据集。 它在开发的早期阶段可能还够用,但离完美就差得远了。 - 我们一旦开始测试此组件,或者想从远端服务器获得英雄数据,我们就不得不修改`heroes`的实现,并要修改每个用到了`HEROES`模拟数据的地方。 + 一旦开始测试此组件,或者想从远端服务器获得英雄数据,就不得不修改`heroes`的实现, + 还要修改每个用到了`HEROES`模拟数据的地方。 Let's make a service that hides how we get hero data. - 我们来制作一个服务,把获取英雄数据的代码封装起来。 + 可以用一个服务把获取英雄数据的代码封装起来。 .l-sub-section :marked @@ -342,12 +352,12 @@ block ctor-syntax write the service code in its own file. 因为服务是一个[分离关注点](https://en.wikipedia.org/wiki/Separation_of_concerns), - 我们建议你把服务代码放到它自己的文件里。 + 建议你把服务代码放到它自己的文件里。 +ifDocsFor('ts') :marked See [this note](#one-class-per-file) for details. - 到[这个笔记](#one-class-per-file)看更多信息。 + 更多信息,见[这个笔记](#one-class-per-file)。 +makeExample('dependency-injection/ts/app/heroes/hero.service.1.ts',null, 'app/heroes/hero.service.ts' ) @@ -355,7 +365,7 @@ block ctor-syntax Our `HeroService` exposes a `getHeroes` method that returns the same mock data as before, but none of its consumers need to know that. - 我们的`HeroService`暴露了`getHeroes`方法,用于返回跟以前一样的模拟数据,但它的消费者不需要知道这一点。 + `HeroService`暴露了`getHeroes`方法,返回跟以前一样的模拟数据,但它的消费者不需要知道这一点。 // #enddocregion di-4 // #docregion di-5 @@ -364,7 +374,7 @@ block ctor-syntax Notice the `@Injectable()` #{_decorator} above the service class. We'll discuss its purpose [shortly](#injectable). - 注意服务类上面这个`@Injectable()`装饰器。我们[很快](#injectable)会讨论它的用途。 + 注意服务类上面这个`@Injectable()`装饰器。[很快](#injectable)会讨论它的用途。 - var _perhaps = _docsFor == 'dart' ? '' : 'perhaps'; .l-sub-section @@ -376,16 +386,16 @@ block ctor-syntax This is important in general, but not to our current story. 我们甚至没有假装这是一个真实的服务。 - 如果我们真的从一个远端服务器获取数据,这个API必须是异步的,可能得返回 - [ES2015 承诺(Promise)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。 - 我们也需要被迫重新处理组件如何消费该服务的方式。通常这个很重要,但是我们目前的故事不需要。 + 如果真的从远端服务器获取数据,这个API必须是异步的,可能得返回 + [ES2015 承诺 (promise)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。 + 需要重新处理组件消费该服务的方式。通常这个很重要,但是目前的故事不需要。 :marked A service is nothing more than a class in Angular. It remains nothing more than a class until we register it with an Angular injector. - 在Angular 2中,服务只是一个类。 - 除非我们把它注册进一个Angular注入器,否则它没有任何特别之处。 + 服务只是 Angular 中的一个类。 + 有 Angular 注入器注册它之前,没有任何特别之处。 #bootstrap :marked @@ -395,8 +405,8 @@ block ctor-syntax We don't have to create an Angular injector. Angular creates an application-wide injector for us during the bootstrap process. - 我们并不需要自己创建一个Angular注入器。 - Angular在启动期间会自动为我们创建一个全应用级注入器。 + 不需要创建 Angular 注入器。 + Angular 在启动过程中自动为我们创建一个应用级注入器。 +makeExample('dependency-injection/ts/app/main.ts', 'bootstrap', 'app/main.ts (excerpt)')(format='.') @@ -405,21 +415,20 @@ block ctor-syntax that create the services our application requires. We'll explain what [providers](#providers) are later in this chapter. - 我们必须先注册**提供商Provider**来配置注入器,这些提供商为我们的应用程序创建所需服务。 - 我们将在本章的稍后部分解释什么是[提供商](#providers)。 - 在此之前,我们先来看一个在启动期间注册提供商的例子。 + 我们必须通过注册**提供商 (provider)**来配置注入器,这些提供商为应用创建所需服务。 + 在本章的稍后部分会解释什么是[提供商](#providers)。 We can either register a provider within an [NgModule](ngmodule.html) or in application components - 我们或者在[NgModule](ngmodule.html)中注册提供商,或者在应用组件中。 + 或者在[NgModule](ngmodule.html)中注册提供商,或者在应用组件中。 ### Registering providers in an NgModule - ### 在NgModule中注册提供商。 + ### 在 NgModule 中注册提供商 Here's our AppModule where we register a `Logger`, a `UserService`, and an `APP_CONFIG` provider. - 在AppModule中,我们注册了`Logger`、`UserService`和`APP_CONFIG`提供商。 + 下面的例子是在 AppModule 中注册`Logger`、`UserService`和`APP_CONFIG`提供商。 - var stylePattern = { otl: /(providers)/ }; @@ -441,21 +450,21 @@ block ctor-syntax :marked ### When to use the NgModule and when an application component? - ### 该用NgModule还是应用组件? + ### 该用 NgModule 还是应用组件? On the one hand, a provider in an NgModule is registered in the root injector. That means that every provider registered within an NgModule will be accessible in the entire application. - 一方面,NgModule中的提供商是被注册到根注入器的。这意味着任何注册到NgModule上的提供商都可以被整个应用访问到。 + 一方面,NgModule 中的提供商是被注册到根注入器。这意味着在 NgModule 中注册的提供商可以被整个应用访问。 On the other hand, a provider registered in an application component is available only on that component and all its children. - 另一方面,注册到应用组件上的只在该组件及其各级子组件中可用。 + 另一方面,在应用组件中注册的提供商只在该组件及其子组件中可用。 We want the `APP_CONFIG` service to be available all across the application, but a `HeroService` is only used within the *Heroes* feature area — and nowhere else. — - 我们希望`APP_CONFIG`服务能在整个应用中可用,而`HeroService`只需在*英雄*特性区可用,而其它地方都不用。 + 我们希望`APP_CONFIG`服务在整个应用中可用,而`HeroService`只需在*英雄*特性区可用,在其它地方都不可用。 .l-sub-section :marked @@ -472,8 +481,8 @@ block ctor-syntax constructor, [as we explained earlier](#ctor-injection). It's a small change: - `HeroListComponent`应该从注入进来的`HeroService`获取英雄数据。 - 遵照依赖注入模式的要求,组件必须在它的构造函数中请求这些服务,[就像我们以前解释过的那样](#ctor-injection)。 + `HeroListComponent`应该从注入的`HeroService`获取英雄数据。 + 遵照依赖注入模式的要求,组件必须在它的构造函数中请求这些服务,[就像以前解释过的那样](#ctor-injection)。 只是个小改动: +makeTabs( @@ -486,11 +495,11 @@ block ctor-syntax .l-sub-section :marked #### Focus on the constructor - #### 来看构造函数 + #### 来看看构造函数 Adding a parameter to the constructor isn't all that's happening here. - 往构造函数中添加一个参数并不是这里所做的一切。 + 往构造函数中添加参数并不是这里所发生的一切。 +makeExample('dependency-injection/ts/app/heroes/hero-list.component.2.ts', 'ctor')(format=".") @@ -501,15 +510,16 @@ block ctor-syntax Also recall that the parent component (`HeroesComponent`) has `providers` information for `HeroService`. - 注意构造函数参数有类型`HeroService`,并且`HeroListComponent`类有一个`@Component`装饰器 - (往上翻可以确认)。另外,记得父级组件(`HeroesComponent`)有`HeroService`的`providers`信息。 + 注意,构造函数参数的类型是`HeroService`,并且`HeroListComponent`类有一个`@Component`装饰器 + (往上翻可以确认)。另外,记得父级组件 (`HeroesComponent`) 有`HeroService`的`providers`信息。 The constructor parameter type, the `@Component` #{_decorator}, and the parent's `providers` information combine to tell the Angular injector to inject an instance of `HeroService` whenever it creates a new `HeroListComponent`. - 该构造函数类型、`@Component`装饰器、父级的`providers`信息这三个合起来,一起告诉Angular的注入器,在任何时候新建一个新的`HeroListComponent`的时候,注入一个`HeroService`的实例。 + 构造函数参数类型、`@Component`装饰器和父级的`providers`信息合起来告诉Angular的注入器, + 任何新建`HeroListComponent`的时候,注入一个`HeroService`的实例。 #di-metadata :marked @@ -521,7 +531,7 @@ block ctor-syntax use it to create a new `Car`. Here we also show how such an injector would be explicitly created: - 当我们在上面介绍注入器的时候,我们展示了如何使用它创建一个新`Car`。这里,我们也展示一下如何显性的创建这样的注入器: + 在前面介绍注入器时,展示了如何使用它创建一个新`Car`。这里,也展示一下如何显性的创建这样的注入器: +makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-create-and-call')(format=".") @@ -533,10 +543,10 @@ block ctor-syntax or after navigating to a component with the [router](./router.html). If we let Angular do its job, we'll enjoy the benefits of automated dependency injection. - 但无论在《英雄指南》还是其它范例中,我们都没有发现这样的代码。 - 在必要时,我们*可以*写[使用显式注入器的代码](#explicit-injector),但却很少这样做。 - 当Angular为我们创建组件时 —— 无论通过像``这样的HTML标签还是通过[路由](./router.html)导航到组件 —— 它都会自己管理好注入器的创建和调用。 - 只要让Angular做好它自己的工作,我们就能安心享受“自动依赖注入”带来的好处。 + 无论在《英雄指南》还是其它范例中,都没有出现这样的代码。 + 在必要时,*可以*写[使用显式注入器的代码](#explicit-injector),但却很少这样做。 + 当 Angular 创建组件时 —— 无论通过像``这样的 HTML 标签还是通过[路由](./router.html)导航到组件 —— 它都会自己管理好注入器的创建和调用。 + 只要让 Angular 做好它自己的工作,我们就能安心享受“自动依赖注入”带来的好处。 :marked ### Singleton services @@ -547,13 +557,13 @@ block ctor-syntax `HeroesComponent` and its `HeroListComponent` children. 在一个注入器的范围内,依赖都是单例的。 - 在我们这个例子中,一个单一的`HeroService`实例被`HeroesComponent`和它的子组件`HeroListComponent`共享。 + 在这个例子中,`HeroesComponent`和它的子组件`HeroListComponent`共享同一个`HeroService`实例。 However, Angular DI is an hierarchical injection system, which means that nested injectors can create their own service instances. Learn more about that in the [Hierarchical Injectors](./hierarchical-dependency-injection.html) chapter. - 然而,Angular DI是一个分层的依赖注入系统,这意味着被嵌套的注入器可以创建它们自己的服务实例。 + 然而,Angular DI 是一个分层的依赖注入系统,这意味着嵌套的注入器可以创建它们自己的服务实例。 要了解更多知识,参见[多级依赖注入器](./hierarchical-dependency-injection.html)一章。 :marked @@ -563,13 +573,13 @@ block ctor-syntax We emphasized earlier that designing a class for dependency injection makes the class easier to test. Listing dependencies as constructor parameters may be all we need to test application parts effectively. - 我们前面强调过,设计一个适合依赖注入的类,可以让这个类更容易测试。 - 要有效的测试应用中的一部分,在构造函数的参数中列出依赖就是我们需要做的一切。 + 前面强调过,设计一个适合依赖注入的类,可以让这个类更容易测试。 + 要有效的测试应用中的一部分,只需要在构造函数的参数中列出依赖。 For example, we can create a new `HeroListComponent` with a mock service that we can manipulate under test: - 比如,我们可以使用一个mock服务来创建新的`HeroListComponent`实例,以便我们可以在测试中操纵它: + 例如,新建的`HeroListComponent`实例使用一个模拟 (mock) 服务,以便可以在测试中操纵它: +makeExample('dependency-injection/ts/app/test.component.ts', 'spec')(format='.') @@ -581,22 +591,22 @@ block ctor-syntax :marked ### When the service needs a service - ### 服务需要别的服务 + ### 当服务需要别的服务时 Our `HeroService` is very simple. It doesn't have any dependencies of its own. - 我们的`HeroService`非常简单。它本身不需要任何依赖。 + 这个`HeroService`非常简单。它本身不需要任何依赖。 What if it had a dependency? What if it reported its activities through a logging service? We'd apply the same *constructor injection* pattern, adding a constructor that takes a `Logger` parameter. - 如果它有依赖呢?如果它需要通过一个日志服务来汇报自己的活动呢? + 如果它也有依赖,该怎么办呢?例如,它需要通过日志服务来汇报自己的活动。 我们同样用*构造函数注入*模式,来添加一个带有`Logger`参数的构造函数。 Here is the revision compared to the original. - 下面是在原始类的基础上所做的修改: + 下面是在原来的基础上所做的修改: +makeTabs( `dependency-injection/ts/app/heroes/hero.service.2.ts, @@ -609,20 +619,21 @@ block ctor-syntax The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `#{_priv}logger`. We call that property within our `getHeroes` method when anyone asks for heroes. - 现在,这个构造函数会要求一个`Logger`类的实例注入进来,并且把它存到一个名为`logger`的私有属性中。 - 当别人要求获得英雄数据时,我们会在`getHeroes`方法中使用这个属性。 + 现在,这个构造函数要求注入一个`Logger`类的实例,并把它存到名为`logger`的私有属性中。 + 当别人请求英雄数据时,在`getHeroes`方法中调用这个属性的方法。 //- FIXME refer to Dart API when that page becomes available. - var injUrl = '../api/core/index/Injectable-decorator.html'; h3#injectable Why @Injectable()? -h3#injectable 为何@Injectable()? +h3#injectable 为什么要用@Injectable()? :marked **@Injectable()** marks a class as available to an injector for instantiation. Generally speaking, an injector will report an error when trying to instantiate a class that is not marked as `@Injectable()`. - **@Injectable()**标志着一个类可以被一个注入器实例化。通常来讲,在试图实例化一个没有被标识为`@Injectable()`的类时候,注入器将会报告错误。 + **@Injectable()** 标识一个类可以被注入器实例化。 + 通常,在试图实例化没有被标识为`@Injectable()`的类时,注入器会报错。 block injectable-not-always-needed-in-ts .l-sub-section @@ -633,29 +644,33 @@ block injectable-not-always-needed-in-ts We need it because Angular requires constructor parameter metadata in order to inject a `Logger`. - 在这里,我们可以在我们第一版的`HeroService`里面省略`@Injectable()`,因为它没有注入的参数。但是现在我们必须要有它,因为我们的服务有了一个注入的依赖。我们需要它,因为Angular需要构造函数参数的元数据来注入一个`Logger`。 + 碰巧,第一版的`HeroService`省略了`@Injectable()`,那因为它没有注入的参数。 + 但是现在必须要有它,因为服务有了一个注入的依赖。 + 我们需要它,因为 Angular 需要构造函数参数的元数据来注入一个`Logger`。 .callout.is-helpful header Suggestion: add @Injectable() to every service class - header 建议:为每一个服务类都添加@Injectable() + header 建议:为每个服务类都添加 @Injectable() :marked We recommend adding `@Injectable()` to every service class, even those that don't have dependencies and, therefore, do not technically require it. Here's why: - 我们建议为每个服务类都添加`@Injectable()`,包括那些没有依赖因此严格来说并不需要它的。因为: + 建议为每个服务类都添加`@Injectable()`,包括那些没有依赖严格来说并不需要它的。因为: ul(style="font-size:inherit") - li Future proofing: No need to remember @Injectable() when we add a dependency later. - li 面向未来: 没有必要记得在后来添加了一个依赖的时候添加@Injectable()。 - li Consistency: All services follow the same rules, and we don't have to wonder why #{_a} #{_decorator} is missing. - li 一致性:所有的服务都遵循同样的规则,并且我们不需要考虑为什么少一个装饰器。 + li + p Future proofing: No need to remember @Injectable() when we add a dependency later. + p 面向未来: 没有必要记得在后来添加依赖的时候添加@Injectable()。 + li + p Consistency: All services follow the same rules, and we don't have to wonder why #{_a} #{_decorator} is missing. + p 一致性:所有的服务都遵循同样的规则,不需要考虑为什么某个地方少了一个。 :marked Injectors are also responsible for instantiating components like `HeroesComponent`. Why haven't we marked `HeroesComponent` as `@Injectable()`? - 注入器同时负责实例化像`HerosComponent`这样的组件。为什么我们不标记`HerosComponent`为`@Injectable()`呢? + 注入器同时负责实例化像`HerosComponent`这样的组件。为什么不标记`HerosComponent`为`@Injectable()`呢? We *can* add it if we really want to. It isn't necessary because the `HeroesComponent` is already marked with `@Component`, and this @@ -664,7 +679,8 @@ block injectable-not-always-needed-in-ts fact `Injectable` #{_decorator}s that identify a class as a target for instantiation by an injector. - 我们**可以**添加它。但是它不是必需的,因为`HerosComponent`已经有`@Component`装饰器了,这个装饰器类(和我们随后将会学到的`@Directive`和`@Pipe`一样)是InjectableMetadata的子类型。实际上,这个`InjectableMetadata`装饰器是把一个类标识为注入器实例化的目标。 + 我们**可以**添加它。但是没有必要,因为`HerosComponent`已经有`@Component`装饰器了, + `@Component`(和随后将会学到的`@Directive`和`@Pipe`一样)是 Injectable 的子类型。实际上,正是这些`Injectable`装饰器是把一个类标识为注入器实例化的目标。 +ifDocsFor('ts') .l-sub-section @@ -673,7 +689,8 @@ block injectable-not-always-needed-in-ts and use the constructor parameter type information to determine what things to inject. - 在运行时,注入器可以从编译后的JavaScript代码中读取类的元数据,并使用构造函数的参数类型信息来决定注入什么。 + 在运行时,注入器可以从编译后的 JavaScript 代码中读取类的元数据, + 并使用构造函数的参数类型信息来决定注入什么。 Not every JavaScript class has metadata. The TypeScript compiler discards metadata by default. @@ -682,18 +699,17 @@ block injectable-not-always-needed-in-ts the compiler adds the metadata to the generated JavaScript for _every class with at least one decorator_. - 不是每一个JavaScript类都有元数据。 - TypeScript编译器默认忽略元数据。 - 如果`emitDecoratorMetadata`编译器选项为`true`(在`tsconfig.json`中它应该为`true`), - 编译器就会在生成的JavaScript中,为_每一个至少拥有一个装饰器的类添加元数据_。 + 不是每一个 JavaScript 类都有元数据。 + TypeScript 编译器默认忽略元数据。 + 如果`emitDecoratorMetadata`编译器选项为`true`(在`tsconfig.json`中它应该为`true`), + 编译器就会在生成的 JavaScript 中,为_每一个至少拥有一个装饰器的类_添加元数据。 While any decorator will trigger this effect, mark the service class with the Injectable #{_decorator} to make the intent clear. - 注入器使用一个类的构造元数据来决定依赖类型,该构造元数据就是构造函数的参数类型所标识的。 - TypeScript为任何带有一个装饰器的类生成这样的元数据,任何装饰器都生成。 - 当然,使用一个合适的Injectable装饰器来标识一个类更加有意义。 + 当然,任何装饰器都会触发这个效果,用 Injectable 来标识服务 + 只是让这一意图更明显。 .callout.is-critical header Always include the parentheses @@ -706,7 +722,7 @@ block injectable-not-always-needed-in-ts Our application will fail mysteriously if we forget the parentheses. 总是使用`@Injectable()`的形式,不能只用`@Injectable`。 - 如果忘了括号,我们的应用就会神不知鬼不觉的失败! + 如果忘了括号,应用就会神不知鬼不觉的失败! .l-main-section#logger-service :marked @@ -719,15 +735,15 @@ block injectable-not-always-needed-in-ts 1. Create the logger service. - 1. 创建日志服务。 + 创建日志服务。 1. Register it with the application. - 1. 把它注册到应用中。 + 把它注册到应用中。 Our logger service is quite simple: - 我们的日志服务很简单: + 这个日志服务很简单: +makeExample('dependency-injection/ts/app/logger.service.ts', null, 'app/logger.service.ts') @@ -739,14 +755,15 @@ block real-logger so we put it in the project's `#{_appDir}` folder, and we register it in the `providers` #{_array} of the metadata for our application module, `AppModule`. - 我们很有可能在应用程序的每个角落都需要日志服务,所以把它放到项目的`#{_appDir}`目录,并在应用程序模块`AppModule`的元数据中的`providers`数组里注册它。 + 应用的每个角落都可能需要日志服务,所以把它放到项目的`#{_appDir}`目录, + 并在应用模块`AppModule`的元数据`providers`数组里注册它。 +makeExcerpt('app/providers.component.ts','providers-logger','app/app.module.ts (excerpt)') :marked If we forget to register the logger, Angular throws an exception when it first looks for the logger: - 如果我们忘了注册这个日志服务,Angular会在首次查找这个日志服务时,抛出一个异常。 + 如果忘了注册这个日志服务,Angular 会在首次查找这个日志服务时,抛出一个异常。 code-example(format="nocode"). EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger) @@ -758,11 +775,14 @@ code-example(format="nocode"). `HeroService`, which it needed to create and inject into a new `HeroListComponent`. - Angular这是在告诉我们,依赖注入器找不到日志服务的*提供商*。在创建`HeroListComponent`的新实例时需要创建和注入`HeroService`,然后`HeroService`需要创建和注入一个`Logger`实例,Angular需要这个提供商来创建一个`Logger`实例。 + Angular 告诉我们,依赖注入器找不到日志服务的*提供商*。 + 在创建`HeroListComponent`的新实例时需要创建并注入`HeroService`, + 而`HeroService`需要创建并注入一个`Logger`实例, + Angular 需要这个`Logger`实例的提供商来。 The chain of creations started with the `Logger` provider. *Providers* are the subject of our next section. - 这个“创建链”始于`Logger`的提供商。这个*提供商*就是我们下一节的主题。 + 这个“创建链”始于`Logger`的提供商。这个*提供商*就是下一节的主题。 .l-main-section#providers :marked @@ -773,16 +793,16 @@ code-example(format="nocode"). The injector relies on **providers** to create instances of the services that the injector injects into components and other services. - 提供商*提供*所需依赖值的一个具体的运行期版本。 - 注入器依靠**提供商们**来创建服务的实例,它会被注入器注入到组件或其它服务中。 + 提供商*提供*依赖值的一个具体的、运行时的版本。 + 注入器依靠**提供商**创建服务的实例,注入器再将服务的实例注入组件或其它服务。 We must register a service *provider* with the injector, or it won't know how to create the service. - 我们必须为注入器注册一个服务的*提供商*,否则它就不知道该如何创建此服务。 + 必须为注入器注册一个服务的*提供商*,否则它不知道该如何创建该服务。 Earlier we registered the `Logger` service in the `providers` #{_array} of the metadata for the `AppModule` like this: - 以前,我们通过`AppModule`元数据中的`providers`数组注册过`Logger`服务,就像这样: + 我们在前面通过`AppModule`元数据中的`providers`数组注册过`Logger`服务,就像这样: +makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger') @@ -798,7 +818,7 @@ code-example(format="nocode"). But it's not the only way. 有很多方式可以*提供*一些#{implementsCn} `Logger`类的东西。 - `Logger`类本身是一个显而易见而且自然而然的提供商 —— 它有正确的形态,并且它设计出来就是等着被创建的。 + `Logger`类本身是一个显而易见而且自然而然的提供商。 但它不是唯一的选项。 We can configure the injector with alternative providers that can deliver #{objectlike} a `Logger`. @@ -806,18 +826,18 @@ code-example(format="nocode"). We could give it a provider that calls a logger factory function. Any of these approaches might be a good choice under the right circumstances. - 我们可以使用其它备选提供商来配置这个注入器,只要它们能交付#{objectlikeCn}`Logger`就可以了。 - 我们可以提供一个替身类。#{loggerlikeCn} - 我们可以给它一个提供商,让它调用一个可以创建日志服务的工厂函数。 + 可以用其它备选提供商来配置注入器,只要它们能交付#{objectlikeCn}`Logger`就可以了。 + 可以提供一个替代类。#{loggerlikeCn} + 可以给它一个提供商,让它调用可以创建日志服务的工厂函数。 所有这些方法,只要用在正确的场合,都可能是一个好的选择。 What matters is that the injector has a provider to go to when it needs a `Logger`. - 最重要的是:当注入器需要一个`Logger`时,它得先有一个提供商。 + 最重要的是,当注入器需要一个`Logger`时,它得先有一个提供商。 //- Dart limitation: the provide function isn't const so it cannot be used in an annotation. - var _andProvideFn = _docsFor == 'dart' ? '' : 'and provide object literal'; -- var _andProvideFnCn = _docsFor == 'dart' ? '' : 'and provide对象常量'; +- var _andProvideFnCn = _docsFor == 'dart' ? '' : '和 provide 对象常量'; #provide :marked ### The *Provider* class !{_andProvideFn} @@ -827,7 +847,7 @@ code-example(format="nocode"). :marked We wrote the `providers` #{_array} like this: - 我们像下面一样写`providers`数组: + 像下面一样写`providers`数组: +makeExample('dependency-injection/ts/app/providers.component.ts','providers-1') @@ -836,40 +856,41 @@ block provider-shorthand This is actually a shorthand expression for a provider registration using a _provider_ object literal with two properties: - 这其实是一个用于注册提供商的简写表达式。 + 这其实是用于注册提供商的简写表达式。 使用的是一个带有两个属性的_提供商_对象字面量: +makeExample('dependency-injection/ts/app/providers.component.ts','providers-3') block provider-ctor-args - var _secondParam = 'provider definition object'; + - var _secondParamCn = '供应商定义对象'; :marked The first is the [token](#token) that serves as the key for both locating a dependency value and registering the provider. - 第一个是[令牌token](#token),它作为键值key使用,用于定位依赖值,以及注册这个提供商。 + 第一个是[令牌 (token)](#token),它作为键值 (key) 使用,用于定位依赖值和注册提供商。 The second is a !{_secondParam}, which we can think of as a *recipe* for creating the dependency value. There are many ways to create dependency values ... and many ways to write a recipe. - 第二个是一个!{_secondParam}。 - 我们可以把它看做一个指导如何创建依赖值的*配方*。 + 第二个是!{_secondParamCn}。 + 可以把它看做是指导如何创建依赖值的*配方*。 有很多方式创建依赖值…… 也有很多方式可以写配方。 #class-provider :marked ### Alternative class providers - ### 备选的“类”提供商 + ### 备选的类提供商 Occasionally we'll ask a different class to provide the service. The following code tells the injector to return a `BetterLogger` when something asks for the `Logger`. 某些时候,我们会请求一个不同的类来提供服务。 - 下列代码告诉注入器:当有人请求一个`Logger`时,请返回一个`BetterLogger`。 + 下列代码告诉注入器,当有人请求`Logger`时,返回`BetterLogger`。 +makeExample('dependency-injection/ts/app/providers.component.ts','providers-4') @@ -885,15 +906,16 @@ block dart-diff-const-metadata This logger gets the user from the injected `UserService`, which happens also to be injected at the application level. - 也许一个`EvenBetterLogger`(更好的日志)可以在日志消息中显示用户名。 - 这个日志服务从一个注入进来的`UserService`中取得用户,`UserService`通常也会在应用级被注入。 + 假设`EvenBetterLogger`可以在日志消息中显示用户名。 + 这个日志服务从注入的`UserService`中取得用户, + `UserService`通常也会在应用级注入。 +makeExample('dependency-injection/ts/app/providers.component.ts','EvenBetterLogger')(format='.') :marked Configure it like we did `BetterLogger`. - 就像我们在`BetterLogger`中那样配置它。 + 就像之前在`BetterLogger`中那样配置它。 +makeExample('dependency-injection/ts/app/providers.component.ts','providers-5')(format=".") @@ -906,26 +928,27 @@ block dart-diff-const-metadata `OldLogger` has the same interface as the `NewLogger`, but for some reason we can't update the old component to use it. - 假设一个老的组件依赖于一个`OldLogger`类。 - `OldLogger`有和`NewLogger`相同的接口,但是由于某些原因,我们不能升级这个老组件并使用它。 + 假设某个旧组件依赖一个`OldLogger`类。 + `OldLogger`和`NewLogger`具有相同的接口,但是由于某些原因, + 我们不能升级这个旧组件并使用它。 When the *old* component logs a message with `OldLogger`, we want the singleton instance of `NewLogger` to handle it instead. - 当*老的*组件想使用`OldLogger`记录消息时,我们希望改用`NewLogger`的单例对象来记录。 + 当*旧*组件想使用`OldLogger`记录消息时,我们希望改用`NewLogger`的单例对象来记录。 The dependency injector should inject that singleton instance when a component asks for either the new or the old logger. The `OldLogger` should be an alias for `NewLogger`. - 不管组件请求的是新的还是老的日志服务,依赖注入器注入的都应该是同一个单例对象。 - 也就是说,`OldLogger`应该是`NewLogger`的一个别名。 + 不管组件请求的是新的还是旧的日志服务,依赖注入器注入的都应该是同一个单例对象。 + 也就是说,`OldLogger`应该是`NewLogger`的别名。 We certainly do not want two different `NewLogger` instances in our app. Unfortunately, that's what we get if we try to alias `OldLogger` to `NewLogger` with `useClass`. - 我们当然不会希望应用中有两个`NewLogger`的不同实例。 - 不幸的是,如果我们尝试通过`useClass`来把`OldLogger`作为`NewLogger`的别名,就会导致这样的后果。 + 我们当然不会希望应用中有两个不同的`NewLogger`实例。 + 不幸的是,如果尝试通过`useClass`来把`OldLogger`作为`NewLogger`的别名,就会导致这样的后果。 +makeExample('dependency-injection/ts/app/providers.component.ts','providers-6a')(format=".") @@ -957,7 +980,7 @@ block dart-diff-const-metadata-ctor Then we register a provider with the `useValue` option, which makes this object play the logger role. - 于是我们可以通过`useValue`选项来注册一个提供商,它会让这个对象直接扮演logger的角色。 + 于是可以通过`useValue`选项来注册提供商,它会让这个对象直接扮演 logger 的角色。 - var stylePattern = { otl: /(useValue: \w*)/gm }; +makeExample('dependency-injection/ts/app/providers.component.ts','providers-7', '', stylePattern)(format=".") @@ -967,7 +990,7 @@ block dart-diff-const-metadata-ctor [Non-class dependencies](#non-class-dependencies) and [OpaqueToken](#opaquetoken) sections. - 在[非类依赖](#non-class-dependencies)和[OpaqueToken](#opaquetoken)查看更多`useValue`的例子。 + 查看更多`useValue`的例子,见[非类依赖](#non-class-dependencies)和[OpaqueToken](#opaquetoken)。 #factory-provider :marked @@ -979,12 +1002,12 @@ block dart-diff-const-metadata-ctor based on information we won't have until the last possible moment. Maybe the information changes repeatedly in the course of the browser session. - 有时我们需要动态创建这个依赖值,因为它所需要的信息我们直到最后一刻才能确定。 - 比如,也许这个信息会在浏览器的会话中不停的变化。 + 有时,我们需要动态创建这个依赖值,因为它所需要的信息直到最后一刻才能确定。 + 也许这个信息会在浏览器的会话中不停地变化。 Suppose also that the injectable service has no independent access to the source of this information. - 假设这个可注入的服务没法通过独立的源访问此信息。 + 还假设这个可注入的服务没法通过独立的源访问此信息。 This situation calls for a **factory provider**. @@ -994,9 +1017,9 @@ block dart-diff-const-metadata-ctor the HeroService must hide *secret* heroes from normal users. Only authorized users should see secret heroes. - 我们通过添加一个新的业务需求来说明这一点: - HeroService必须对普通用户隐藏掉*秘密*英雄。 - 只有获得授权的用户才能看到秘密英雄。 + 下面通过添加新的业务需求来说明这一点: + HeroService 必须对普通用户隐藏掉*秘密*英雄。 + 只有授权用户才能看到秘密英雄。 Like the `EvenBetterLogger`, the `HeroService` needs a fact about the user. It needs to know if the user is authorized to see secret heroes. @@ -1005,13 +1028,13 @@ block dart-diff-const-metadata-ctor 就像`EvenBetterLogger`那样,`HeroService`需要了解此用户的身份。 它需要知道,这个用户是否有权看到隐藏英雄。 - 这个授权可能在一个单一的应用会话中被改变,比如我们改用另一个用户的身份登录时。 + 这个授权可能在单一的应用会话中被改变,例如,改用另一个用户的身份登录时。 Unlike `EvenBetterLogger`, we can't inject the `UserService` into the `HeroService`. The `HeroService` won't have direct access to the user information to decide who is authorized and who is not. - 和`EvenBetterLogger`不同,我们不能把`UserService`注入到`HeroService`中。 + 与`EvenBetterLogger`不同,不能把`UserService`注入到`HeroService`中。 `HeroService`无权访问用户信息,来决定谁有授权谁没有授权。 .l-sub-section @@ -1031,8 +1054,8 @@ block dart-diff-const-metadata-ctor We can inject the `Logger`, but we can't inject the boolean `isAuthorized`. We'll have to take over the creation of new instances of this `HeroService` with a factory provider. - 我们可以注入`Logger`,但是我们不能注入逻辑型的`isAuthorized`。 - 我们不得不通过通过一个工厂提供商创建这个`HeroService`的新实例。 + 我们可以注入`Logger`,但是不能注入逻辑型的`isAuthorized`。 + 我们不得不通过通过工厂提供商创建这个`HeroService`的新实例。 A factory provider needs a factory function: @@ -1043,11 +1066,11 @@ block dart-diff-const-metadata-ctor :marked Although the `HeroService` has no access to the `UserService`, our factory function does. - 虽然`HeroService`不能访问`UserService`,但是我们的工厂方法可以。 + 虽然`HeroService`不能访问`UserService`,但是工厂方法可以。 We inject both the `Logger` and the `UserService` into the factory provider and let the injector pass them along to the factory function: - 我们同时把`Logger`和`UserService`注入到工厂提供商中,并且让注入器把它们传给工厂方法: + 同时把`Logger`和`UserService`注入到工厂提供商中,并且让注入器把它们传给工厂方法: +makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','provider', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.') @@ -1056,15 +1079,15 @@ block dart-diff-const-metadata-ctor The `useFactory` field tells Angular that the provider is a factory function whose implementation is the `heroServiceFactory`. - `useFactory`字段告诉Angular:这个提供商是一个工厂方法,它的实现是`heroServiceFactory`。 + `useFactory`字段告诉 Angular:这个提供商是一个工厂方法,它的实现是`heroServiceFactory`。 The `deps` property is #{_an} #{_array} of [provider tokens](#token). The `Logger` and `UserService` classes serve as tokens for their own class providers. The injector resolves these tokens and injects the corresponding services into the matching factory function parameters. - `deps`属性是一个[提供商令牌](#token)数组。 - `Logger`和`UserService`类作为它们自身提供商的令牌。 - 注入器解析这些令牌,并且把相应的服务注入到工厂函数中相应的参数中去。 + `deps`属性是[提供商令牌](#token)数组。 + `Logger`和`UserService`类作为它们自身类提供商的令牌。 + 注入器解析这些令牌,把相应的服务注入到工厂函数中相应的参数中去。 - var exportedvar = _docsFor == 'dart' ? 'constant' : 'exported variable' - var anexportedvarCn = _docsFor == 'dart' ? '一个常量' : '一个导出的变量' @@ -1078,15 +1101,15 @@ block dart-diff-const-metadata-ctor 注意,我们在#{anexportedvarCn}中捕获了这个工厂提供商:`heroServiceProvider`。 这个额外的步骤让工厂提供商可被复用。 - 只要需要,我们就可以使用这个#{variableCn}注册`HeroService`,无论在哪。 + 无论哪里需要,都可以使用这个#{variableCn}注册`HeroService`。 In our sample, we need it only in the `HeroesComponent`, where it replaces the previous `HeroService` registration in the metadata `providers` #{_array}. Here we see the new and the old implementation side-by-side: - 在这个例子中,我们只在`HeroesComponent`中需要它, + 在这个例子中,只在`HeroesComponent`中需要它, 这里,它代替了元数据`providers`数组中原来的`HeroService`注册。 - 我们来对比一下新的和老的实现: + 对比一下新的和旧的实现: - var stylePattern = { otl: /(providers.*),$/gm }; +makeTabs( @@ -1106,16 +1129,16 @@ block dart-diff-const-metadata-ctor The injector maintains an internal *token-provider* map that it references when asked for a dependency. The token is the key to the map. - 当我们为注入器注册一个提供商时,实际上是把这个提供商和一个DI令牌关联起来了。 - 注入器维护一个内部的*令牌-提供商*映射表,这个映射表会在请求一个依赖时被引用到。 - 令牌就是这个映射表中的键值key。 + 当向注入器注册提供商时,实际上是把这个提供商和一个 DI 令牌关联起来了。 + 注入器维护一个内部的*令牌-提供商*映射表,这个映射表会在请求依赖时被引用到。 + 令牌就是这个映射表中的键值。 In all previous examples, the dependency value has been a class *instance*, and the class *type* served as its own lookup key. Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the token: - 在以前的所有范例中,依赖值都是一个类*实例*,并且类的*类型*是它自己的查找键值。 - 这种情况下,我们实际上是直接从注入器中以`HeroService`类型作为令牌,来获取一个`HeroService` 实例。 + 在前面的所有例子中,依赖值都是一个类*实例*,并且类的*类型*作为它自己的查找键值。 + 在下面的代码中,`HeroService`类型作为令牌,直接从注入器中获取`HeroService` 实例: +makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.') @@ -1125,15 +1148,16 @@ block dart-diff-const-metadata-ctor and Angular knows to inject the service associated with that `HeroService` class token: - 写一个需要基于类的依赖注入的构造函数对我们来说是很幸运的。 - 我们只要以`HeroService`类为类型,定义一个构造函数参数,Angular就会知道把跟`HeroService`类令牌关联的服务注入进来: + 编写需要基于类的依赖注入的构造函数对我们来说是很幸运的。 + 只要定义一个`HeroService`类型的构造函数参数, + Angular 就会知道把跟`HeroService`类令牌关联的服务注入进来: +makeExample('dependency-injection/ts/app/heroes/hero-list.component.ts', 'ctor-signature') :marked This is especially convenient when we consider that most dependency values are provided by classes. - 这是一个特殊的规约,因为我们考虑到大多数依赖值都是以类的形式提供的。 + 这是一个特殊的规约,因为大多数依赖值都是以类的形式提供的。 //- TODO: if function injection is useful explain or illustrate why. :marked @@ -1145,10 +1169,9 @@ p | What if the dependency value isn't a class? Sometimes the thing we want to inject is a block non-class-dep-eg span string, function, or object. -p - | 如果依赖值不是一个类呢?有时候我们想要注入的东西是一个 - block non-class-dep-eg - span 一个字符串,函数或者对象。 + +p 如果依赖值不是一个类呢?有时候想要注入的东西是一个字符串,函数或者对象。 + p | Applications often define configuration objects with lots of small facts | (like the title of the application or the address of a web API endpoint) @@ -1156,13 +1179,9 @@ p |  but these configuration objects aren't always instances of a class. | They can be object literals |  such as this one: -p - | 应用程序经常为很多很小的因素定义配置对象 - | (比如应用程序的标题,或者一个网络API终点的地址) - block config-obj-maps - |  但是这些配置对象不总是类的实例。 - | 它们可能是对象 - |  比如下面这个: + +p 应用程序经常为很多很小的因素定义配置对象(例如应用程序的标题或网络API终点的地址)。 + 但是这些配置对象不总是类的实例,它们可能是对象,如下面这个: +makeExample('dependency-injection/ts/app/app.config.ts','config','app/app-config.ts (excerpt)')(format='.') @@ -1170,8 +1189,7 @@ p We'd like to make this configuration object available for injection. We know we can register an object with a [value provider](#value-provider). - 我们想让这个`config`对象在注入时可用。 - 我们已经知道可以使用一个[值提供商](#value-provider)来注册一个对象。 + 我们想让这个配置对象在注入时可用,而且知道可以使用[值提供商](#value-provider)来注册一个对象。 block what-should-we-use-as-token :marked @@ -1179,32 +1197,33 @@ block what-should-we-use-as-token We don't have a class to serve as a token. There is no `AppConfig` class. - 但是这种情况下我们要把什么用作令牌呢? - 我们没办法找一个类来当做令牌,因为没有`Config`类。 + 但是,这种情况下用什么作令牌呢? + 我们没办法找一个类来当作令牌,因为没有`Config`类。 .l-sub-section#interface :marked ### TypeScript interfaces aren't valid tokens - ### TypeScript接口不是一个有效的令牌 + ### TypeScript 接口不是一个有效的令牌 The `HERO_DI_CONFIG` constant has an interface, `AppConfig`. Unfortunately, we cannot use a TypeScript interface as a token: - `CONFIG`常量有一个接口:`Config`。不幸的是,我们不能把TypeScript接口用作令牌: + `CONFIG`常量有一个接口:`AppConfig`。不幸的是,不能把 TypeScript 接口用作令牌: +makeExample('dependency-injection/ts/app/providers.component.ts','providers-9-interface')(format=".") +makeExample('dependency-injection/ts/app/providers.component.ts','provider-9-ctor-interface')(format=".") :marked That seems strange if we're used to dependency injection in strongly typed languages, where an interface is the preferred dependency lookup key. - 如果我们是在一个强类型的语言中使用依赖注入,这会看起来很奇怪,强类型语言中,接口是首选的用于查找依赖的主键。 + 对于习惯于在强类型的语言中使用依赖注入的开发人员,这会看起来很奇怪, + 因为在强类型语言中,接口是首选的用于查找依赖的主键。 It's not Angular's fault. An interface is a TypeScript design-time artifact. JavaScript doesn't have interfaces. The TypeScript interface disappears from the generated JavaScript. There is no interface type information left for Angular to find at runtime. - 这不是Angular的错。接口只是TypeScript的一个设计期概念。JavaScript没有接口。 - 在生成JavaScript代码时,TypeScript的接口就消失了。 - 在运行期,没有接口类型信息可供Angular查找。 + 这不是 Angular 的错。接口只是 TypeScript 设计时 (design-time) 的概念。JavaScript 没有接口。 + TypeScript 接口不会出现在生成的 JavaScript 代码中。 + 在运行期,没有接口类型信息可供 Angular 查找。 // end Typescript only //- FIXME simplify once APIs are defined for Dart. @@ -1216,14 +1235,14 @@ block what-should-we-use-as-token to define and use an !{opaquetoken}. The definition looks like this: - 解决方案是定义和使用一个!{opaquetoken}(不透明的令牌)。定义方式类似于这样: + 解决方案是定义和使用 !{opaquetoken}(不透明的令牌)。定义方式类似于这样: +makeExample('dependency-injection/ts/app/app.config.ts','token')(format='.') :marked We register the dependency provider using the `OpaqueToken` object: - 我们使用这个`OpaqueToken`对象注册依赖的提供商: + 使用这个`OpaqueToken`对象注册依赖的提供商: +makeExample('dependency-injection/ts/app/providers.component.ts','providers-9')(format=".") @@ -1231,7 +1250,7 @@ block what-should-we-use-as-token Now we can inject the configuration object into any constructor that needs it, with the help of an `@Inject` #{_decorator}: - 现在,在`@Inject`#{decoratorCn}的帮助下,我们可以把这个配置对象注入到任何需要它的构造函数中: + 现在,在`@Inject`装饰器的帮助下,这个配置对象可以注入到任何需要它的构造函数中: +makeExample('dependency-injection/ts/app/app.component.2.ts','ctor')(format=".") @@ -1247,7 +1266,7 @@ block dart-map-alternative :marked Or we can provide and inject the configuration object in an ngModule like `AppModule`. - 或者我们在顶级组件`AppComponent`中提供并注入这个配置对象。 + 或者在 ngModule 中提供并注入这个配置对象,如`AppModule`。 +makeExcerpt('app/app.module.ts','ngmodule-providers') @@ -1261,8 +1280,8 @@ block dart-map-alternative We can tell Angular that the dependency is optional by annotating the constructor argument with `@Optional()`: - 我们的`HeroService`*需要*一个`Logger`,但是如果它可以不用一个Logger就行呢? - 我们可以通过把构造函数的参数标记为`@Optional()`来告诉Angular该依赖是可选的: + `HeroService`*需要*一个`Logger`,但是如果想不提供Logger也能得到它,该怎么办呢? + 可以把构造函数的参数标记为`@Optional()`,告诉 Angular 该依赖是可选的: +ifDocsFor('ts') +makeExample('dependency-injection/ts/app/providers.component.ts','import-optional', '') @@ -1273,7 +1292,8 @@ block dart-map-alternative don't register a logger somewhere up the line, the injector will set the value of `logger` to null. - 当使用`@Optional()`时,我们的代码必须要为一个空值做准备。如果我们不在组件或父级组件中注册一个`logger`的话,注入器会设置该`logger`的值为空null。 + 当使用`@Optional()`时,代码必须准备好如何处理空值。 + 如果其它的代码没有注册一个 logger,注入器会设置该`logger`的值为空 null。 .l-main-section :marked @@ -1285,60 +1305,60 @@ block dart-map-alternative and we know how to ask for an injected object (such as a service) by adding a parameter to a constructor. - 在本章中,我们学习了Angular依赖注入的基础。 - 我们可以注册很多种类的提供商,还知道了该如何通过添加构造函数的参数来请求一个被注入对象(比如服务)。 + 本章,我们学习了 Angular 依赖注入的基础知识。 + 我们可以注册很多种类的提供商,知道如何通过添加构造函数的参数来请求一个注入对象(例如一个服务)。 Angular dependency injection is more capable than we've described. We can learn more about its advanced features, beginning with its support for nested injectors, in the [Hierarchical Dependency Injection](hierarchical-dependency-injection.html) chapter. - Angular依赖注入比我们描述的更能干。 - 我们还可以学到它的更多高级特性,从它对嵌套注入器的支持开始,参见[多级依赖注入](hierarchical-dependency-injection.html)一章。 + Angular 依赖注入比前面描述的更能干。 + 学习更多高级特性,如对嵌套注入器的支持,见[多级依赖注入](hierarchical-dependency-injection.html)一章。 .l-main-section#explicit-injector :marked ## Appendix: Working with injectors directly - ## 附录:直接使用注入器工作 + ## 附录:直接使用注入器 We rarely work directly with an injector, but here's an `InjectorComponent` that does. 这里的`InjectorComponent`直接使用了注入器, - 但我们很少直接使用注入器工作。 + 但我们很少直接使用它。 +makeExample('dependency-injection/ts/app/injector.component.ts', 'injector', 'app/injector.component.ts') :marked An `Injector` is itself an injectable service. - `Injector`本身是一个可注入的服务。 + `Injector`本身是可注入的服务。 In this example, Angular injects the component's own `Injector` into the component's constructor. The component then asks the injected injector for the services it wants. - 在这个例子中,Angular把组件自身的`Injector`注入到了组件的构造函数中。 - 然后组件向注入进来的这个注入器请求它所需的服务。 + 在这个例子中,Angular 把组件自身的`Injector`注入到了组件的构造函数中。 + 然后,组件向注入的注入器请求它所需的服务。 Note that the services themselves are not injected into the component. They are retrieved by calling `injector.get`. - 注意,这些服务本身没有被注入到组件中,它们是通过调用`injector.get`获得的。 + 注意,这些服务本身没有注入到组件,它们是通过调用`injector.get`获得的。 The `get` method throws an error if it can't resolve the requested service. We can call `get` with a second parameter (the value to return if the service is not found) instead, which we do in one case to retrieve a service (`ROUS`) that isn't registered with this or any ancestor injector. - `get`方法如果解析不出所请求的服务,它就会抛出一个异常。 - 我们还可以带上第二个参数(如果服务没找到,就把它作为默认值返回)调用`get`, - 在该例子中,我们获取了一个服务(`ROUS`),它没有在这个注入器或它的任何祖先中注册过。 + `get`方法如果不能解析所请求的服务,会抛出异常。 + 调用`get`时,还可以使用第二个参数,一旦获取的服务 (`ROUS`) 没有在当前或任何祖先注入器中注册过, + 就把它作为返回值。 .l-sub-section :marked The technique we just described is an example of the [service locator pattern](https://en.wikipedia.org/wiki/Service_locator_pattern). - 我们刚描述的这项技术是[服务定位器模式](https://en.wikipedia.org/wiki/Service_locator_pattern)的一个范例。 + 刚描述的这项技术是[服务定位器模式](https://en.wikipedia.org/wiki/Service_locator_pattern)的一个范例。 We **avoid** this technique unless we genuinely need it. It encourages a careless grab-bag approach such as we see here. @@ -1347,23 +1367,23 @@ block dart-map-alternative It could acquire services from any ancestor component, not just its own. We're forced to spelunk the implementation to discover what it does. - 我们要**避免使用**此技术,除非我们确实需要它。 - 它会鼓励鲁莽的方法,就像我们在这里看到的。 + 要**避免使用**此技术,除非确实需要它。 + 它会鼓励鲁莽的方式,就像在这里看到的。 它难以解释、理解和测试。 - 仅通过阅读构造函数,我们没法知道这个类需要什么或者它将做什么。 + 仅通过阅读构造函数,没法知道这个类需要什么或者它将做什么。 它可以从任何祖先组件中获得服务,而不仅仅是它自己。 - 我们会被迫深入它的实现,才可能明白它都做了啥。 + 会迫使我们深入它的实现,才可能明白它都做了啥。 Framework developers may take this approach when they must acquire services generically and dynamically. - 在框架程序员必须采用泛型或者动态方式获取服务时,他们可能采用这个方法。 + 框架开发人员必须采用通用的或者动态的方式获取服务时,可能采用这个方法。 +ifDocsFor('ts') .l-main-section#one-class-per-file :marked ## Appendix: Why we recommend one class per file - ## 附录:为什么我们建议每个文件只放一个类 + ## 附录:为什么建议每个文件只放一个类 Having multiple classes in the same file is confusing and best avoided. Developers expect one class per file. Keep them happy. @@ -1377,8 +1397,10 @@ block dart-map-alternative If we define the component before the service, we'll get a runtime null reference error. - 如果我们蔑视这个建议,并且 —— 比如说 —— 把`HeroService`和`HeroesComponent`组合在同一个文件里,**就得把组件定义放在后面!** - 如果我们把组件定义在了服务的前面,就会在运行时获得一个空指针错误。 + 如果我们蔑视这个建议,并且 —— 比如说 —— 把`HeroService`和`HeroesComponent`组合在同一个文件里, + **就得把组件定义放在最后面!** + 如果把组件定义在了服务的前面, + 在运行时抛出空指针错误。 .l-sub-section :marked @@ -1387,7 +1409,7 @@ block dart-map-alternative But why flirt with trouble? Avoid the problem altogether by defining components and services in separate files. - 在`forwardRef()`方法的帮助下,我们实际上也可以先定义组件。 - 它的原理解释在这个[博客](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html)中。 + 在`forwardRef()`方法的帮助下,实际上也可以先定义组件, + 具体说明见这个[博客](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html)。 但是为什么要先给自己找麻烦呢? 还是通过在独立的文件中定义组件和服务,完全避免此问题吧。 diff --git a/public/resources/css/_translate.scss b/public/resources/css/_translate.scss index e84f1fea39..b20e1cc862 100644 --- a/public/resources/css/_translate.scss +++ b/public/resources/css/_translate.scss @@ -43,6 +43,14 @@ } } + .callout { + li { + p { + padding: 0; + } + } + } + th, td, li { p { margin: 0;