如何为可复用Angular表格组件的行添加自定义图标/进度条?
嘿,这个问题我之前做Angular表格组件时也踩过坑!默认的<ng-content>确实只会把投影内容渲染一次——不管你在循环里写多少次,Angular的内容投影机制都会把所有传入的内容一次性放到第一个匹配的<ng-content>节点里,所以后面的循环项就没内容可投影了。
要实现每一行的指定列都能自定义图标、进度条这类动态内容,咱们得用模板引用(TemplateRef)+ ngTemplateOutlet的方案,这是Angular组件复用中处理动态行内容的标准玩法,具体步骤如下:
第一步:在表格组件中接收模板输入
先在table.component.ts里定义一个@Input(),用来接收父组件传递的列模板映射:
import { Component, Input, TemplateRef } from '@angular/core'; @Component({ selector: 'app-data-table', templateUrl: './table.component.html' }) export class DataTableComponent { @Input() data: any[] = []; @Input() cols: any[] = []; // 新增:接收列名到模板的映射 @Input() columnTemplates: { [colProp: string]: TemplateRef<any> } = {}; // 保留你原有的selectColHeader、sort等方法... }
第二步:修改表格模板,动态渲染模板
把原来的<ng-content>替换成ngTemplateOutlet,判断当前列是否有对应的自定义模板,有就渲染模板,没有就显示默认的行数据:
<table> <thead> <!-- 你的表头代码保持不变 --> <tr> <th *ngFor="let col of cols" (click)="selectColHeader(col.prop); col.enableSort && sort(col.prop)"> {{col.header}} <input type="text" class="ds-c-field" [(ngModel)]=fields[col.prop] (click)="selectColInput(col.prop, $event)" *ngIf=col.enableFilter/> <img class="arrow" *ngIf="col.enableSort && col.prop === selectedColHeader" [src]="direction === 'asc' ? upArrowPath : downArrowPath"/> </th> </tr> </thead> <tbody> <tr *ngFor="let row of data"> <td *ngFor="let col of cols"> <!-- 优先渲染自定义模板 --> <ng-container *ngIf="columnTemplates[col.prop]; else defaultCell"> <ng-template [ngTemplateOutlet]="columnTemplates[col.prop]" [ngTemplateOutletContext]="{ $implicit: row }" ></ng-template> </ng-container> <!-- 默认内容:显示行数据 --> <ng-template #defaultCell> {{ row[col.prop] }} </ng-template> </td> </tr> </tbody> </table>
这里的ngTemplateOutletContext是关键——它把当前行的row数据传递给了模板,让你能在自定义模板里根据行数据动态渲染内容。
第三步:在父组件中定义并传递模板
现在回到app.component.html,用<ng-template>定义你需要的图标/进度条模板,然后把模板传递给表格组件:
<app-data-table [data]="data" [cols]="cols" [columnTemplates]="{ status: statusIconTemplate, progress: progressBarTemplate }" > <!-- 状态列的图标模板:let-row接收传递过来的当前行数据 --> <ng-template #statusIconTemplate let-row> <img src="/icons/{{row.status}}.svg" alt="{{row.status}}状态图标"/> </ng-template> <!-- 进度列的进度条模板 --> <ng-template #progressBarTemplate let-row> <div class="progress-container"> <div class="progress-fill" [style.width.%]="row.progress"></div> <span>{{row.progress}}%</span> </div> </ng-template> </app-data-table>
这里的{ status: statusIconTemplate }是把列的prop(比如你的cols里定义的prop: 'status')和对应的模板绑定起来,表格组件就会在status列的每一行渲染这个图标模板。
为什么这个方案可行?
和<ng-content>的静态内容投影不同,ngTemplateOutlet是动态渲染模板实例——每一行的循环都会创建一个新的模板实例,并且能接收当前行的数据,完美解决了循环中重复渲染自定义内容的需求。
内容的提问来源于stack exchange,提问作者FakeEmpire

