Angular 动态组件
实现步骤
- Directive
- HostComponent
- 动态组件
- AdService
- 配置AppModule
- 需要了解的概念
Directive
- 我们需要一个Directive来标记动态组件是在哪个容器组件内部进行渲染的。
- 这个Directive可以获取对容器组件的引用。
- 仅此而已。
import { Directive, ViewContainerRef } from ‘@angular/core‘;
@Directive({
selector: ‘[appAd]‘,
})
export class AdDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
HostComponent
- 我们需要一个容器组件,所有动态组件都是在这个容器组件中创建,销毁,重新创建。。。
- 需要将动态组件需要展示的数据和动态组件进行绑定。
import { Component, Input, AfterViewInit, ViewChild, ComponentFactoryResolver, OnDestroy } from ‘@angular/core‘;
import { AdDirective } from ‘./ad.directive‘;
import { AdItem } from ‘./ad-item‘;
import { AdComponent } from ‘./ad.component‘;
@Component({
selector: ‘app-add-banner‘,
template: `
<div class="ad-banner">
<h3>Advertisements</h3>
<!-- hostElement 在此! -->
<ng-template appAd></ng-template>
</div>
`
})
export class AdBannerComponent implements AfterViewInit, OnDestroy {
@Input() ads: AdItem[];
currentAddIndex: number = -1;
@ViewChild(AdDirective) adHost: AdDirective;
subscription: any;
interval: any;
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
// 在 view 初始化结束后才开始创建动态组件
ngAfterViewInit() {
this.loadComponent();
this.getAds();
}
ngOnDestroy() {
clearInterval(this.interval);
}
loadComponent() {
this.currentAddIndex = (this.currentAddIndex + 1) % this.ads.length;
let adItem = this.ads[this.currentAddIndex];
// 这里使用了工厂模式。其实Angular对于模板中出现的每一个Component都会创建一个ComponentFactory。在创建销毁时
// 实际上使用的是组件工厂来创建组件的新实例。
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);
let viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear();
// 传入对应的组件工厂来创建新的组件,并保存新组建的引用。
let componentRef = viewContainerRef.createComponent(componentFactory);
// 绑定数据
(<AdComponent>componentRef.instance).data = adItem.data;
}
getAds() {
this.interval = setInterval(() => {
this.loadComponent();
}, 3000);
}
}
// add-item.ts
import { Type } from ‘@angular/core‘;
export class AdItem {
constructor(public component: Type<any>, public data: any) {}
}
// ad.component.ts
export interface AdComponent {
data: any;
}
在AppComponent 中使用
// app.component.ts
import { Component, OnInit } from ‘@angular/core‘;
import { AdService } from ‘./ad.service‘;
import { AdItem } from ‘./ad-item‘;
@Component({
selector: ‘app-root‘,
template: `
<div>
<app-add-banner [ads]="ads"></app-add-banner>
</div>
`
})
export class AppComponent implements OnInit {
ads: AdItem[];
constructor(private adService: AdService) {}
ngOnInit() {
this.ads = this.adService.getAds();
}
}
创建动态组件
// hero-job-ad.component.ts
import { Component, Input } from ‘@angular/core‘;
import { AdComponent } from ‘./ad.component‘;
@Component({
template: `
<div class="job-ad">
<h4>{{data.headline}}</h4>
{{data.body}}
</div>
`
})
export class HeroJobAdComponent implements AdComponent {
@Input() data: any;
}
// hero-profile.component.ts
import { Component, Input } from ‘@angular/core‘;
import { AdComponent } from ‘./ad.component‘;
@Component({
template: `
<div class="hero-profile">
<h3>Featured Hero Profile</h3>
<h4>{{data.name}}</h4>
<p>{{data.bio}}</p>
<strong>Hire this hero today!</strong>
</div>
`
})
export class HeroProfileComponent implements AdComponent {
@Input() data: any;
}
创建service
import { Injectable } from ‘@angular/core‘;
import { HeroJobAdComponent } from ‘./hero-job-ad.component‘;
import { HeroProfileComponent } from ‘./hero-profile.component‘;
import { AdItem } from ‘./ad-item‘;
@Injectable()
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‘}),
];
}
}
配置 AppModule
// app.module.ts
import { BrowserModule } from ‘@angular/platform-browser‘;
import { NgModule } from ‘@angular/core‘;
import { AppComponent } from ‘./app.component‘;
import { HeroJobAdComponent } from ‘./hero-job-ad.component‘;
import { AdBannerComponent } from ‘./ad-banner.component‘;
import { HeroProfileComponent } from ‘./hero-profile.component‘;
import { AdDirective } from ‘./ad.directive‘;
import { AdService } from ‘./ad.service‘;
@NgModule({
imports: [ BrowserModule ],
providers: [AdService],
declarations: [ AppComponent,
AdBannerComponent,
HeroJobAdComponent,
HeroProfileComponent,
AdDirective ],
// 注意这里,需要手动引入动态组件的class,放入 entryComponents数组中,这样
// Angular才能为动态组件创建组件工厂。如果不写,Angular在模板中不会发现这两个组件的具体引用,
// 可能在打包时将组件代码排除。
entryComponents: [ HeroJobAdComponent, HeroProfileComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule {
constructor() {}
}
ng serve
完成。
需要了解的概念
- ViewContainerRef
- ViewChild
- ComponentFactoryResolver
- ComponentFactory
- ComponentRef