Angular 4:如何显示继承类组件的模板
嘿,我懂你现在的困扰——你有一堆继承自ShapeComponent的子类组件,想把它们放进数组里循环渲染,让每个组件用自己的模板,结果用<app-shape *ngFor="let shape of shapes"></app-shape>只能渲染基类的模板,完全达不到预期。这是因为Angular的静态模板编译机制在搞鬼,它只会认你模板里写的app-shape选择器,不管数组里的实例实际是什么子类。
别担心,用Angular的ngComponentOutlet指令就能完美解决这个问题,下面是一步步的实现方案:
1. 先改掉手动实例化组件的坏习惯
你现在直接new CircleComponent()这种写法会跳过Angular的依赖注入和生命周期钩子,组件根本没法正常工作。正确的做法是存储组件的类型,而不是实例:
// app.component.ts import { Component, Type } from '@angular/core'; import { ShapeComponent } from './shape.component'; import { CircleComponent } from './circle.component'; import { SquareComponent } from './square.component'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent { // 存储组件类型,交给Angular来实例化 shapeComponents: Type<ShapeComponent>[] = [CircleComponent, SquareComponent]; }
2. 用ngComponentOutlet动态渲染每个组件
把模板里的<app-shape>换成ngComponentOutlet,它能根据组件类型动态加载对应的模板:
<!-- app.component.html --> <div class="shape-list" *ngFor="let comp of shapeComponents"> <ng-container *ngComponentOutlet="comp"></ng-container> </div>
3. 确保组件在模块中正确声明
在你的AppModule里,要把所有子类组件都加到declarations数组里(Angular 9+用Ivy编译器的话,不需要再加entryComponents了,旧版本记得加上):
// app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { ShapeComponent } from './shape.component'; import { CircleComponent } from './circle.component'; import { SquareComponent } from './square.component'; @NgModule({ declarations: [ AppComponent, ShapeComponent, CircleComponent, SquareComponent ], imports: [BrowserModule], // Angular 9+ 可省略这一行 // entryComponents: [CircleComponent, SquareComponent], bootstrap: [AppComponent] }) export class AppModule { }
4. 给动态组件传参(可选)
如果需要给每个组件传递输入属性,比如形状的尺寸,可以结合ngComponentOutlet的inputs参数:
// app.component.ts shapeConfigs = [ { component: CircleComponent, size: 60 }, { component: SquareComponent, size: 40 } ];
模板里这么写:
<div *ngFor="let config of shapeConfigs"> <ng-container *ngComponentOutlet="config.component; inputs: { size: config.size }"> </ng-container> </div>
为啥你原来的写法不行?
再给你解释下原理:Angular的模板是静态编译的,当你写<app-shape *ngFor="let shape of shapes">时,Angular在编译阶段就把所有循环项都绑定到ShapeComponent了,不管你数组里的实例是哪个子类——它只会按模板里的选择器渲染,不会在运行时动态识别实例类型。只有ngComponentOutlet这种动态加载机制,才能根据组件类型渲染对应的模板。
内容的提问来源于stack exchange,提问作者Eibi

