docs(dyn-comp-loader): add explanations for methods, add doc structure, plunker (#3451)
This commit is contained in:
parent
cd0c36340e
commit
4f87145170
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"description": "Dynamic Component Loader",
|
||||
"basePath": "src/",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
],
|
||||
"tags":["cookbook component"]
|
||||
}
|
|
@ -11,11 +11,12 @@ import { AdComponent } from './ad.component';
|
|||
template: `
|
||||
<div class="ad-banner">
|
||||
<h3>Advertisements</h3>
|
||||
<template ad-host></template>
|
||||
<ng-template ad-host></ng-template>
|
||||
</div>
|
||||
`
|
||||
// #enddocregion ad-host
|
||||
// #enddocregion ad-host
|
||||
})
|
||||
// #docregion class
|
||||
export class AdBannerComponent implements AfterViewInit, OnDestroy {
|
||||
@Input() ads: AdItem[];
|
||||
currentAddIndex: number = -1;
|
||||
|
@ -53,3 +54,4 @@ export class AdBannerComponent implements AfterViewInit, OnDestroy {
|
|||
}, 3000);
|
||||
}
|
||||
}
|
||||
// #enddocregion class
|
||||
|
|
|
@ -3,107 +3,158 @@ include ../_util-fns
|
|||
:marked
|
||||
Component templates are not always fixed. An application may need to load new components at runtime.
|
||||
|
||||
In this cookbook we show how to use `ComponentFactoryResolver` to add components dynamically.
|
||||
This cookbook shows you how to use `ComponentFactoryResolver` to add components dynamically.
|
||||
|
||||
<a id="toc"></a>
|
||||
:marked
|
||||
## Table of contents
|
||||
# Contents
|
||||
|
||||
[Dynamic Component Loading](#dynamic-loading)
|
||||
* [Dynamic component loading](#dynamic-loading)
|
||||
* [The directive](#directive)
|
||||
* [Loading components](#loading-components)
|
||||
|
||||
[Where to load the component](#where-to-load)
|
||||
* [Resolving Components](#resolving-components)
|
||||
* [Selector References](#selector-references)
|
||||
|
||||
[Loading components](#loading-components)
|
||||
* [A common _AdComponent_ interface](#common-interface)
|
||||
* [Final ad banner](#final-ad-banner)
|
||||
|
||||
:marked
|
||||
See the <live-example name="cb-dynamic-component-loader"></live-example>
|
||||
of the code in this cookbook.
|
||||
|
||||
.l-main-section
|
||||
<a id="dynamic-loading"></a>
|
||||
:marked
|
||||
## Dynamic Component Loading
|
||||
## Dynamic component loading
|
||||
|
||||
The following example shows how to build a dynamic ad banner.
|
||||
|
||||
The hero agency is planning an ad campaign with several different ads cycling through the banner.
|
||||
The following example shows how to build a dynamic ad banner.
|
||||
|
||||
New ad components are added frequently by several different teams. This makes it impractical to use a template with a static component structure.
|
||||
|
||||
Instead we need a way to load a new component without a fixed reference to the component in the ad banner's template.
|
||||
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.
|
||||
|
||||
Angular comes with its own API for loading components dynamically. In the following sections you will learn how to use it.
|
||||
Instead, you need a way to load a new component without a fixed
|
||||
reference to the component in the ad banner's template.
|
||||
|
||||
Angular comes with its own API for loading components dynamically.
|
||||
|
||||
|
||||
.l-main-section
|
||||
<a id="where-to-load"></a>
|
||||
<a id="directive"></a>
|
||||
:marked
|
||||
## Where to load the component
|
||||
## The directive
|
||||
|
||||
Before components can be added we have to define an anchor point to mark where components can be inserted dynamically.
|
||||
Before you can add components you have to define an anchor point
|
||||
to tell Angular where to insert components.
|
||||
|
||||
The ad banner uses a helper directive called `AdDirective` to mark valid insertion points in the template.
|
||||
The ad banner uses a helper directive called `AdDirective` to
|
||||
mark valid insertion points in the template.
|
||||
|
||||
+makeExample('cb-dynamic-component-loader/ts/src/app/ad.directive.ts',null,'src/app/ad.directive.ts')(format='.')
|
||||
|
||||
:marked
|
||||
`AdDirective` injects `ViewContainerRef` to gain access to the view container of the element that will become the host of the dynamically added component.
|
||||
|
||||
`AdDirective` injects `ViewContainerRef` to gain access to the view
|
||||
container of the element that will host the dynamically added component.
|
||||
|
||||
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.
|
||||
|
||||
.l-main-section
|
||||
<a id="loading-components"></a>
|
||||
:marked
|
||||
## Loading components
|
||||
|
||||
The next step is to implement the ad banner. Most of the implementation is in `AdBannerComponent`.
|
||||
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.
|
||||
|
||||
We start by adding a `template` element with the `AdDirective` directive applied.
|
||||
|
||||
+makeTabs(
|
||||
`cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts,
|
||||
cb-dynamic-component-loader/ts/src/app/ad.service.ts,
|
||||
cb-dynamic-component-loader/ts/src/app/ad-item.ts,
|
||||
cb-dynamic-component-loader/ts/src/app/app.module.ts,
|
||||
cb-dynamic-component-loader/ts/src/app/app.component.ts`,
|
||||
null,
|
||||
`ad-banner.component.ts,
|
||||
ad.service.ts,
|
||||
ad-item.ts,
|
||||
app.module.ts,
|
||||
app.component`
|
||||
)
|
||||
|
||||
:marked
|
||||
The `template` element decorated with the `ad-host` directive marks where dynamically loaded components will be added.
|
||||
|
||||
Using a `template` element is recommended since it doesn't render any additional output.
|
||||
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.
|
||||
|
||||
+makeExample('cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts', 'ad-host' ,'src/app/ad-banner.component.ts (template)')(format='.')
|
||||
|
||||
:marked
|
||||
### Resolving Components
|
||||
The `<ng-template>` element is a good choice for dynamic components
|
||||
because it doesn't render any additional output.
|
||||
|
||||
`AdBanner` takes an array of `AdItem` objects as input. `AdItem` objects specify the type of component to load and any data to bind to the component.
|
||||
a#resolving-components
|
||||
:marked
|
||||
### Resolving components
|
||||
|
||||
The ad components making up the ad campaign are returned from `AdService`.
|
||||
|
||||
Passing an array of components to `AdBannerComponent` allows for a dynamic list of ads without static elements in the template.
|
||||
Take a closer look at the methods in `ad-banner.component.ts`.
|
||||
|
||||
`AdBannerComponent` cycles through the array of `AdItems` and loads the corresponding components on an interval. Every 3 seconds a new component is loaded.
|
||||
`AdBannerComponent` takes an array of `AdItem` objects as input,
|
||||
which ultimately comes from `AdService`. `AdItem` objects specify
|
||||
the type of component to load and any data to bind to the
|
||||
component.`AdService` returns the actual ads making up the ad campaign.
|
||||
|
||||
`ComponentFactoryResolver` is used to resolve a `ComponentFactory` for each specific component. The component factory is need to create an instance of the component.
|
||||
Passing an array of components to `AdBannerComponent` allows for a
|
||||
dynamic list of ads without static elements in the template.
|
||||
|
||||
`ComponentFactories` are generated by the Angular compiler.
|
||||
|
||||
Generally the compiler will generate a component factory for any component referenced in a template.
|
||||
|
||||
With dynamically loaded components there are no selector references in the templates since components are loaded at runtime. In order to ensure that the compiler will still generate a factory, dynamically loaded components have to be added to their `NgModule`'s `entryComponents` array.
|
||||
With its `getAds()` method, `AdBannerComponent` cycles through the array of `AdItems`
|
||||
and loads a new component every 3 seconds by calling `loadComponent()`.
|
||||
|
||||
+makeExample('cb-dynamic-component-loader/ts/src/app/app.module.ts', 'entry-components' ,'src/app/app.module.ts (entry components)')(format='.')
|
||||
+makeExample('cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts', 'class' ,'src/app/ad-banner.component.ts (excerpt)')(format='.')
|
||||
|
||||
:marked
|
||||
Components are added to the template by calling `createComponent` on the `ViewContainerRef` reference.
|
||||
|
||||
`createComponent` returns a reference to the loaded component. The component reference can be used to pass input data or call methods to interact with the component.
|
||||
The `loadComponent()` method is doing a lot of the heavy lifting here.
|
||||
Take it step by step. First, it picks an ad.
|
||||
|
||||
In the Ad banner, all components implement a common `AdComponent` interface to standardize the api for passing data to the components.
|
||||
.l-sub-section
|
||||
:marked
|
||||
**How _loadComponent()_ chooses an ad**
|
||||
|
||||
Two sample components and the `AdComponent` interface are shown below:
|
||||
The `loadComponent()` method chooses an ad using some math.
|
||||
|
||||
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.
|
||||
|
||||
:marked
|
||||
After `loadComponent()` selects an ad, it uses `ComponentFactoryResolver`
|
||||
to resolve a `ComponentFactory` for each specific component.
|
||||
The `ComponentFactory` then creates an instance of each component.
|
||||
|
||||
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
|
||||
directive you set up earlier to tell Angular where to insert dynamic components.
|
||||
|
||||
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.
|
||||
|
||||
To add the component to the template, you call `createComponent()` on `ViewContainerRef`.
|
||||
|
||||
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.
|
||||
|
||||
a#selector-references
|
||||
:marked
|
||||
#### Selector references
|
||||
|
||||
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.
|
||||
|
||||
To ensure that the compiler still generates a factory,
|
||||
add dynamically loaded components to the `NgModule`'s `entryComponents` array:
|
||||
+makeExample('cb-dynamic-component-loader/ts/src/app/app.module.ts', 'entry-components' ,'src/app/app.module.ts (entry components)')(format='.')
|
||||
|
||||
a#common-interface
|
||||
:marked
|
||||
### A common _AdComponent_ interface
|
||||
|
||||
In the ad banner, all components implement a common `AdComponent` interface to
|
||||
standardize the API for passing data to the components.
|
||||
|
||||
Here are two sample components and the `AdComponent` interface for reference:
|
||||
|
||||
+makeTabs(
|
||||
`cb-dynamic-component-loader/ts/src/app/hero-job-ad.component.ts,
|
||||
|
@ -113,9 +164,14 @@ include ../_util-fns
|
|||
`hero-job-ad.component.ts,
|
||||
hero-profile.component.ts,
|
||||
ad.component.ts`
|
||||
)
|
||||
)
|
||||
|
||||
a#final-ad-baner
|
||||
:marked
|
||||
### Final ad banner
|
||||
The final ad banner looks like this:
|
||||
figure.image-display
|
||||
img(src="/resources/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads")
|
||||
img(src="/resources/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads")
|
||||
|
||||
:marked
|
||||
See the <live-example name="cb-dynamic-component-loader"></live-example>.
|
||||
|
|
Loading…
Reference in New Issue