问题描述
我正在尝试将多个 ng-template
传递给可重用的 component
(my-table组件),即内容投影.现在,我需要获取每个传递的 ng-template
的参考值,以便可以使用该值来知道为哪个列传递了哪个模板.基本上,我在Angular材质表的顶部创建了一个可重用的表组件,用户可以为每个列传递一个单独的模板.
I am trying to pass multiple ng-template
to my reusable component
(my-table component), content projection. Now I need to get the reference value of each passed ng-template
so I can use that value to know, which template is passed for which column. Basically I am creating a reusable table component (on top of Angular material table) where user can pass an separate template for each column.
请提出建议-还是有更好的方法呢?
Kindly suggest - OR is there a better approach of doing this?
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
,则组件用户将以模板ref值作为列标题值传递每个列模板.列,应该像,
I can create directive for each column, but than no of column might change so directive route will not work. I am thinking that, component user will pass each column template with template ref value as column header value, for example, if user is passing an ng-template
for "firstName" column, it should be like ,
<ng-template #firstName let-firstname>
<h1>this template is for column firstName</h1>
</ng-template>
我需要一种方法来获取所有提供的 ng-template
及其引用,以便我知道哪个模板属于哪一列.
And I need a way to get all the provided ng-template
with their ref so I can know, which template belongs to which column.
推荐答案
Directive
是解决此问题的一种好方法,因此您已经在朝着正确的方向进行思考.指令还支持输入参数,因此您可以将列名或标题指定为指令的参数.还要检查官方文档以获取更多详细信息.
A Directive
is a good approach for this so you are already thinking in the right direction. Directives support also input parameters so you can specify the column name or header as the parameter to the directive. Check also the official documentation for more details.
以下是使用此方法的示例指令:
Here is a sample directive using this approach:
import { Directive, TemplateRef, Input } from '@angular/core';
@Directive({
selector: '[tableColumn]'
})
export class TableColumnDirective {
constructor(public readonly template: TemplateRef<any>) { }
@Input('tableColumn') columnName: string;
}
如您所见,该指令具有一个输入属性,该属性将接收列名,并且还会注入 TemplateRef
,因此您可以直接从该指令访问它.
As you can see the directive has an input property that will receive the column name and also it injects the TemplateRef
so you can access it directly from the directive.
然后您可以像这样定义列:
You can then define the columns like this:
<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
,并获取所有可访问列名和模板的指令.
In the component you then query the ContentChildren
by the directive and get all the directives which gives you access to the column names and templates.
这是更新的组件:
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());
}
}
这是一种稍微不同的方式,也许您更喜欢这种方式.由于您提供了更多信息,因此我现在将基于您的自定义表格示例.
Here is a slightly different way to do it maybe you like this more. I will now base it on your custom table sample since you provided more information.
您可以创建一个接受内容的指令,然后将模板指定为内容.这是一个示例实现:
You can create a directive that takes content and you specify the template as the content. Here is a sample implementation:
@Directive({
selector: 'custom-mat-column',
})
export class CustomMatColumnComponent {
@Input() public columnName: string;
@ContentChild(TemplateRef) public columnTemplate: TemplateRef<any>;
}
然后您的父组件模板将更改为此:
Then your parent component template will change to this:
<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>
您的自定义表格组件需要更改.而不是接收 templateNameList
,它需要根据需要从 ContentChildren
生成它.
Your custom table component needs to be changed. instead of receiving the templateNameList
it needs to generate it from the ContentChildren
on demand.
@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示例,以便您可以在实践中看到它.
If you don't like this second approach you can still use what I suggested in the original sample in the same way. The only difference is how it looks in the template.I created also a StackBlitz sample so you can see it in practice.
这篇关于使用angular5中的contentChildren获取多个ng-template ref值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!