From 4f8714517064c9aaa1ad4fe25ca9a4a2eba550f0 Mon Sep 17 00:00:00 2001 From: Kapunahele Wong Date: Sat, 1 Apr 2017 14:31:05 -0400 Subject: [PATCH] docs(dyn-comp-loader): add explanations for methods, add doc structure, plunker (#3451) --- .../cb-dynamic-component-loader/ts/plnkr.json | 9 + .../ts/src/app/ad-banner.component.ts | 6 +- .../cookbook/dynamic-component-loader.jade | 176 ++++++++++++------ 3 files changed, 129 insertions(+), 62 deletions(-) create mode 100644 public/docs/_examples/cb-dynamic-component-loader/ts/plnkr.json diff --git a/public/docs/_examples/cb-dynamic-component-loader/ts/plnkr.json b/public/docs/_examples/cb-dynamic-component-loader/ts/plnkr.json new file mode 100644 index 0000000000..c43fb15c12 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-component-loader/ts/plnkr.json @@ -0,0 +1,9 @@ +{ + "description": "Dynamic Component Loader", + "basePath": "src/", + "files":[ + "!**/*.d.ts", + "!**/*.js" + ], + "tags":["cookbook component"] +} diff --git a/public/docs/_examples/cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts b/public/docs/_examples/cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts index 293d31dea9..8489fce5b2 100644 --- a/public/docs/_examples/cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts +++ b/public/docs/_examples/cb-dynamic-component-loader/ts/src/app/ad-banner.component.ts @@ -11,11 +11,12 @@ import { AdComponent } from './ad.component'; template: `

Advertisements

- +
` - // #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 diff --git a/public/docs/ts/latest/cookbook/dynamic-component-loader.jade b/public/docs/ts/latest/cookbook/dynamic-component-loader.jade index feace0d526..583e8e1e26 100644 --- a/public/docs/ts/latest/cookbook/dynamic-component-loader.jade +++ b/public/docs/ts/latest/cookbook/dynamic-component-loader.jade @@ -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. :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 + of the code in this cookbook. .l-main-section :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 - + :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 :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 `` 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 `` 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 `` 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") \ No newline at end of file + img(src="/resources/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads") + +:marked + See the .