Angular如何编程式动态渲染传入组件实现可配置Widget仪表盘
Angular仪表盘动态组件渲染标准实现
1. 定义Widget接口类型
component属性直接使用Angular核心包提供的Type<any>类型,用来接收组件类本身:
import { Type } from '@angular/core'; export interface Widget { x: number; y: number; component: Type<any>; }
2. 父组件传值
直接导入需要渲染的具体小部件组件类,赋值给component属性即可。注意所有动态渲染的组件,需要在所属模块的declarations中声明(Angular 13+基于Ivy引擎无需额外配置entryComponents,12及以下版本需要将动态组件加入模块的entryComponents数组;如果是Standalone独立组件,直接在使用处导入即可)。
// 导入自定义的小部件组件 import { StatCardWidgetComponent } from './widgets/stat-card-widget/stat-card-widget.component'; import { LineChartWidgetComponent } from './widgets/line-chart-widget/line-chart-widget.component'; widgets(): Widget[] { return [ { x: 0, y: 0, component: StatCardWidgetComponent }, { x: 1, y: 0, component: LineChartWidgetComponent }, ]; }
3. dashboard-widget组件动态渲染实现
最简便的标准方案是使用Angular内置的*ngComponentOutlet指令,无需手动处理组件工厂逻辑,直接在模板中渲染传入的组件:
首先在dashboard-widget组件类中声明对应输入属性:
import { Component, Input, Type } from '@angular/core'; @Component({ selector: 'dashboard-widget', templateUrl: './dashboard-widget.component.html' }) export class DashboardWidgetComponent { @Input() x: number = 0; @Input() y: number = 0; @Input() component: Type<any>; }
然后在模板中使用指令渲染动态组件:
<div class="widget-wrapper" [style.left.px]="x" [style.top.px]="y"> <!-- 动态组件直接渲染在ng-container位置 --> <ng-container *ngComponentOutlet="component"></ng-container> </div>
扩展:给动态组件传参
如果需要给动态组件传入@Input属性、绑定@Output事件,有两种常用方式:
- Angular 14+版本可以直接给
*ngComponentOutlet传inputs、outputs配置:
<ng-container *ngComponentOutlet="component; inputs: { title: '数据概览', data: widgetData }; outputs: { refresh: onRefresh }" ></ng-container>
- 低版本或者需要更灵活控制的场景,可以用
ViewContainerRef手动创建组件:
首先在模板中标记组件挂载容器:
<div class="widget-wrapper" [style.left.px]="x" [style.top.px]="y"> <ng-template #widgetHost></ng-template> </div>
然后在组件类中获取容器引用,手动创建组件实例:
import { Component, Input, ViewChild, ViewContainerRef, Type, OnInit, OnDestroy } from '@angular/core'; export class DashboardWidgetComponent implements OnInit, OnDestroy { @Input() x: number = 0; @Input() y: number = 0; @Input() component: Type<any>; @Input() widgetData: any; @ViewChild('widgetHost', { read: ViewContainerRef, static: true }) widgetContainer: ViewContainerRef; private componentRef: any; ngOnInit() { this.widgetContainer.clear(); this.componentRef = this.widgetContainer.createComponent(this.component); // 传入输入属性 this.componentRef.instance.data = this.widgetData; // 订阅输出事件 this.componentRef.instance.refresh.subscribe(() => { console.log('widget触发刷新'); }); } ngOnDestroy() { // 销毁组件实例避免内存泄漏 this.componentRef?.destroy(); } }
内容的提问来源于stack exchange,提问作者Christoffer
相关产品推荐
相关产品推荐

