Angular 7 个动态注射模板
Angular 7 Dynamic injectible templates
我会尽可能简单地解释我的问题:
- 我目前正在使用
NgxDatatable
渲染 crud table。
- 我有一个名为
CrudComponent
的基础组件来处理所有的垃圾
- 这个组件是为了扩展所有简单的实体
我现在面临的问题是为 后代 提供一种方式来注入自定义 cellTemplate
.
我试图避免代码重复,这样我就不必为了添加 1-2 ng-template
.
而重新复制父模板
例如我在 CrudComponent
:
@ViewChild('actionsCellRenderer') actionsCellRenderer: TemplateRef<any>;
并在模板中:
<ng-template #actionsCellRenderer let-row="row" let-value="value">
<button mat-icon-button (click)="startEdit(row)">
<fa-icon [icon]="['far', 'edit']"></fa-icon>
</button>
<button mat-icon-button (click)="startDelete(row)" color="warn">
<fa-icon [icon]="['far', 'trash-alt']"></fa-icon>
</button>
</ng-template>
假设我用 MovieComponent
扩展了这个 CrudComponent
。我需要 手动复制整个 html 模板 到新的 MovieComponent.html
模板并添加:
@ViewChild('ratingCellRenderer') ratingCellRenderer: TemplateRef<any>;
<ng-template #ratingCellRenderer let-row="row" let-value="value">
<bar-rating
[(rate)]="value"
[max]="10"
theme="horizontal"
readOnly="true"
></bar-rating>
</ng-template>
可能的解决方案:
- 这个问题的一个解决方案是使用一些模板预编译器,比如
twig
。这可能吗?如果可以,怎么做?
- 或更多angular解决方案。基本上
TemplateRef<any>
的 table 像这样:
cellRenderers = {
rating: new TemplateRef<any>(), //but how do I manually create TemplateRef?
picture: new TemplateRef<any>(),
}
然后在NgxDatatable
列定义:
{name: 'Score', prop: 'score', cellTemplate: this.cellRenderers['rating']},
- 或者也许有另一种更优雅的方法来处理这个问题?
我最终删除了 NgxDatatable
并编写了我自己的 CrudTable
实现,其中包含 ColumnRendererComponent
:
import {Component, ComponentFactoryResolver, Input, OnInit, ViewChild, ViewContainerRef} from '@angular/core';
import {TableColumnType} from '../Table/TableColumnType';
import {FieldConfig} from '../../../Interface/FieldConfig';
import {RendererInterface} from './RendererInterface';
import {PlainRenderer} from './PlainRenderer';
import {FunctionRenderer} from './FunctionRenderer';
@Component({
selector: 'column-renderer',
template: `<ng-template #renderer></ng-template>`
})
export class ColumnRendererComponent implements OnInit {
@Input() public value: any;
@Input() public row: any;
@Input() public column: FieldConfig;
@Input() public columnType: TableColumnType;
constructor(
protected componentFactoryResolver: ComponentFactoryResolver,
public viewContainerRef: ViewContainerRef
) {
}
ngOnInit(): void {
this.loadRenderer();
}
loadRenderer() {
let component = this.columnType.template ? this.columnType.template :
(this.columnType.renderFn ? FunctionRenderer : PlainRenderer);
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
let viewContainerRef = this.viewContainerRef;
viewContainerRef.clear();
let componentRef = viewContainerRef.createComponent(componentFactory);
let instance = <RendererInterface>componentRef.instance;
instance.column = this.column;
instance.columnType = this.columnType;
instance.row = this.row;
instance.value = this.value;
}
}
我会尽可能简单地解释我的问题:
- 我目前正在使用
NgxDatatable
渲染 crud table。 - 我有一个名为
CrudComponent
的基础组件来处理所有的垃圾 - 这个组件是为了扩展所有简单的实体
我现在面临的问题是为 后代 提供一种方式来注入自定义 cellTemplate
.
我试图避免代码重复,这样我就不必为了添加 1-2 ng-template
.
例如我在 CrudComponent
:
@ViewChild('actionsCellRenderer') actionsCellRenderer: TemplateRef<any>;
并在模板中:
<ng-template #actionsCellRenderer let-row="row" let-value="value">
<button mat-icon-button (click)="startEdit(row)">
<fa-icon [icon]="['far', 'edit']"></fa-icon>
</button>
<button mat-icon-button (click)="startDelete(row)" color="warn">
<fa-icon [icon]="['far', 'trash-alt']"></fa-icon>
</button>
</ng-template>
假设我用 MovieComponent
扩展了这个 CrudComponent
。我需要 手动复制整个 html 模板 到新的 MovieComponent.html
模板并添加:
@ViewChild('ratingCellRenderer') ratingCellRenderer: TemplateRef<any>;
<ng-template #ratingCellRenderer let-row="row" let-value="value">
<bar-rating
[(rate)]="value"
[max]="10"
theme="horizontal"
readOnly="true"
></bar-rating>
</ng-template>
可能的解决方案:
- 这个问题的一个解决方案是使用一些模板预编译器,比如
twig
。这可能吗?如果可以,怎么做? - 或更多angular解决方案。基本上
TemplateRef<any>
的 table 像这样:
cellRenderers = {
rating: new TemplateRef<any>(), //but how do I manually create TemplateRef?
picture: new TemplateRef<any>(),
}
然后在NgxDatatable
列定义:
{name: 'Score', prop: 'score', cellTemplate: this.cellRenderers['rating']},
- 或者也许有另一种更优雅的方法来处理这个问题?
我最终删除了 NgxDatatable
并编写了我自己的 CrudTable
实现,其中包含 ColumnRendererComponent
:
import {Component, ComponentFactoryResolver, Input, OnInit, ViewChild, ViewContainerRef} from '@angular/core';
import {TableColumnType} from '../Table/TableColumnType';
import {FieldConfig} from '../../../Interface/FieldConfig';
import {RendererInterface} from './RendererInterface';
import {PlainRenderer} from './PlainRenderer';
import {FunctionRenderer} from './FunctionRenderer';
@Component({
selector: 'column-renderer',
template: `<ng-template #renderer></ng-template>`
})
export class ColumnRendererComponent implements OnInit {
@Input() public value: any;
@Input() public row: any;
@Input() public column: FieldConfig;
@Input() public columnType: TableColumnType;
constructor(
protected componentFactoryResolver: ComponentFactoryResolver,
public viewContainerRef: ViewContainerRef
) {
}
ngOnInit(): void {
this.loadRenderer();
}
loadRenderer() {
let component = this.columnType.template ? this.columnType.template :
(this.columnType.renderFn ? FunctionRenderer : PlainRenderer);
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
let viewContainerRef = this.viewContainerRef;
viewContainerRef.clear();
let componentRef = viewContainerRef.createComponent(componentFactory);
let instance = <RendererInterface>componentRef.instance;
instance.column = this.column;
instance.columnType = this.columnType;
instance.row = this.row;
instance.value = this.value;
}
}