使用 angular 5 中的 contentChildren 获取多个 ng-template ref 值

Aka*_*ash 12 angular-material angular

我试图将多个传递ng-template给我的可重用component(我的表组件),内容投影。现在我需要获取每个传递的参考值,ng-template以便我可以使用该值来知道为哪一列传递了哪个模板。基本上,我正在创建一个可重用的表组件(在 Angular 材料表之上),用户可以在其中为每列传递一个单独的模板。

请建议 - 或者有没有更好的方法来做到这一点?

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

应用程序组件.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>
Run Code Online (Sandbox Code Playgroud)

我可以为每一列创建指令,但没有列可能会改变,因此指令路由将不起作用。我在想,组件用户将使用模板引用值作为列标题值传递每个列模板,例如,如果用户传递的ng-template是“firstName”列,它应该是这样的,

 <ng-template #firstName let-firstname>
     <h1>this template is for column firstName</h1>
 </ng-template> 
Run Code Online (Sandbox Code Playgroud)

我需要一种方法来获取所有提供ng-template的 ref 以便我知道哪个模板属于哪个列。

Ale*_*esD 16

ADirective是一个很好的方法,因此您已经在朝着正确的方向思考。指令还支持输入参数,因此您可以指定列名称或标题作为指令的参数。另请查看官方文档以获取更多详细信息。

这是使用此方法的示例指令:

import { Directive, TemplateRef, Input } from '@angular/core';

@Directive({
  selector: '[tableColumn]'
})
export class TableColumnDirective {

  constructor(public readonly template: TemplateRef<any>) { }

  @Input('tableColumn') columnName: string;
}
Run Code Online (Sandbox Code Playgroud)

如您所见,该指令具有一个输入属性,该属性将接收列名,并且它还会注入 ,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>
Run Code Online (Sandbox Code Playgroud)

在组件中,您然后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());
  }

}
Run Code Online (Sandbox Code Playgroud)

这是一种稍微不同的方法,也许你更喜欢这个。由于您提供了更多信息,我现在将基于您的自定义表格示例。

您可以创建一个接受内容的指令,并将模板指定为内容。这是一个示例实现:

@Directive({
  selector: 'custom-mat-column',
})
export class CustomMatColumnComponent {
  @Input() public columnName: string;
  @ContentChild(TemplateRef) public columnTemplate: TemplateRef<any>;
}
Run Code Online (Sandbox Code Playgroud)

然后你的父组件模板会变成这样:

<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>
Run Code Online (Sandbox Code Playgroud)

您的自定义表格组件需要更改。而不是接收templateNameList它需要从ContentChildren按需生成它。

@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 });
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您不喜欢第二种方法,您仍然可以以相同的方式使用我在原始示例中建议的方法。唯一的区别是它在模板中的外观。我还创建了一个StackBlitz 示例,以便您可以在实践中看到它。