Skip to content

Commit

Permalink
docs: update Dynamic Component loader docs to rely on `ngComponentOut…
Browse files Browse the repository at this point in the history
…let`

With angular#51148, the `ngComponentOutlet` directive now supports inputs.
This allows a less verbose and simpler API to instantiate components dynamicaly.

Fixes angular#49875
  • Loading branch information
JeanMeche committed Jul 26, 2023
1 parent 3a59de6 commit cb57289
Show file tree
Hide file tree
Showing 14 changed files with 84 additions and 211 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { browser, element, by } from 'protractor';
import { browser, element, by, ExpectedConditions } from 'protractor';

describe('Dynamic Component Loader', () => {

Expand All @@ -17,6 +17,7 @@ describe('Dynamic Component Loader', () => {
const name = element(by.cssContainingText('h4', 'Bombasto'));
const bio = element(by.cssContainingText('p', 'Brave as they come'));

await browser.wait(ExpectedConditions.textToBePresentInElement(element(by.tagName('h4')), 'Bombasto'), 5000);
expect(await headline.isPresent()).toBe(true);
expect(await name.isPresent()).toBe(true);
expect(await bio.isPresent()).toBe(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,56 +1,45 @@
// #docregion
import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Component, inject, OnDestroy, OnInit, } from '@angular/core';
import { CommonModule, NgComponentOutlet } from '@angular/common';

import { AdDirective } from './ad.directive';
import { AdItem } from './ad-item';
import { AdComponent } from './ad.component';
import { AdService } from './ad.service';

@Component({
selector: 'app-ad-banner',
// #docregion ad-host
standalone: true,
imports: [CommonModule, NgComponentOutlet],
// #docregion ng-container
template: `
<div class="ad-banner-example">
<h3>Advertisements</h3>
<ng-template adHost></ng-template>
<ng-container *ngComponentOutlet="
currentAd.component;
inputs: currentAd.inputs;
" />
</div>
`
// #enddocregion ad-host
// #enddocregion ng-container
})
// #docregion class
export class AdBannerComponent implements OnInit, OnDestroy {
@Input() ads: AdItem[] = [];
adList = inject(AdService).getAds();

currentAdIndex = -1;
currentAdIndex = 0;
currentAd = this.adList[this.currentAdIndex];

@ViewChild(AdDirective, {static: true}) adHost!: AdDirective;
private interval!: number;

private clearTimer: VoidFunction | undefined;

ngOnInit(): void {
this.loadComponent();
this.getAds();
ngOnInit() {
this.interval = window.setInterval(() => this.displayNextAd(), 2000);
}

ngOnDestroy() {
this.clearTimer?.();
displayNextAd() {
this.currentAdIndex = (this.currentAdIndex + 1) % this.adList.length;
this.currentAd = this.adList[this.currentAdIndex];
}

loadComponent() {
this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;
const adItem = this.ads[this.currentAdIndex];

const viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear();

const componentRef = viewContainerRef.createComponent<AdComponent>(adItem.component);
componentRef.instance.data = adItem.data;
}

getAds() {
const interval = setInterval(() => {
this.loadComponent();
}, 3000);
this.clearTimer = () => clearInterval(interval);
ngOnDestroy() {
clearInterval(this.interval);
}
}
// #enddocregion class

This file was deleted.

This file was deleted.

This file was deleted.

41 changes: 23 additions & 18 deletions aio/content/examples/dynamic-component-loader/src/app/ad.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,33 @@ import { Injectable } from '@angular/core';

import { HeroJobAdComponent } from './hero-job-ad.component';
import { HeroProfileComponent } from './hero-profile.component';
import { AdItem } from './ad-item';

@Injectable()
@Injectable({ providedIn: 'root' })
export class AdService {
getAds() {
return [
new AdItem(
HeroProfileComponent,
{ name: 'Bombasto', bio: 'Brave as they come' }
),
new AdItem(
HeroProfileComponent,
{ name: 'Dr. IQ', bio: 'Smart as they come' }
),
new AdItem(
HeroJobAdComponent,
{ headline: 'Hiring for several positions', body: 'Submit your resume today!' }
),
new AdItem(
HeroJobAdComponent,
{ headline: 'Openings in all departments', body: 'Apply today' }
)
{
component: HeroProfileComponent,
inputs: { name: 'Dr. IQ', bio: 'Smart as they come' },
},
{
component: HeroProfileComponent,
inputs: { name: 'Bombasto', bio: 'Brave as they come' },
},
{
component: HeroJobAdComponent,
inputs: {
headline: 'Hiring for several positions',
body: 'Submit your resume today!',
},
},
{
component: HeroJobAdComponent,
inputs: {
headline: 'Openings in all departments',
body: 'Apply today',
},
},
];
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/* eslint-disable @angular-eslint/no-input-rename */
// #docregion
import { Component, Input } from '@angular/core';

import { AdComponent } from './ad.component';

@Component({
standalone: true,
template: `
<div class="job-ad">
<h4>{{data.headline}}</h4>
{{data.body}}
<h4>{{ headline }}</h4>
{{ body }}
</div>
`
`,
})
export class HeroJobAdComponent implements AdComponent {
@Input() data: any;
export class HeroJobAdComponent {
@Input({ required: true }) headline!: string;
@Input({ required: true }) body!: string;
}

Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
/* eslint-disable @angular-eslint/no-input-rename */
// #docregion
import { Component, Input } from '@angular/core';

import { AdComponent } from './ad.component';

@Component({
standalone: true,
template: `
<div class="hero-profile">
<h3>Featured Hero Profile</h3>
<h4>{{data.name}}</h4>
<p>{{data.bio}}</p>
<h4>{{ name }}</h4>
<p>{{ bio }}</p>
<strong>Hire this hero today!</strong>
</div>
`
`,
})
export class HeroProfileComponent implements AdComponent {
@Input() data: any;
export class HeroProfileComponent {
@Input({ required: true }) name!: string;
@Input({ required: true }) bio!: string;
}


Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<link rel="stylesheet" href="assets/sample.css">
</head>
<body>
<app-root></app-root>
<app-ad-banner></app-ad-banner>
</body>

</html>
8 changes: 3 additions & 5 deletions aio/content/examples/dynamic-component-loader/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// #docregion
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { bootstrapApplication } from '@angular/platform-browser';
import { AdBannerComponent } from './app/ad-banner.component';

import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
bootstrapApplication(AdBannerComponent);
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"!**/*.d.ts",
"!**/*.js"
],
"file": "src/app/app.component.ts",
"file": "src/app/ad-banner.component.ts",
"tags":["cookbook component"]
}
Loading

0 comments on commit cb57289

Please sign in to comment.