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

标签 angular angular-material

我正在尝试将多个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/

相关文章:

dynamic - 无模型的角度 Material 表动态列

angularjs - Angular Material 中的 md 菜单行为

Angular 响应式(Reactive)验证

angular - 如何解决在 Angular 6 中为业力测试抛出的 [object ErrorEvent]

Angular - 为每个请求设置 header

http - angular2 如何将来自 http.get 的响应头数据存储在额外变量中?

angular - 事件绑定(bind) Angular 2

angular - 什么是 Angular 平台浏览器?

angular - 错误 : Cannot reattach ActivatedRouteSnapshot created from a different route

angular - 没有 MatDialogRef 的提供者