我正在尝试将多个ng-template
传递给我的可重用组件
(my-table组件),内容投影。现在我需要获取每个传递的 ng-template 的引用值,以便我可以使用该值来了解为哪一列传递了哪个模板。基本上,我正在创建一个可重用的表格组件(在 Angular Material 表的顶部),用户可以在其中为每一列传递一个单独的模板。
请建议 - 或者有更好的方法吗?
temp.component.ts
import { Component, OnInit, ContentChildren, QueryList, TemplateRef, AfterContentInit } from '@angular/core';
@Component({
selector: 'my-table',
template: `<h1>This is the temp component</h1>`,
styleUrls: ['./temp.component.scss']
})
export class TempComponent implements OnInit, AfterContentInit {
constructor() { }
@ContentChildren(TemplateRef) tempList: QueryList<TemplateRef<any>>;
ngOnInit() {
}
ngAfterContentInit() {
console.log('template list');
console.log(this.tempList);
}
}
app.component.html
<my-table>
<ng-template #column1 let-company let-func="func">
<h1>this template is for column 1</h1>
</ng-template>
<ng-template #column2 let-company let-func="func">
<h1>this template is for column 2</h1>
</ng-template>
</my-table>
我可以为每一列创建指令,但没有任何列可能会改变,因此指令路由将不起作用。我认为,组件用户将传递每个列模板,其中模板引用值作为列标题值,例如,如果用户为“firstName”列传递 ng-template
,它应该是这样的,
<ng-template #firstName let-firstname>
<h1>this template is for column firstName</h1>
</ng-template>
我需要一种方法来获取所有提供的 ng-template
及其引用,以便我可以知道哪个模板属于哪一列。
最佳答案
指令
是一个很好的方法,因此您已经在朝着正确的方向思考。指令还支持输入参数,因此您可以指定列名称或标题作为指令的参数。另请检查 official documentation了解更多详情。
以下是使用此方法的示例指令:
import { Directive, TemplateRef, Input } from '@angular/core';
@Directive({
selector: '[tableColumn]'
})
export class TableColumnDirective {
constructor(public readonly template: TemplateRef<any>) { }
@Input('tableColumn') columnName: string;
}
正如您所看到的,该指令有一个输入属性,它将接收列名称,并且它还会注入(inject) TemplateRef
,以便您可以直接从指令访问它。
然后您可以像这样定义列:
<ng-template tableColumn="firstname" let-firstname>
<h1>this template is for column firstName</h1>
</ng-template>
<ng-template tableColumn="lastName" let-lastname>
<h1>this template is for column lastName</h1>
</ng-template>
然后,在组件中,您可以通过指令查询 ContentChildren
并获取可让您访问列名称和模板的所有指令。
这是更新后的组件:
import { Component, OnInit, ContentChildren, QueryList, TemplateRef, AfterContentInit } from '@angular/core';
@Component({
selector: 'my-table',
template: `<h1>This is the temp component</h1>`,
styleUrls: ['./temp.component.scss']
})
export class TempComponent implements OnInit,AfterContentInit {
constructor() { }
@ContentChildren(TableColumnDirective) columnList: QueryList<TableColumnDirective>;
ngOnInit() {
}
ngAfterContentInit(){
console.log('column template list');
console.log(this.columnList.toArray());
}
}
这里有一个稍微不同的方法,也许你更喜欢这个。由于您提供了更多信息,我现在将根据您的自定义表格示例进行调整。
您可以创建一个接受内容的指令,并将模板指定为内容。这是一个示例实现:
@Directive({
selector: 'custom-mat-column',
})
export class CustomMatColumnComponent {
@Input() public columnName: string;
@ContentChild(TemplateRef) public columnTemplate: TemplateRef<any>;
}
然后你的父组件模板将更改为:
<custom-mat-table [tableColumns]="columnList" [tableDataList]="tableDataList
(cellClicked)="selectTableData($event)" (onSort)="onTableSort($event)" class="css-class-admin-users-table">
<custom-mat-column columnName="firstname">
<ng-template let-item let-func="func">
<div class="css-class-table-apps-name">
<comp-avatar [image]="" [name]="item?.processedName" [size]="'small'"></comp-avatar>
<comp-button (onClick)="func(item)" type="text">{{item?.processedName}}</comp-button>
</div>
</ng-template>
</custom-mat-column>
<custom-mat-column columnName="status">
<ng-template #status let-item>
<div [ngClass]="{'item-active' : item?.status, 'item-inactive' : !item?.status}"
class="css-class-table-apps-name">{{item?.status | TextCaseConverter}}
</div>
</ng-template>
</custom-mat-column>
<custom-mat-column columnName="lastname">
<ng-template #lastname let-item>
<div class="css-class-table-apps-name">
{{item?.lastname}}</div>
</ng-template>
</custom-mat-column>
</custom-mat-table>
您的自定义表格组件需要更改。它需要根据需要从 ContentChildren
生成它,而不是接收 templateNameList
。
@Component({
selector: 'custom-mat-table',
templateUrl: './customTable.component.html',
styleUrls: ['./customTable.component.scss']
})
export class NgMatTableComponent<T> implements OnChanges, AfterViewInit {
@ContentChildren(CustomMatColumnComponent) columnDefinitions: QueryList<CustomMatColumnComponent>;
templateNameList: { [key: string]: TemplateRef<any> } {
if (this.columnDefinitions != null) {
const columnTemplates: { [key: string]: TemplateRef<any> } = {};
for (const columnDefinition of this.columnDefinitions.toArray()) {
columnTemplates[columnDefinition.columnName] = columnDefinition.columnTemplate;
}
return columnTemplates;
} else {
return {};
}
};
@Input() tableColumns: TableColumns[] = [];
@Input() tableDataList: T[] = [];
@Output() cellClicked: EventEmitter<PayloadType> = new EventEmitter();
@Output() onSort: EventEmitter<TableSortEventData> = new EventEmitter();
displayedColumns: string[] = [];
tableDataSource: TableDataSource<T>;
@ViewChild(MatSort) sort: MatSort;
constructor() {
this.tableDataSource = new TableDataSource<T>();
}
onCellClick(e: T, options?: any) {
this.cellClicked.emit({ 'row': e, 'options': options });
}
ngOnChanges(change: SimpleChanges) {
if (change['tableDataList']) {
this.tableDataSource.emitTableData(this.tableDataList);
this.displayedColumns = this.tableColumns.map(x => x.displayCol);
}
}
ngAfterViewInit() {
this.tableDataSource.sort = this.sort;
}
sortTable(e: any) {
const { active: sortColumn, direction: sortOrder } = e;
this.onSort.emit({ sortColumn, sortOrder });
}
}
如果您不喜欢第二种方法,您仍然可以以相同的方式使用我在原始示例中建议的方法。唯一的区别是它在模板中的外观。 我还创建了一个 StackBlitz sample这样您就可以在实践中看到它。
关于angular - 使用 Angular 5 中的 contentChildren 获取多个 ng-template ref 值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59185318/