first review of dependency-injection.jade is completed.

This commit is contained in:
Zhimin(Rex) YE 2016-05-15 22:48:49 +01:00
parent 44e5e8fca3
commit 74b13f5f60
1 changed files with 181 additions and 106 deletions

View File

@ -9,6 +9,7 @@ include ../_util-fns
<a id="toc"></a>
:marked
## Table of contents
## 目录
[Application-wide dependencies](#app-wide-dependencies)
@ -118,6 +119,7 @@ include ../_util-fns
<a id="app-wide-dependencies"></a>
:marked
## Application-wide dependencies
## 应用程序全局依赖
Register providers for dependencies used throughout the application in the root application component, `AppComponent`.
@ -160,6 +162,7 @@ include ../_util-fns
.l-main-section
:marked
## External module configuration
## 外部模块设置
We can register _certain_ module providers when bootstrapping rather than in the root application component.
@ -199,6 +202,7 @@ a(id="nested-dependencies")
.l-main-section
:marked
## *@Injectable* and nested service dependencies
## *@Injectable*和嵌套服务依赖
The consumer of an injected service does not know how to create that service.
@ -251,6 +255,7 @@ figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/logged-in-user.png" alt="Logged In User")
:marked
### *@Injectable()*
### *@Injectable()*
Notice the `@Injectable()`decorator on the `UserContextService` class.
@ -297,6 +302,7 @@ figure.image-display
.l-main-section
:marked
## Limit service scope to a component subtree
## 限制服务作用范围到一个组件支树
All injected service dependencies are singletons meaning that,
@ -356,6 +362,7 @@ figure.image-display
.alert.is-helpful
:marked
### Take a break!
### 休息一下!
This much Dependency Injection knowledge may be all that many Angular developers
@ -428,6 +435,7 @@ a(id="qualify-dependency-lookup")
.l-main-section
:marked
## Qualify dependency lookup with *@Optional* and *@Host*
## 使用*@Optional*和*@Host*装饰来认证依赖调用过程
We learned that dependencies can be registered at any level in the component hierarchy.
@ -558,6 +566,7 @@ figure.image-display
<a id="component-element"></a>
:marked
## Inject the component's element
## 注入组件的元素
On occasion we might need to access a component's corresponding DOM element.
@ -603,6 +612,7 @@ figure.image-display
.l-main-section
:marked
## Define dependencies with providers
## 使用供应商来定义依赖
In this section we learn to write providers that deliver dependent services.
@ -639,7 +649,7 @@ figure.image-display
注入器在哪儿得到的依赖?
它可能在自己内部容器里已经有该依赖了。
如果它没有,它可能能在***供应商***的帮助下新建一个。
*供应商*是通过令牌派送对应服务的方法
通过令牌,*供应商*可以派送对应服务
.l-sub-section
:marked
@ -647,53 +657,61 @@ figure.image-display
to its parent injector, where the process repeats until there are no more injectors.
If the search is futile, the injector throws an error ... unless the request was [optional](#optional).
如果注入器对请求的令牌没有一个对应的供应商,它便将这个请求交给它父级的注入器,这个过程反复从夫,知道没有更多注入器位置
如果搜索无用,注入器便抛出一个错误...除非这个请求是[optional](#optional)。
如果注入器根据令牌在自己内部找不到对应的供应商,它便将请求移交给它父级注入器,这个过程反复重复,直到没有更多注入器为止
如果搜索无用,注入器便抛出一个错误...除非这个请求是[可选的](#optional)。
Let's return our attention to providers themselves.
让我们把注意力转回到供应商。
:marked
A new injector has no providers.
一个新的注入器没有供应商。
新的注入器是没有供应商。
Angular initializes the injectors it creates with some providers it cares about.
We have to register our _own_ application providers manually,
usually in the `providers` array of the `Component` or `Directive` metadata:
Angular初始化一些他自己建立和需要的注入器附带一些供应商。我们必须要亲自手动注册属于_自己_的供应商通常在`组件`或者`指令`的元数据里面的`providers`数组(里面注册)。
Angular使用一些自带的供应商来初始化自己在乎的注入器。我们必须要亲自注册属于_自己_的供应商通常在`组件`或者`指令`的元数据里面的`providers`数组(里面注册)。
+makeExample('cb-dependency-injection/ts/app/app.component.ts','providers','app/app.component.ts (providers)')
:marked
### Defining providers
### 定义供应商
The simple class provider is the most typical by far.
We mention the class in the `providers` array and we're done.
目前一个简单的类供应商是最典型的例子。
我们在`providers`的数值里面提到该类就行了。
简单的类供应商是最典型的例子。我们在`providers`数值里面提到该类就可以了。
+makeExample('cb-dependency-injection/ts/app/hero-bios.component.ts','class-provider','app/hero-bios.component.ts (class provider)')(format='.')
:marked
It's that simple because the most common injected service is an instance of a class.
But not every dependency can be satisfied by creating a new instance of a class.
We need other ways to deliver dependency values and that means we need other ways to specify a provider.
这就是那么简单,因为最常见的被注入的服务是一个类的实例。但是,不是所有的依赖能在能建立一个类的新实例就够了的。我们需要其它的提交依赖值得方法,也就是说我们需要其它方法来指定一个供应商。
注入依赖就是那么简单,因为最常见的被注入的服务是一个类的实例。
但是,不是所有的依赖能在建立一个类的新实例就够了的。我们需要其它的提交依赖的方法,也就是说我们需要其它方法来指定一个供应商。
The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why we need them.
`HeroOfTheMonthComponent`例子示范了一些备择方案,揭示了为什么我们需要它们。
`HeroOfTheMonthComponent`例子示范了一些备择方案,展示了为什么需要它们。
figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/hero-of-month.png" alt="Hero of the month" width="300px")
:marked
It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about.
它看起来很简单一些属性和一个日志output。但是后面的代码给我们很多议论点。
它看起来很简单:一些属性和一个日志输出。但是代码的背后给了我们很多议论点。
+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','hero-of-the-month','hero-of-the-month.component.ts')
.l-main-section
a(id='provide')
:marked
#### The *provide* function
#### *provide*函数
The imported Angular `provide` function creates an instance of
@ -709,35 +727,38 @@ a(id='provide')
The *definition* object has one main property, (e.g. `useValue`) that indicates how the provider
should create or return the provided value.
该*定义*项目有一个主要属性,(即为`userValue`)来标识该供应商应该怎么新建和返回被提供的结果
该*定义*项目有一个主要属性(即为`userValue`),用来标识该供应商应该如何新建和返回依赖
.l-main-section
a(id='usevalue')
:marked
#### useValue - the *value provider*
#### useValue - *值供应商*
#### useValue - *值-供应商*
Set the `useValue` property to a ***fixed value*** that the provider can return as the dependency object.
把一个***固定的值**,也就是供应商可以当作依赖对象返回的值,赋给`userValue`属性。
把一个***固定的值**,也就是供应商可以将其当作依赖对象返回的值,赋给`userValue`属性。
Use this technique to provide *runtime configuration constants* such as web-site base addresses and feature flags.
We often use a *value provider* in a unit test to replace a production service with a fake or mock.
使用该技巧来提供*运行时间设置常量*,比如网站的基础地址和功能标志等。我们通常在单元测试中使用一个*value 供应商*,用一个假的或模仿的(服务)来取代一个成品服务。
使用该技巧来进行*运行时常量设置*,比如网站的基础地址和功能标志等。
我们通常在单元测试中使用一个*值-供应商*,用一个假的或模仿的(服务)来取代一个成品服务。
The `HeroOfTheMonthComponent` example has two *value providers*.
The first provides an instance of the `Hero` class;
the second specifies a literal string resource:
`HeroOfTheMonthComponent`例子有两个*value 供应商*。
第一个提供了一个`Hero`类的实例;第二个指定了一个文字字符串资源:
`HeroOfTheMonthComponent`例子有两个*值-供应商*。
第一个提供了一个`Hero`类的实例;第二个指定了一个字符串资源:
+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-value')(format='.')
:marked
The `Hero` provider token is a class which makes sense because the value is a `Hero`
and the consumer of the injected hero would want the type information.
该`Hero`供应商令牌是一个类,这很合理,因为提供的结果是一个`Hero`,而且使用这个被注入英雄的组件也想要知道这个类型信息。
`Hero`供应商的令牌是一个类,这很合理,因为它提供的结果是一个`Hero`实例,并且使用这个被注入英雄的组件也想要知道它类型信息。
The `TITLE` provider token is *not a class*.
It's a special kind of provider lookup key called an [OpaqueToken](#opaquetoken).
@ -749,19 +770,21 @@ a(id='usevalue')
Obviously the title string literal is immediately available.
The `someHero` variable in this example was set earlier in the file:
一个*value 供应商*的值必须要*立刻*定义。我们不能过后定义它的值。很显然,该标题字符串立刻可用。
该例中的`someHero`变量是于早先,在下面的文件里面定义的:
一个*值-供应商*的值必须要*立刻*定义。我们不能过后定义它的值。很显然,标题字符串立刻可用。
该例中的`someHero`变量是在之前,在下面的文件里面定义的:
+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','some-hero')
:marked
The other providers create their values *lazily* when they're needed for injection.
其它供应商只在注入需要它们的时候才创建它们值 - *延迟加载*
其它供应商只在需要注入它们的时候才创建并*延迟加载*它们值。
.l-main-section
a(id='useclass')
:marked
#### useClass - the *class provider*
#### useClass - *类供应商*
#### useClass - *类-供应商*
The `useClass` provider creates and returns new instance of the specified class.
@ -771,25 +794,27 @@ a(id='useclass')
The alternative could implement a different strategy, extend the default class,
or fake the behavior of the real class in a test case.
使用此项技术来为公共或默认类***提供候选实现***。该候选方法能实现一个不同的策略,拓展默认类或者在测试的时候假冒真类的行为
使用此项技巧来为公共或默认类***提供候选实施***。该候选方法能实现一个不同的策略,比如拓展默认类或者在测试的时候假冒真类。
We see two examples in the `HeroOfTheMonthComponent`:
请看下面`HeroOfTheMonthComponent`里的两个例子:
+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-class')(format='.')
:marked
The first provider is the *de-sugared*, expanded form of the most typical case in which the
class to be created (`HeroService`) is also the provider's injection token.
We wrote it in this long form to de-mystify the preferred short form.
第一个供应商是*无语法糖的*,是从典型的情况扩展的,一般被新建的类(`HeroService`)同时也是该供应商的注入令牌。
我们使用长的形式来编写它来解释首选的短形式。
第一个供应商是*无语法糖的*,是从一个典型的情况扩展的。一般,被新建的类(`HeroService`)同时也是该供应商的注入令牌。
我们使用长的形式来编写它,用来解释我们喜欢的缩写形式。
The second provider substitutes the `DateLoggerService` for the `LoggerService`.
The `LoggerService` is already registered at the `AppComponent` level.
When _this component_ requests the `LoggerService`, it receives the `DateLoggerService` instead.
第二个供应商使用`DateLoggerService`来满足`LoggerService`。该`LoggerService`在`AppComponent`级别已经被注册。当_这个组件_要求`LoggerService`的时候,取而代之,它得到的是`DateLoggerService`服务。
.l-sub-section
:marked
This component and its tree of child components receive the `DateLoggerService` instance.
@ -800,19 +825,22 @@ a(id='useclass')
The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message:
`DateLoggerService`从`LoggerService`继承;它把当前的日期/时间附加到每条信息上。
+makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','date-logger-service','app/date-logger.service.ts')(format='.')
.l-main-section
a(id='useexisting')
:marked
#### useExisting - the *alias provider*
#### useExisting - *别名供应商*
#### useExisting - *别名-供应商*
The `useExisting` provider maps one token to another.
In effect, the first token is an ***alias*** for the service associated with second token,
creating ***two ways to access the same service object***.
使用`useExisting` 供应商来把一个令牌映射到另一个。实际上,第一个令牌是第二个令牌所对应的服务的一个***别名***,构成***对一个服务对象的两种访问方法***。
使用`useExisting` 供应商来把一个令牌映射到另一个令牌。实际上,第一个令牌是第二个令牌所对应的服务的一个***别名***,构成***对一个服务对象的两种访问方法***。
+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-existing')
:marked
Narrowing an API through an aliasing interface is _one_ important use case for this technique.
@ -820,14 +848,15 @@ a(id='useexisting')
Imagine that the `LoggerService` had a large API (it's actually only three methods and a property).
We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](#class-interface):
通过使用别名接口把一个API变窄是_一个_很重要的该技术的使用例子。我们在这里就是为了这个目的使用了别名。
想象一下如果`LoggerService`有个很大的API(它其实只有三个方法,一个属性),我们通过使用`MinimalLogger`[*类-接口*](#class-interface)别名把这个API界面缩小到只暴露两个成员
通过使用别名接口来把一个API变窄是_一个_很重要的该技巧的使用例子。我们在这里就是为了这个目的使用了别名。
想象一下如果`LoggerService`有个很大的API(它其实只有三个方法,一个属性),我们通过使用`MinimalLogger`[*类-接口*](#class-interface)别名,成功把这个API界面缩小到只暴露两个成员
+makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','minimal-logger','app/date-logger.service.ts (MinimalLogger)')(format='.')
:marked
The constructor's `logger` parameter is typed as `MinimalLogger` so only its two members are visible in TypeScript:
构造函数的`logger`参数是一个`MinimalLogger`类型所有它只有两个成员在TypeScript里面可见
构造函数的`logger`参数是一个`MinimalLogger`类型所有在TypeScript里面它只有两个成员可见
figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API")
:marked
@ -835,8 +864,9 @@ figure.image-display
which happens to be the `DateLoggerService` thanks to the override provider registered previously via `useClass`.
The following image, which displays the logging date, confirms the point:
Angular实际上设置`logger`参数给注入器里的`LoggerService`的完整版本,该完整版本被`DateLoggerService`所取代多谢在之前provder注册里使用`useClass`
实际上Angular把`logger`参数设置给注入器里的`LoggerService`的完整版本。因为在之前供应商注册里使用`useClass`,该完整版本被`DateLoggerService`所取代
在下面的图片中,显示了日志日期,确认了这点:
figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px")
@ -844,18 +874,20 @@ figure.image-display
a(id='usefactory')
:marked
#### useFactory - the *factory provider*
#### useFactory - *factory 供应商*
#### useFactory - *工厂-供应商*
The `useFactory` provider creates a dependency object by calling a factory function
as seen in this example.
`useFactory` 供应商通过调用一个在下例看到的factory函数来新建一个依赖对象。
`useFactory` 供应商通过调用工厂函数来新建一个依赖对象,如下例所示。
+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-factory')
:marked
Use this technique to ***create a dependency object***
with a factory function whose inputs are some ***combination of injected services and local state***.
使用这个技巧,利用一个包含了一些***依赖服务和本地状态***的输入的factory函数来***建立一个依赖对象***。
使用这个技巧,利用一个包含了一些***依赖服务和本地状态***输入的工厂函数来***建立一个依赖对象***。
The *dependency object* doesn't have to be a class instance. It could be anything.
In this example, the *dependency object* is a string of the names of the runners-up
@ -871,7 +903,7 @@ a(id='usefactory')
The `runnersUpFactory` itself isn't the provider factory function.
The true provider factory function is the function that `runnersUpFactory` returns.
`runnersUpFactory`自己供应商 factory函数。真正的供应商 factory函数是一个`runnersUpFactory`返回的函数。
`runnersUpFactory`自身不是供应商工厂函数。真正的供应商工厂函数是`runnersUpFactory`返回的函数。
+makeExample('cb-dependency-injection/ts/app/runners-up.ts','factory-synopsis','runners-up.ts (excerpt)')(format='.')
:marked
@ -884,12 +916,12 @@ a(id='usefactory')
The two `deps` values are *tokens* that the injector uses
to provide these factory function dependencies.
Angular通过注入值提供这些参数,这些值是从`deps`数组中使用两个*令牌*来提取的。这两个`deps`值是注入器使用*令牌*来提供的factory函数依赖。
Angular通过使用`deps`数组中两个*令牌*,来识别注入的值,用来满足这些参数。这两个`deps`值是注入器使用的*令牌*,用来提供工厂函数依赖。
After some undisclosed work, the function returns the string of names
and Angular injects it into the `runnersUp` parameter of the `HeroOfTheMonthComponent`.
一些没有公开的工作后这个函数返回名字字符串Angular将其注入到`HeroOfTheMonthComponent`组件的`runnersUp`参数里面。
一些内部工作后这个函数返回名字字符串Angular将其注入到`HeroOfTheMonthComponent`组件的`runnersUp`参数里面。
.l-sub-section
:marked
@ -898,25 +930,25 @@ a(id='usefactory')
Look at the [live example](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)
for the full source code.
该函数从`HeroService`获取英雄参赛者,从中取`2`个作为亚军,并把他们的名字合并起来。请到[在线例子](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)看全部原代码。
该函数从`HeroService`获取英雄参赛者,从中取`2`个作为亚军,并把他们的名字合并起来。请到[在线例子](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)查看全部原代码。
a(id="tokens")
.l-main-section
:marked
## Provider token alternatives: the *class-interface* and *OpaqueToken*
## 可选供应商令牌:**类-接口*和*OpaqueToken*
## 可选供应商令牌:*类-接口*和*OpaqueToken*
Angular dependency injection is easiest when the provider *token* is a class
that is also the type of the returned dependency object (what we usually call the *service*).
Angular依赖注入在*令牌*为类,并且该类同时也是返回依赖的对象的时候最为容易(我们通常叫*服务*
Angular依赖注入在注入服务的时候是最简单的:*令牌*为类,而且该类同时也是返回的依赖对象
But the token doesn't have to be a class and even when it is a class,
it doesn't have to be the same type as the returned object.
That's the subject of our next section.
但是令牌不一定非要是类,就算它是一个类,它也不一定非要是返回对象相同类型的类。这是我们下一节的课题。
但是令牌不一定都是类,就算它是一个类,它也不一定都返回类型相同的对象。这是我们的下一节课题。
<a id="class-interface"></a>
### class-interface
@ -931,6 +963,7 @@ a(id="tokens")
The `MinimalLogger` is an abstract class.
该`MinimalLogger`是一个抽象类。
+makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','minimal-logger')(format='.')
:marked
We usually inherit from an abstract class.
@ -941,7 +974,8 @@ a(id="tokens")
Look again at the declaration for `DateLoggerService`
请再看`DateLoggerService`的声明。
请再看`DateLoggerService`的声明
+makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','date-logger-service-signature')(format='.')
:marked
`DateLoggerService` inherits (extends) from `LoggerService`, not `MinimalLogger`.
@ -953,24 +987,27 @@ a(id="tokens")
The key benefit of a *class-interface* is that we can get the strong-typing of an interface
and we can ***use it as a provider token*** in the same manner as a normal class.
我们称这种用法的类叫做*类-接口*。它关键的好处是一个*类-接口*给我们提供了一个接口的强类型,同时,我们能想正常类一样***使用它作为一个供应商令牌***。
我们称这种用法的类叫做*类-接口*。它关键的好处是:给我们提供了接口的强类型,同时,我们能像正常类一样***把它当做供应商令牌使用***。
A ***class-interface*** should define *only* the members that its consumers are allowed to call.
Such a narrowing interface helps decouple the concrete class from its consumers.
The `MinimalLogger` defines just two of the `LoggerClass` members.
一个***类-接口***应该*只*定义它的消费者容许调用的成员。这样一个窄的接口帮助我们分离该类的具体类实例和他的消费者。
***类-接口***应该*只*定义它的消费者允许调用的成员。窄的接口帮助我们分离该类的具体类实例和它的消费者。
该`MinimalLogger`只定义了两个`LoggerClass`的成员。
.l-sub-section
:marked
#### Why *MinimalLogger* is a class and not an interface
#### 为什么*MinimalLogger*是一个类而不是一个接口
We can't use an interface as a provider token because
interfaces are not JavaScript objects.
They exist only in the TypeScript design space.
They disappear after the code is transpiled to JavaScript.
### 为什么*MinimalLogger*是一个类而不是一个接口?因为我们不能把一个接口当做一个供应商的令牌因为接口不是JavaScript对象。
我们不能把接口当做供应商的令牌,因为接口不是有效的JavaScript对象。
它们只存在在TypeScript的设计空间里。它们会在被编译到JavaScript之后消失。
A provider token must be a real JavaScript object of some kind:
@ -985,40 +1022,44 @@ a(id="tokens")
The minimize memory cost, the class should have *no implementation*.
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript:
为了节省内存费用,该类不应该有*执行*。该`MinimalLogger`翻译到一个下面这个没有优化的最小化之前的JavaScript
为了节省内存占用,该类应该***没有具体实施***。`MinimalLogger`会被翻译到一个下面这个没有优化的最小化之前的JavaScript
+makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','minimal-logger-transpiled')(format='.')
:marked
It never grows larger no matter how many members we add *as long as they are typed but not implemented*.
*只要我们不实现它*,不管我们添加多少成员,它永远不会增长大小。
***只要我们不实施它***,不管我们添加多少成员,它永远不会增长大小。
a(id='opaque-token')
:marked
### OpaqueToken
### OpaqueToken
Dependency objects can be simple values like dates, numbers and strings or
shapeless objects like arrays and functions.
依赖对象可以是一个简单的,比如日期,数字和字符串,或者一个没有形状的对象,比如数组和函数。
依赖对象可以是一个简单的,比如日期,数字和字符串,或者一个没有形状的对象,比如数组和函数。
Such objects don't have application interfaces and therefore aren't well represented by a class.
They're better represented by a token that is both unique and symbolic,
a JavaScript object that has a friendly name but won't conflict with
another token that happens to have the same name.
这样的对象没有应用程序接口,所以不能用一个类来表示。它们可被一个唯一的和标志性的令牌更好的表达一个JavaScript对象拥有一个友好的名字但是不会与其它名字相同的令牌有冲突。
这样的对象没有应用程序接口,所以不能用一个类来表示。能更好表达它们的是:唯一的和标志性的令牌一个JavaScript对象拥有一个友好的名字但是不会与其它对象同名令牌有冲突。
The `OpaqueToken` has these characteristics.
We encountered them twice in the *Hero of the Month* example,
in the *title* value provider and in the *runnersUp* factory provider.
`OpaqueToken`具有这些特征。我们在*Hero of the Month*例子中遇见它们两次,一个是*title*的值,一个是*runnersUp* factory 供应商。
`OpaqueToken`具有这些特征。我们在*Hero of the Month*例子中遇见它们两次,一个是*title*的值,一个是*runnersUp* 工厂供应商。
+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','provide-opaque-token')(format='.')
:marked
We created the `TITLE` token like this:
我们这样创建该`TITLE`令牌:
我们这样创建`TITLE`令牌:
+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','opaque-token')(format='.')
@ -1026,14 +1067,15 @@ a(id="di-inheritance")
.l-main-section
:marked
## Inject into a derived class
## 注入一个衍生类
## 注入到一个衍生类
We must take care when writing a component that inherits from another component.
If the base component has injected dependencies,
we must re-provide and re-inject them in the derived class
and then pass them down to the base class through the constructor.
当编写一个从另一个组件衍生的组件的时候,我们要格外小心。如果基础组件有注入的依赖,我们必须要在衍生类中重新提供和重新注入它们,并且将它们通过构造函数传给基类。
当编写一个衍生另一个组件的组件时,我们要格外小心。如果基础组件有依赖注入,我们必须要在衍生类中重新提供和重新注入它们,并且将它们通过构造函数传给基类。
In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent`
to display a *sorted* list of heroes.
@ -1056,12 +1098,13 @@ figure.image-display
This rule makes the component safe to construct under test without fear that it will do something dramatic like talk to the server.
That's why we call the `HeroService` from within the `ngOnInit` rather than the constructor.
我们强烈的推荐简单的结构函数。它们应该只用来初始化变量。这个规则让组件在测试环境中被安全的构造,免除他可能要去做一些非常戏剧化的动作(比如连接服务)的担心
我们强烈推荐简单的结构函数。它们应该***只***用来初始化变量。这个规则帮助我们组件在测试环境中放心构造它们,免除构造它们时,无意做了一些非常戏剧化的动作(比如连接服务)。
这就是为什么我们在`ngOnInit`里面调用`HeroService`,而不是在构造函数中。
We explain the mysterious `_afterGetHeroes` below.
我们在下面解释这个神秘的`_afterGetHeroes`。
:marked
Users want to see the heroes in alphabetical order.
Rather than modify the original component, we sub-class it and create a
@ -1069,14 +1112,14 @@ figure.image-display
The `SortedHeroesComponent` lets the base class fetch the heroes.
(we said it was contrived).
用户希望看到英雄按字母顺序排序。与其修改原始的组件,我们衍生它,建立一个`SortedHeroesComponent`,用来在展示英雄之前进行排序。
该`SortedHeroesComponent`让基本类来获取英雄。(我们说过这个是人为的)。
用户希望看到英雄按字母顺序排序。与其修改原始的组件,我们衍生它,建`SortedHeroesComponent`,用来在展示英雄之前进行排序。
`SortedHeroesComponent`让基础类来获取英雄。(我们说过这么做是人为的,仅用来解释机制)。
Unfortunately, Angular cannot inject the `HeroService` directly into the base class.
We must provide the `HeroService` again for *this* component,
then pass it down to the base class inside the constructor.
可惜Angular不能直接在基类里直接注入`HeroService`。我们必须在*这个*组件里再次提供`HeroService`,然后通过构造函数传给基础类。
可惜Angular不能直接在基类里直接注入`HeroService`。我们必须在*这个*组件里再次提供`HeroService`,然后通过构造函数传给基础类。
+makeExample('cb-dependency-injection/ts/app/sorted-heroes.component.ts','sorted-heroes','app/sorted-heroes.component.ts (SortedHeroesComponent)')
:marked
@ -1085,72 +1128,75 @@ figure.image-display
But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit`
so we'd be sorting the heroes array *before they arrived*. That produces a nasty error.
现在,请注意`_afterGetHeroes`方法。
我们第一反应是在`SortedHeroesComponent`组件里面建一个`ngOnInit`方法来做排序。但是Angular先调用*衍生*类的`ngOnInit`,后调用基本类的`ngOnInit`
现在,请注意`_afterGetHeroes`方法。
我们第一反应是在`SortedHeroesComponent`组件里面建一个`ngOnInit`方法来做排序。但是Angular先调用*衍生*类的`ngOnInit`,后调用基础类的`ngOnInit`
所以我们可能在*英雄到达之前*开始排序。这便产生了一个可恶的错误。
Overriding the base class's `_afterGetHeroes` method solves the problem
覆盖基类的`_afterGetHeroes`方法解决了这个问题。
覆盖基类的`_afterGetHeroes`方法解决了这个问题。
These complications argue for *avoiding component inheritance*.
这些复杂性强调了*避免使用组件衍生*(的潜规则)
上面的这些复杂性强调了*避免使用组件衍生*的法则
a(id="find-parent")
.l-main-section
:marked
## Find a parent component by injection
## 通过注入来找到一个父级组件
Application components often need to share information.
We prefer the more loosely coupled techniques such as data binding and service sharing.
But sometimes it makes sense for one component to have a direct reference to another component
perhaps to access values or call methods on that component.
应用程序组件经常需要共享信息。我们喜欢更加松散结合的技术,比如数据绑定和服务共享。
但是有时候一个组件拥有一个直接的另一个组件的引用会更加合理,可能用来访问值或者调用该组件的函数方法。
但是有时候组件需要拥有另一个组件的引用,用来访问该组件的属性值或者调用它的函数方法。
Obtaining a component reference is a bit tricky in Angular.
Although an Angular application is a tree of components,
there is no public API for inspecting and traversing that tree.
在Angular里面获取一个组件的引用比较复杂。虽然一个Angular应用程序是一个组件树但是没有公共API来在该树种巡查和穿梭。
在Angular里面获取一个组件的引用比较复杂。虽然Angular应用程序是一个组件树但是没有公共API来在该树种巡查和穿梭。
There is an API for acquiring a child reference
(checkout `Query`, `QueryList`, `ViewChildren`, and `ContentChildren`).
有一个API可以获取一个子级引用(请看`Query`, `QueryList`, `ViewChildren`,和`ContentChildren`)。
有一个API可以获取子级引用(请看`Query`, `QueryList`, `ViewChildren`,和`ContentChildren`)。
There is no public API for acquiring a parent reference.
But because every component instance is added to an injector's container,
we can use Angular dependency injection to reach a parent component.
没有公共API来获取一个父级组件的引用。但是因为每个组件实例都被加到了一个注入器的容器中我们能使用Angular依赖注入来抓住一个父级组件。
没有公共API来获取父级组件的引用。但是因为每个组件实例都被加到了依赖注入器容器中我们可以使用Angular依赖注入来抓住父级组件。
This section describes some techniques for doing that.
该节描述了怎么做这个的技巧。
本章节描述了这个技巧。
<a id="known-parent"></a>
### Find a parent component of known type
### 找到一个已知类型的父级组件
### 找到已知类型的父级组件
We use standard class injection to acquire a parent component whose type we know.
我们使用一个标准类注入来获取一个已知类型的父级组件。
我们使用标准类注入来获取已知类型的父级组件。
In the following example, the parent `AlexComponent` has several children including a `CathyComponent`:
在下面的例子中,父级`AlexComponent`有几个子级,包括一个`CathyComponent`:
在下面的例子中,父级`AlexComponent`有几个子级,包括`CathyComponent`:
a(id='alex')
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-1','parent-finder.component.ts (AlexComponent v.1)')(format='.')
:marked
*Cathy* reports whether or not she has access to *Alex*
after injecting an `AlexComponent` into her constructor:
在注入一个*AlexComponent`进来后,*Cathy*报告她是否对*Alex*有访问权:
在注入*AlexComponent`进来后,*Cathy*报告它是否对*Alex*有访问权:
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','cathy','parent-finder.component.ts (CathyComponent)')(format='.')
:marked
We added the [@Optional](#optional) qualifier for safety but
@ -1161,24 +1207,25 @@ a(id='alex')
<a id="base-parent"></a>
### Cannot find a parent by its base class
### 不能通过它的基本类找到一个父级
### 无法通过它的基础类找到一个父级
What if we do *not* know the concrete parent component class?
如果我们*不*知道父级组件类的具体实怎么办?
如果我们*不*知道父级组件类的具体实怎么办?
A re-usable component might be a child of multiple components.
Imagine a component for rendering breaking news about a financial instrument.
For sound (cough) business reasons, this news component makes frequent calls
directly into its parent instrument as changing market data stream by.
一个可重复使用的组件可能是多个组件的子级。想想一个用来渲染金融工具头条新闻的组件。为了合理(咳嗽)的商业理由,该新闻组件在实时变化的市场数据流过时要频繁的直接调用其父级工具
一个可重复使用的组件可能是多个组件的子级。想象一个用来渲染金融工具头条新闻的组件。为了合理(咳咳)的商业理由,该新闻组件在实时变化的市场数据流过时要频繁的直接调用其父级工具
The app probably defines more than a dozen financial instrument components.
If we're lucky, they all implement the same base class
whose API our `NewsComponent` understands.
该应用程序可能定义多于一打的金融工具组件。如果我们幸运,它们可能实现同一个基本类其API是我们`NewsComponent`组件明白的。
该应用程序可能有多于一打的金融工具组件。如果我们幸运,它们可能从同一个基础类衍生其API是我们`NewsComponent`组件明白的。
.l-sub-section
:marked
@ -1186,22 +1233,25 @@ a(id='alex')
That's not possible because TypeScript interfaces disappear from the transpiled JavaScript
which doesn't support interfaces. There's no artifact we could look for.
找一个实现一个接口的组件会好一些。但是这不可能因为TypeScript的借口在编译成JavaScript以后就消失了JavaScript不支持接口。我们没有东西可查。
更好的是通过接口来寻找实施它的组件。但是这不可能因为TypeScript的接口在编译成JavaScript以后就消失了JavaScript不支持接口。我们没有东西可查。
:marked
We're not claiming this is good design.
We are asking *can a component inject its parent via the parent's base class*?
我们没有说这是一个好的设计。我们在问*一个组件是否能通过它父级的基类来注入它的父级呢*
我们没有说这是一个好的设计。我们在问*一个组件是否能通过它父级的基类来注入它的父级呢*
The sample's `CraigComponent` explores this question. [Looking back](#alex)
we see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
`CraigComponent`例子探究了这个问题。[往回看]{#alex},我们看到`Alex`组件从一个叫`Base`的类*延伸**衍生*)。
`CraigComponent`例子探究了这个问题。[往回看Alex]{#alex},我们看到`Alex`组件*延伸**衍生*)一个叫`Base`的类。
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-class-signature','parent-finder.component.ts (Alex class signature)')(format='.')
:marked
The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded.
`CraigComponent`试图注入`Base`到它的`alex`构造函数参数,并报告是否成功。
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','craig','parent-finder.component.ts (CraigComponent)')(format='.')
:marked
Unfortunately, this does not work.
@ -1209,11 +1259,12 @@ a(id='alex')
confirms that the `alex` parameter is null.
*We cannot inject a parent by its base class.*
喜,这样不行。[在线例子](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)确认了`alex`参数是null。
*我们不能通过基类注入父级*
这样不行。[在线例子](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)确认了`alex`参数是null。
*我们不能通过基类注入父级*
<a id="class-interface-parent"></a>
### Find a parent by its class-interface
### 通过类-接口找到一个父级
We can find a parent component with a [class-interface](#class-interface).
@ -1235,21 +1286,25 @@ a(id='alex')
我们编写一个[*别名供应商*](#useexisting) &mdash一个拥有`useExisting`定义的`provide`函数 &mdash;
它新建一个*可选的*方法来注入一样的组件实例,并添加这个供应商到`AlexComponent`的`@Component`元数据里的`providers`数组。
a(id="alex-providers")
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.')
:marked
[Parent](#parent-token) is the provider's *class-interface* token.
The [*forwardRef*](#forwardref) breaks the circular reference we just created by having the `AlexComponent` refer to itself.
[父级](#parent-token)是该供应商的*类-接口*令牌。我们刚通过在`AlexComponent`引用了自己的建立了一个引用循环, 这个[*forwardRef*](#forwardRef)打破了该循环。
[Parent](#parent-token)是该供应商的*类-接口*令牌。`AlexComponent`引用了自身,造成引用循环,我们使用[*forwardRef*](#forwardRef)打破了该循环。
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before:
*Carol*,该*Alex*子级组件的第三个组件,注入了父级到自己的`parent`参数,和我们之前做的一样:
*Carol**Alex*的第三个子级组件,把父级注入到自己的`parent`参数,和我们之前做的一样:
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','carol-class','parent-finder.component.ts (CarolComponent class)')(format='.')
:marked
Here's *Alex* and family in action:
下面是*Alex*和其家庭的运行结果:
figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/alex.png" alt="Alex in action")
@ -1262,16 +1317,18 @@ a(id="parent-tree")
Both *Alice* and *Barry* implement the `Parent` *class-interface*.
想象一下一个组件层次的一个分支为:*Alice* -> *Barry* -> *Carol*。
*Alice*和*Barry*两个都实现`父级`*类-接口*。
*Alice*和*Barry*两个都实现`Parent`*类-接口*。
*Barry* is the problem. He needs to reach his parent, *Alice*, and also be a parent to *Carol*.
That means he must both *inject* the `Parent` *class-interface* to get *Alice* and
*provide* a `Parent` to satisfy *Carol*.
*Barry*是一个问题。他需要访问它的父级*Alice*,同时他也是*Carol*的父级。这个意味着它必须同时*注入*`父级`*类-接口*来得到*Alice*,和*提供*一个`父级`来满足*Carol*。
*Barry*是一个问题。他需要访问它的父级*Alice*,同时它也是*Carol*的父级。这个意味着它必须同时*注入*`Parent`*类-接口*来得到*Alice*,和*提供*一个`Parent`来满足*Carol*。
Here's *Barry*:
下面是*Barry*
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','barry','parent-finder.component.ts (BarryComponent)')(format='.')
:marked
*Barry*'s `providers` array looks just like [*Alex*'s](#alex-providers).
@ -1281,7 +1338,9 @@ a(id="parent-tree")
如果我们一直要像这样编写[*别名供应商*](#useexisting)的话,我们应该建立一个[帮助函数](#provideparent)。
For now, focus on *Barry*'s constructor:
眼下,集中主意*Barry*的构造函数:
眼下,请集中主意*Barry*的构造函数:
+makeTabs(
'cb-dependency-injection/ts/app/parent-finder.component.ts, cb-dependency-injection/ts/app/parent-finder.component.ts',
'barry-ctor, carol-ctor',
@ -1311,7 +1370,7 @@ a(id="parent-tree")
Here's *Alice*, *Barry* and family in action:
这里是*Alice**Barry*和该家庭的实际代码:
这里是*Alice**Barry*和该家庭的代码:
figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/alice.png" alt="Alice in action")
@ -1319,75 +1378,86 @@ figure.image-display
a(id="parent-token")
:marked
### The *Parent* class-interface
### *父级*类-接口
### *Parent*类-接口
We [learned earlier](#class-interface) that a *class-interface* is an abstract class used as an interface rather than as a base class.
我们[之前学到](#class-interface)一个*类-接口*是一个抽象类,被当做一个接口来使用,而非被当做基类使用。
我们[之前学到](#class-interface)*类-接口*是一个抽象类,被当做一个接口来使用,而非被当做基类使用。
Our example defines a `Parent` *class-interface* .
我们的例子定义了一个`Parent`*类-接口*。
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','parent','parent-finder.component.ts (Parent class-interface)')(format='.')
:marked
The `Parent` *class-interface* defines a `name` property with a type declaration but *no implementation*.,
The `name` property is the only member of a parent component that a child component can call.
Such a narrowing interface helps decouple the child component class from its parent components.
该`Parent` *类-接口*定义了一个`Name`属性,它有一个类型声明,但是*没有实现*,该`name`是该父级组件的子级组件们唯一能调用的属性。
该`Parent` *类-接口*定义了`Name`属性,它有类型声明,但是*没有实施*,该`name`是该父级的所有子级组件们唯一能调用的属性。
这样一个窄的接口帮助分离子级类和它的父级组件。
A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does:
一个能当做父级的组件*应该*实现*类-接口*,和下面的`AliceComponent`的做法一样:
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alice-class-signature','parent-finder.component.ts (AliceComponent class signature)')(format='.')
:marked
Doing so adds clarity to the code. But it's not technically necessary.
Although the `AlexComponent` has a `name` property (as required by its `Base` class)
its class signature doesn't mention `Parent`:
这样做增加了代码的清晰度。但是技术并不一定需要它。虽然`AlexComponent`有一个`name`属性(被他的`基本`类要求),它的类签名并不提及`Parent`。
这样做增加了代码的清晰度。但是在技术上,我们并不一定需要它。虽然`AlexComponent`有一个`name`属性(被它的`基础`类要求),它的类签名并不提及`Parent`。
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-class-signature','parent-finder.component.ts (AlexComponent class signature)')(format='.')
.l-sub-section
:marked
The `AlexComponent` *should* implement `Parent` as a matter of proper style.
It doesn't in this example *only* to demonstrate that the code will compile and run without the interface
为了正确的代码风格,该`AlexComponent`*应该*实现`Parent`。在这个例子里面它不这样,仅仅是为了演示在没有该接口的情况下,该代码会被编译并行。
为了正确的代码风格,该`AlexComponent`*应该*实现`Parent`。在这个例子里面它不这样,仅仅是为了演示在没有该接口的情况下,该代码会被编译并行。
a(id="provideparent")
:marked
### A *provideParent* helper function
### 一个*privdeParent*帮助函数
### *provideParent*助手函数
Writing variations of the same parent *alias provider* gets old quickly,
especially this awful mouthful with a [*forwardRef*](#forwardref):
编写同一个父级的各种*别名供应商*很快就变得很啰嗦,在用[*forwardRef](#forwardRef)的时候尤其绕口:
编写同一个父级的各种*别名供应商*很快就会变得很啰嗦,在用[*forwardRef](#forwardRef)的时候尤其绕口:
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-providers')(format='.')
:marked
We can extract that logic into a helper function like this:
我们可以像这样把这个逻辑提取到一个帮助函数里面:
我们可以像这样把这个逻辑提取到一个助手函数里面:
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','provide-the-parent')(format='.')
:marked
Now we can add a simpler, more meaningful parent provider to our components:
现在我们就可以为我们的组件添加一个简单点的,更加直观的父级供应商:
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alice-providers')(format='.')
:marked
We can do better. The current version of the helper function can only alias the `Parent` *class-interface*.
Our application might have a variety of parent types, each with its own *class-interface* token.
我们可以做的更好。当前版本的帮助函数只能为`父级`*类-接口*提供别名。我们的应用程序可能有很多类型的父级,每个父级有自己的*类-接口*令牌。
我们可以做的更好。当前版本的助手函数只能为`Parent`*类-接口*提供别名。我们的应用程序可能有很多类型的父级,每个父级有自己的*类-接口*令牌。
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*.
这里是一个修改版本,默认接受一个`父级`,但是同时接受一个可选的第二个参数,可以用来指定一个不同的父级*类-接口*。
这里是一个修改版本,默认接受一个`Parent`,但是同时接受一个可选的第二个参数,可以用来指定一个不同的父级*类-接口*。
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','provide-parent')(format='.')
:marked
And here's how we could use it with a different parent type:
下面是我们如何使用它添加一个不同类型的父级:
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','beth-providers')(format='.')
:marked
@ -1395,7 +1465,8 @@ a(id="forwardref")
.l-main-section
:marked
## Break circularities with a forward class reference (*forwardRef*)
## 使用一个转寄类引用(*forwardRef*)来来打破循环
## 使用一个转寄类引用(*forwardRef*)来打破循环
The order of class declaration matters in TypeScript.
We can't refer directly to a class until it's been defined.
@ -1407,8 +1478,8 @@ a(id="forwardref")
We're in a bind when class 'A refers to class 'B' and 'B' refers to 'A'.
One of them has to be defined first.
它通常不是一个问题,特别是当我们遵循*一个类一个文件*规则的时候。
但是有时候循环引用可能不能避免。当一个类*A引用类B*,同时'B'引用'A'的时候,我们就遇到困境了:它们中间的一个必须要先定义。
它通常不是一个问题,特别是当我们遵循*一个文件一个类*规则的时候。
但是有时候循环引用可能不能避免。当一个类*A引用类B*,同时'B'引用'A'的时候,我们就遇到困境了:它们中间的一个必须要先定义。
The Angular `forwardRef` function creates an *indirect* reference that Angular can resolve later.
@ -1416,7 +1487,7 @@ a(id="forwardref")
The *Parent Finder* sample is full of circular class references that are impossible to break.
这个*父级寻找器*例子是到处都是类循环引用,我们无法打破
这个*父级寻找器*例子里,到处都是我们无法打破的类循环引用
In the [*Alex/Cathy* example](#known-parent) above:
@ -1425,7 +1496,7 @@ a(id="forwardref")
* the `AlexComponent` lists the `CathyComponent` in its component metadata `directives` array
so it can display *Cathy* in its template.
* `AlexComponent`在它的组件元数据`指令`数值里面列出`CathyComponent`,这样它可以在自己的模板中显示*Cathy*。
* `AlexComponent`在它的组件元数据`Directives`数值里面列出`CathyComponent`,这样它可以在自己的模板中显示*Cathy*。
* the `CathyComponent` constructor injects the parent `AlexComponent` which means that the `alex` parameter
of its constructor has the `AlexComponent` type.
@ -1434,11 +1505,12 @@ a(id="forwardref")
*Alex* refers to *Cathy* and *Cathy* refers to *Alex*. We're stuck. We must define one of them first.
*Alex* 引用了*Cathy*,同时,*Cathy*引用了*Alex*。我们被卡住了。我们必须要先它们中的一个。
*Alex* 引用了*Cathy*,同时,*Cathy*引用了*Alex*。我们被卡住了。我们必须要先定义它们中的一个。
We defined *Alex* first and built its `C_DIRECTIVES` array with a forward reference to *Cathy*:
我们先定义了*Alex*,使用一个*Cathy*的转寄引用,建筑了它的`C_DIRECTIVES`数值:
我们先定义了*Alex*,然后使用*Cathy*的转寄引用来创建`C_DIRECTIVES`数列:
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','C_DIRECTIVES','parent-finder.component.ts (C_DIRECTIVES)')(format='.')
:marked
.l-sub-section
@ -1446,12 +1518,14 @@ a(id="forwardref")
Defining *Alex* and *Cathy* in separate files won't help.
*Alex* would have to import *Cathy* and *Cathy* would have to import *Alex*.
在单独的文件里面定义*Alex*和*Cathy*并不能帮忙。*Alex*必须要导入*Cathy**Cathy*必须要导入*Alex*。
在单独的文件里面定义*Alex*和*Cathy*没有作用。*Alex*必须要导入*Cathy**Cathy*必须要导入*Alex*。
We *had* to define *Alex* first because,
while we can add `forwardRef(CathyComponent)` to *Alex*'s `directives` array,
we can't write `public alex: forwardRef(AlexComponent))` in *Cathy*'s constructor.
我们*被迫*先定义*Alex*,因为我们可以添加`forwardRef(CathyComponent)`到*Alex*的`指令`数组里面,但是我们不能在*Cathy*的构造函数里面使用`public alex: forwardRef(AlexComponent))`。
我们*被迫*先定义*Alex*,因为我们可以添加`forwardRef(CathyComponent)`到*Alex*的`Directives`数组里面,但是我们不能在*Cathy*的构造函数里面使用`public alex: forwardRef(AlexComponent))`。
:marked
We face a similar dilemma when a class makes *a reference to itself*
as does the `AlexComponent` in its `providers` array.
@ -1464,5 +1538,6 @@ a(id="forwardref")
Again we break the circularity with `forwardRef`:
我们又一次使用`forwardRef`来打破该循环:
+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.')
:marked