2017-05-08 23:47:34 -04:00
|
|
|
|
# Dynamic Component Loader
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
2017-07-29 12:03:22 -04:00
|
|
|
|
# 动态组件加载器
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
2017-02-22 13:09:39 -05:00
|
|
|
|
Component templates are not always fixed. An application may need to load new components at runtime.
|
|
|
|
|
|
2017-07-22 23:51:25 -04:00
|
|
|
|
组件的模板不会永远是固定的。应用可能会需要在运行期间加载一些新的组件。
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
This cookbook shows you how to use `ComponentFactoryResolver` to add components dynamically.
|
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
这本烹饪书为你展示如何使用 `ComponentFactoryResolver` 来动态添加组件。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-04-21 20:21:45 -04:00
|
|
|
|
See the <live-example name="dynamic-component-loader"></live-example>
|
2017-03-31 19:57:13 -04:00
|
|
|
|
of the code in this cookbook.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-03 08:06:01 -05:00
|
|
|
|
到<live-example name="dynamic-component-loader"></live-example>查看本烹饪书的源码。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
{@a dynamic-loading}
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
## Dynamic component loading
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-07-22 23:51:25 -04:00
|
|
|
|
## 动态组件加载
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
The following example shows how to build a dynamic ad banner.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-07-22 23:51:25 -04:00
|
|
|
|
下面的例子展示了如何构建动态广告条。
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
The hero agency is planning an ad campaign with several different
|
|
|
|
|
ads cycling through the banner. New ad components are added
|
|
|
|
|
frequently by several different teams. This makes it impractical
|
|
|
|
|
to use a template with a static component structure.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-07-22 23:51:25 -04:00
|
|
|
|
英雄管理局正在计划一个广告活动,要在广告条中显示一系列不同的广告。几个不同的小组可能会频繁加入新的广告组件。
|
|
|
|
|
再用只支持静态组件结构的模板显然是不现实的。
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
Instead, you need a way to load a new component without a fixed
|
|
|
|
|
reference to the component in the ad banner's template.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-22 05:18:48 -04:00
|
|
|
|
你需要一种新的组件加载方式,它不需要在广告条组件的模板中引用固定的组件。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
Angular comes with its own API for loading components dynamically.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
Angular 自带的 API 就能支持动态加载组件。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
{@a directive}
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-05-08 23:47:34 -04:00
|
|
|
|
## The anchor directive
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-07-22 23:51:25 -04:00
|
|
|
|
## 指令
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
Before you can add components you have to define an anchor point
|
|
|
|
|
to tell Angular where to insert components.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
在添加组件之前,先要定义一个锚点来告诉 Angular 要把组件插入到什么地方。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
The ad banner uses a helper directive called `AdDirective` to
|
|
|
|
|
mark valid insertion points in the template.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
广告条使用一个名叫 `AdDirective` 的辅助指令来在模板中标记出有效的插入点。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-04-21 20:21:45 -04:00
|
|
|
|
<code-example path="dynamic-component-loader/src/app/ad.directive.ts" title="src/app/ad.directive.ts" linenums="false">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
`AdDirective` injects `ViewContainerRef` to gain access to the view
|
|
|
|
|
container of the element that will host the dynamically added component.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
`AdDirective` 注入了 `ViewContainerRef` 来获取对容器视图的访问权,这个容器就是那些动态加入的组件的宿主。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
In the `@Directive` decorator, notice the selector name, `ad-host`;
|
|
|
|
|
that's what you use to apply the directive to the element.
|
|
|
|
|
The next section shows you how.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-22 05:18:48 -04:00
|
|
|
|
在 `@Directive` 装饰器中,要注意选择器的名称:`ad-host`,它就是你将应用到元素上的指令。下一节会展示该如何做。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
{@a loading-components}
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
## Loading components
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-07-22 23:51:25 -04:00
|
|
|
|
## 加载组件
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
Most of the ad banner implementation is in `ad-banner.component.ts`.
|
|
|
|
|
To keep things simple in this example, the HTML is in the `@Component`
|
|
|
|
|
decorator's `template` property as a template string.
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
广告条的大部分实现代码都在 `ad-banner.component.ts` 中。
|
2018-03-22 05:18:48 -04:00
|
|
|
|
为了让这个例子简单点,HTML 被直接放在了 `@Component` 装饰器的 `template` 属性中。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
The `<ng-template>` element is where you apply the directive you just made.
|
|
|
|
|
To apply the `AdDirective`, recall the selector from `ad.directive.ts`,
|
|
|
|
|
`ad-host`. Apply that to `<ng-template>` without the square brackets. Now Angular knows
|
|
|
|
|
where to dynamically load components.
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
`<ng-template>` 元素就是刚才制作的指令将应用到的地方。
|
|
|
|
|
要应用 `AdDirective`,回忆一下来自 `ad.directive.ts` 的选择器 `ad-host`。把它应用到 `<ng-template>`(不用带方括号)。
|
|
|
|
|
这下,Angular 就知道该把组件动态加载到哪里了。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-04-21 20:21:45 -04:00
|
|
|
|
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" title="src/app/ad-banner.component.ts (template)" linenums="false">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
The `<ng-template>` element is a good choice for dynamic components
|
|
|
|
|
because it doesn't render any additional output.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
`<ng-template>` 元素是动态加载组件的最佳选择,因为它不会渲染任何额外的输出。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
{@a resolving-components}
|
|
|
|
|
|
2017-05-08 23:47:34 -04:00
|
|
|
|
## Resolving components
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-08-06 01:21:34 -04:00
|
|
|
|
## 解析组件
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
Take a closer look at the methods in `ad-banner.component.ts`.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
深入看看 `ad-banner.component.ts` 中的方法。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
`AdBannerComponent` takes an array of `AdItem` objects as input,
|
2017-04-26 08:11:02 -04:00
|
|
|
|
which ultimately comes from `AdService`. `AdItem` objects specify
|
|
|
|
|
the type of component to load and any data to bind to the
|
2017-03-31 19:57:13 -04:00
|
|
|
|
component.`AdService` returns the actual ads making up the ad campaign.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
`AdBannerComponent` 接收一个 `AdItem` 对象的数组作为输入,它最终来自 `AdService`。
|
|
|
|
|
`AdItem` 对象指定要加载的组件类,以及绑定到该组件上的任意数据。
|
|
|
|
|
`AdService` 可以返回广告活动中的那些广告。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
Passing an array of components to `AdBannerComponent` allows for a
|
|
|
|
|
dynamic list of ads without static elements in the template.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-22 05:18:48 -04:00
|
|
|
|
给 `AdBannerComponent` 传入一个组件数组可以在模板中放入一个广告的动态列表,而不用写死在模板中。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
With its `getAds()` method, `AdBannerComponent` cycles through the array of `AdItems`
|
|
|
|
|
and loads a new component every 3 seconds by calling `loadComponent()`.
|
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
通过 `getAds()` 方法,`AdBannerComponent` 可以循环遍历 `AdItems` 的数组,并且每三秒调用一次 `loadComponent()` 来加载新组件。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-04-21 20:21:45 -04:00
|
|
|
|
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="class" title="src/app/ad-banner.component.ts (excerpt)" linenums="false">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
The `loadComponent()` method is doing a lot of the heavy lifting here.
|
|
|
|
|
Take it step by step. First, it picks an ad.
|
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
这里的 `loadComponent()` 方法很重要。
|
2018-03-22 05:18:48 -04:00
|
|
|
|
来一步步看看。首先,它选取了一个广告。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
<div class="l-sub-section">
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
**How _loadComponent()_ chooses an ad**
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
**`loadComponent()` 如何选择广告**
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
The `loadComponent()` method chooses an ad using some math.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
`loadComponent()` 方法使用某种算法选择了一个广告。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
First, it sets the `currentAddIndex` by taking whatever it
|
|
|
|
|
currently is plus one, dividing that by the length of the `AdItem` array, and
|
|
|
|
|
using the _remainder_ as the new `currentAddIndex` value. Then, it uses that
|
|
|
|
|
value to select an `adItem` from the array.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
(译注:循环选取算法)首先,它把 `currentAddIndex` 递增一,然后用它除以 `AdItem` 数组长度的*余数*作为新的 `currentAddIndex` 的值,
|
|
|
|
|
最后用这个值来从数组中选取一个 `adItem`。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-26 08:11:02 -04:00
|
|
|
|
After `loadComponent()` selects an ad, it uses `ComponentFactoryResolver`
|
|
|
|
|
to resolve a `ComponentFactory` for each specific component.
|
2017-03-31 19:57:13 -04:00
|
|
|
|
The `ComponentFactory` then creates an instance of each component.
|
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
在 `loadComponent()` 选取了一个广告之后,它使用 `ComponentFactoryResolver` 来为每个具体的组件解析出一个 `ComponentFactory`。
|
|
|
|
|
然后 `ComponentFactory` 会为每一个组件创建一个实例。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-04-26 08:11:02 -04:00
|
|
|
|
Next, you're targeting the `viewContainerRef` that
|
|
|
|
|
exists on this specific instance of the component. How do you know it's
|
|
|
|
|
this specific instance? Because it's referring to `adHost` and `adHost` is the
|
2017-03-31 19:57:13 -04:00
|
|
|
|
directive you set up earlier to tell Angular where to insert dynamic components.
|
|
|
|
|
|
2018-03-22 05:18:48 -04:00
|
|
|
|
接下来,你要把 `viewContainerRef` 指向这个组件的现有实例。但我们怎么才能找到这个实例呢?
|
|
|
|
|
很简单,因为它指向了 `adHost`,而这个 `adHost` 就是你以前设置过的指令,用来告诉 Angular 该把动态组件插入到什么位置。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-04-26 08:11:02 -04:00
|
|
|
|
As you may recall, `AdDirective` injects `ViewContainerRef` into its constructor.
|
|
|
|
|
This is how the directive accesses the element that you want to use to host the dynamic component.
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
回忆一下,`AdDirective` 曾在它的构造函数中注入了一个 `ViewContainerRef`。
|
2018-03-22 05:18:48 -04:00
|
|
|
|
因此这个指令可以访问到这个你打算用作动态组件宿主的元素。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
To add the component to the template, you call `createComponent()` on `ViewContainerRef`.
|
|
|
|
|
|
2018-03-22 05:18:48 -04:00
|
|
|
|
要把这个组件添加到模板中,你可以调用 `ViewContainerRef` 的 `createComponent()`。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
The `createComponent()` method returns a reference to the loaded component.
|
|
|
|
|
Use that reference to interact with the component by assigning to its properties or calling its methods.
|
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
`createComponent()` 方法返回一个引用,指向这个刚刚加载的组件。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
使用这个引用就可以与该组件进行交互,比如设置它的属性或调用它的方法。
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
{@a selector-references}
|
|
|
|
|
|
|
|
|
|
#### Selector references
|
|
|
|
|
|
2017-07-22 23:51:25 -04:00
|
|
|
|
#### 对选择器的引用
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
Generally, the Angular compiler generates a `ComponentFactory`
|
|
|
|
|
for any component referenced in a template. However, there are
|
|
|
|
|
no selector references in the templates for
|
|
|
|
|
dynamically loaded components since they load at runtime.
|
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
通常,Angular 编译器会为模板中所引用的每个组件都生成一个 `ComponentFactory` 类。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
但是,对于动态加载的组件,模板中不会出现对它们的选择器的引用。
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
To ensure that the compiler still generates a factory,
|
|
|
|
|
add dynamically loaded components to the `NgModule`'s `entryComponents` array:
|
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
要想确保编译器照常生成工厂类,就要把这些动态加载的组件添加到 `NgModule` 的 `entryComponents` 数组中:
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-04-21 20:21:45 -04:00
|
|
|
|
<code-example path="dynamic-component-loader/src/app/app.module.ts" region="entry-components" title="src/app/app.module.ts (entry components)" linenums="false">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
{@a common-interface}
|
|
|
|
|
|
2017-05-08 23:47:34 -04:00
|
|
|
|
## The _AdComponent_ interface
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
## 公共的 `AdComponent` 接口
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
In the ad banner, all components implement a common `AdComponent` interface to
|
|
|
|
|
standardize the API for passing data to the components.
|
|
|
|
|
|
2018-03-22 05:18:48 -04:00
|
|
|
|
在广告条中,所有组件都实现了一个公共接口 `AdComponent`,它定义了一个标准化的 API,来把数据传给组件。
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
Here are two sample components and the `AdComponent` interface for reference:
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2018-03-20 05:09:18 -04:00
|
|
|
|
下面就是两个范例组件及其 `AdComponent` 接口:
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
<code-tabs>
|
|
|
|
|
|
2017-04-21 20:21:45 -04:00
|
|
|
|
<code-pane title="hero-job-ad.component.ts" path="dynamic-component-loader/src/app/hero-job-ad.component.ts">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-pane>
|
|
|
|
|
|
2017-04-21 20:21:45 -04:00
|
|
|
|
<code-pane title="hero-profile.component.ts" path="dynamic-component-loader/src/app/hero-profile.component.ts">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-pane>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-21 20:21:45 -04:00
|
|
|
|
<code-pane title="ad.component.ts" path="dynamic-component-loader/src/app/ad.component.ts">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-pane>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-tabs>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
{@a final-ad-baner}
|
|
|
|
|
|
2017-05-08 23:47:34 -04:00
|
|
|
|
## Final ad banner
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2017-08-06 01:21:34 -04:00
|
|
|
|
## 最终的广告栏
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2018-03-03 08:06:01 -05:00
|
|
|
|
The final ad banner looks like this:
|
2017-07-22 23:51:25 -04:00
|
|
|
|
|
2018-03-03 08:06:01 -05:00
|
|
|
|
最终的广告栏是这样的:
|
2017-03-30 15:04:18 -04:00
|
|
|
|
|
2017-05-09 18:53:32 -04:00
|
|
|
|
<figure>
|
2018-03-03 08:06:01 -05:00
|
|
|
|
<img src="generated/images/guide/dynamic-component-loader/ads.gif" alt="Ads">
|
|
|
|
|
</figure>
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
2017-08-06 01:21:34 -04:00
|
|
|
|
See the <live-example name="dynamic-component-loader"></live-example>.
|
|
|
|
|
|
2018-03-12 01:04:24 -04:00
|
|
|
|
参见<live-example name="dynamic-component-loader"></live-example>。
|