139 lines
5.0 KiB
Markdown
139 lines
5.0 KiB
Markdown
@title
|
|
Dynamic Component Loader
|
|
|
|
@intro
|
|
Load components dynamically
|
|
|
|
@description
|
|
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.
|
|
|
|
<a id="toc"></a>## Table of contents
|
|
|
|
[Dynamic Component Loading](#dynamic-loading)
|
|
|
|
[Where to load the component](#where-to-load)
|
|
|
|
[Loading components](#loading-components)
|
|
|
|
<a id="dynamic-loading"></a>## 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.
|
|
|
|
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.
|
|
|
|
Angular comes with its own API for loading components dynamically. In the following sections you will learn how to use it.
|
|
|
|
|
|
<a id="where-to-load"></a>## Where to load the component
|
|
|
|
Before components can be added we have to define an anchor point to mark where components can be inserted dynamically.
|
|
|
|
The ad banner uses a helper directive called `AdDirective` to mark valid insertion points in the template.
|
|
|
|
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/ad.directive.ts'}
|
|
|
|
`AdDirective` injects `ViewContainerRef` to gain access to the view container of the element that will become the host of the dynamically added component.
|
|
|
|
<a id="loading-components"></a>## Loading components
|
|
|
|
The next step is to implement the ad banner. Most of the implementation is in `AdBannerComponent`.
|
|
|
|
We start by adding a `template` element with the `AdDirective` directive applied.
|
|
|
|
<md-tab-group>
|
|
|
|
<md-tab label="ad-banner.component.ts">
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="ad.service.ts">
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/ad.service.ts'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="ad-item.ts">
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/ad-item.ts'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="app.module.ts">
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/app.module.ts'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="app.component">
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/app.component.ts'}
|
|
</md-tab>
|
|
|
|
|
|
</md-tab-group>
|
|
|
|
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.
|
|
|
|
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts' region='ad-host'}
|
|
|
|
### Resolving Components
|
|
|
|
`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.
|
|
|
|
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.
|
|
|
|
`AdBannerComponent` cycles through the array of `AdItems` and loads the corresponding components on an interval. Every 3 seconds a new component is loaded.
|
|
|
|
`ComponentFactoryResolver` is used to resolve a `ComponentFactory` for each specific component. The component factory is need to create an instance of the component.
|
|
|
|
`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.
|
|
|
|
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/app.module.ts' region='entry-components'}
|
|
|
|
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.
|
|
|
|
In the Ad banner, all components implement a common `AdComponent` interface to standardize the api for passing data to the components.
|
|
|
|
Two sample components and the `AdComponent` interface are shown below:
|
|
|
|
<md-tab-group>
|
|
|
|
<md-tab label="hero-job-ad.component.ts">
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/hero-job-ad.component.ts'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="hero-profile.component.ts">
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/hero-profile.component.ts'}
|
|
</md-tab>
|
|
|
|
|
|
<md-tab label="ad.component.ts">
|
|
{@example 'cb-dynamic-component-loader/ts/src/app/ad.component.ts'}
|
|
</md-tab>
|
|
|
|
|
|
</md-tab-group>
|
|
|
|
The final ad banner looks like this:
|
|
<figure class='image-display'>
|
|
<img src="assets/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads"> </img>
|
|
</figure>
|
|
|