我知道这是一个老问题,但这是我多年来在 Angular 项目中的做法。
我们需要一个简单的TemplateRef 实例映射:
import { TemplateRef } from "@angular/core";
export class TemplateStore {
private templates: Map<string, TemplateRef<any>> = new Map<string, TemplateRef<any>>();
public add(key: string, template: TemplateRef<any>): void {
// The argument validation is omitted for brevity
this.templates.set(key, template);
}
public get(key: string): TemplateRef<any> {
return this.templates.get(key);
}
}
还有一个为模板分配键的指令:
import { Directive, Input, TemplateRef } from "@angular/core";
import { TemplateStore } from "./template-store";
@Directive({
selector: "[appTemplateKey]"
})
export class TemplateKey {
constructor(
private readonly template: TemplateRef<any>,
private readonly templateStore: TemplateStore
) { }
@Input("appTemplateKey")
public set key(value: string) {
this.templateStore.add(value, this.template);
}
}
然后我们把全局模板放到app组件模板中:
<ng-template appTemplateKey="avatar" let-user>
<a routerLink="/{{user.id}}" aria-hidden="true" tabindex="-1">
<img [src]="user.pictureUrl" alt="" loading="lazy">
</a>
</ng-template>
及用法:
<ng-container *ngTemplateOutlet="templateStore.get('avatar'); context: {$implicit: currentUser}"></ng-container>
这里的不便之处在于我们需要将TemplateStore 注入到每个使用它的组件中。由于在我的 Angular 项目中,所有组件都继承了一个基类,我们可以通过在基类中放入如下方法来避免注入:
public template(key: string): TemplateRef<any> {
return AppContext.current.templateStore.get(key);
}
AppContext.current 是一个全局对象,包含对TemplateStore 的引用。所以用法变成:
<ng-container *ngTemplateOutlet="template('avatar'); context: {$implicit: currentUser}"></ng-container>