121 lines
4.9 KiB
Plaintext
121 lines
4.9 KiB
Plaintext
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.
|
|
|
|
<a id="toc"></a>
|
|
:marked
|
|
## Table of contents
|
|
|
|
[Dynamic Component Loading](#dynamic-loading)
|
|
|
|
[Where to load the component](#where-to-load)
|
|
|
|
[Loading components](#loading-components)
|
|
|
|
.l-main-section
|
|
<a id="dynamic-loading"></a>
|
|
:marked
|
|
## 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.
|
|
|
|
|
|
.l-main-section
|
|
<a id="where-to-load"></a>
|
|
:marked
|
|
## 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.
|
|
|
|
+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.
|
|
|
|
.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`.
|
|
|
|
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.
|
|
|
|
+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
|
|
|
|
`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.
|
|
|
|
+makeExample('cb-dynamic-component-loader/ts/src/app/app.module.ts', 'entry-components' ,'src/app/app.module.ts (entry components)')(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.
|
|
|
|
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:
|
|
|
|
+makeTabs(
|
|
`cb-dynamic-component-loader/ts/src/app/hero-job-ad.component.ts,
|
|
cb-dynamic-component-loader/ts/src/app/hero-profile.component.ts,
|
|
cb-dynamic-component-loader/ts/src/app/ad.component.ts`,
|
|
null,
|
|
`hero-job-ad.component.ts,
|
|
hero-profile.component.ts,
|
|
ad.component.ts`
|
|
)
|
|
|
|
:marked
|
|
The final ad banner looks like this:
|
|
figure.image-display
|
|
img(src="/resources/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads") |