{ "id": "guide/dynamic-component-loader", "title": "Dynamic component loader", "contents": "\n\n\n
\n mode_edit\n
\n\n\n
\n

Dynamic component loaderlink

\n

Component templates are not always fixed. An application may need to load new components at runtime.

\n

This cookbook shows you how to use ComponentFactoryResolver to add components dynamically.

\n

See the \nof the code in this cookbook.

\n\n

Dynamic component loadinglink

\n

The following example shows how to build a dynamic ad banner.

\n

The hero agency is planning an ad campaign with several different\nads cycling through the banner. New ad components are added\nfrequently by several different teams. This makes it impractical\nto use a template with a static component structure.

\n

Instead, you need a way to load a new component without a fixed\nreference to the component in the ad banner's template.

\n

Angular comes with its own API for loading components dynamically.

\n\n

The anchor directivelink

\n

Before you can add components you have to define an anchor point\nto tell Angular where to insert components.

\n

The ad banner uses a helper directive called AdDirective to\nmark valid insertion points in the template.

\n\nimport { Directive, ViewContainerRef } from '@angular/core';\n\n@Directive({\n selector: '[adHost]',\n})\nexport class AdDirective {\n constructor(public viewContainerRef: ViewContainerRef) { }\n}\n\n\n\n\n

AdDirective injects ViewContainerRef to gain access to the view\ncontainer of the element that will host the dynamically added component.

\n

In the @Directive decorator, notice the selector name, adHost;\nthat's what you use to apply the directive to the element.\nThe next section shows you how.

\n\n

Loading componentslink

\n

Most of the ad banner implementation is in ad-banner.component.ts.\nTo keep things simple in this example, the HTML is in the @Component\ndecorator's template property as a template string.

\n

The <ng-template> element is where you apply the directive you just made.\nTo apply the AdDirective, recall the selector from ad.directive.ts,\n[adHost]. Apply that to <ng-template> without the square brackets. Now Angular knows\nwhere to dynamically load components.

\n\ntemplate: `\n <div class=\"ad-banner-example\">\n <h3>Advertisements</h3>\n <ng-template adHost></ng-template>\n </div>\n `\n\n\n

The <ng-template> element is a good choice for dynamic components\nbecause it doesn't render any additional output.

\n\n

Resolving componentslink

\n

Take a closer look at the methods in ad-banner.component.ts.

\n

AdBannerComponent takes an array of AdItem objects as input,\nwhich ultimately comes from AdService. AdItem objects specify\nthe type of component to load and any data to bind to the\ncomponent.AdService returns the actual ads making up the ad campaign.

\n

Passing an array of components to AdBannerComponent allows for a\ndynamic list of ads without static elements in the template.

\n

With its getAds() method, AdBannerComponent cycles through the array of AdItems\nand loads a new component every 3 seconds by calling loadComponent().

\n\nexport class AdBannerComponent implements OnInit, OnDestroy {\n @Input() ads: AdItem[];\n currentAdIndex = -1;\n @ViewChild(AdDirective, {static: true}) adHost: AdDirective;\n interval: any;\n\n constructor(private componentFactoryResolver: ComponentFactoryResolver) { }\n\n ngOnInit() {\n this.loadComponent();\n this.getAds();\n }\n\n ngOnDestroy() {\n clearInterval(this.interval);\n }\n\n loadComponent() {\n this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;\n const adItem = this.ads[this.currentAdIndex];\n\n const componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);\n\n const viewContainerRef = this.adHost.viewContainerRef;\n viewContainerRef.clear();\n\n const componentRef = viewContainerRef.createComponent<AdComponent>(componentFactory);\n componentRef.instance.data = adItem.data;\n }\n\n getAds() {\n this.interval = setInterval(() => {\n this.loadComponent();\n }, 3000);\n }\n}\n\n\n

The loadComponent() method is doing a lot of the heavy lifting here.\nTake it step by step. First, it picks an ad.

\n
\n

How loadComponent() chooses an ad

\n

The loadComponent() method chooses an ad using some math.

\n

First, it sets the currentAdIndex by taking whatever it\ncurrently is plus one, dividing that by the length of the AdItem array, and\nusing the remainder as the new currentAdIndex value. Then, it uses that\nvalue to select an adItem from the array.

\n
\n

After loadComponent() selects an ad, it uses ComponentFactoryResolver\nto resolve a ComponentFactory for each specific component.\nThe ComponentFactory then creates an instance of each component.

\n

Next, you're targeting the viewContainerRef that\nexists on this specific instance of the component. How do you know it's\nthis specific instance? Because it's referring to adHost and adHost is the\ndirective you set up earlier to tell Angular where to insert dynamic components.

\n

As you may recall, AdDirective injects ViewContainerRef into its constructor.\nThis is how the directive accesses the element that you want to use to host the dynamic component.

\n

To add the component to the template, you call createComponent() on ViewContainerRef.

\n

The createComponent() method returns a reference to the loaded component.\nUse that reference to interact with the component by assigning to its properties or calling its methods.

\n\n

The AdComponent interfacelink

\n

In the ad banner, all components implement a common AdComponent interface to\nstandardize the API for passing data to the components.

\n

Here are two sample components and the AdComponent interface for reference:

\n\n\n \nimport { Component, Input } from '@angular/core';\n\nimport { AdComponent } from './ad.component';\n\n@Component({\n template: `\n <div class=\"job-ad\">\n <h4>{{data.headline}}</h4>\n\n {{data.body}}\n </div>\n `\n})\nexport class HeroJobAdComponent implements AdComponent {\n @Input() data: any;\n\n}\n\n\n\n\n\n \nimport { Component, Input } from '@angular/core';\n\nimport { AdComponent } from './ad.component';\n\n@Component({\n template: `\n <div class=\"hero-profile\">\n <h3>Featured Hero Profile</h3>\n <h4>{{data.name}}</h4>\n\n <p>{{data.bio}}</p>\n\n <strong>Hire this hero today!</strong>\n </div>\n `\n})\nexport class HeroProfileComponent implements AdComponent {\n @Input() data: any;\n}\n\n\n\n\n\n\n \nexport interface AdComponent {\n data: any;\n}\n\n\n\n\n\n\n

Final ad bannerlink

\n

The final ad banner looks like this:

\n
\n \"Ads\"\n
\n

See the .

\n\n \n
\n\n\n" }