使用contentChildren以角度5获取多个ng-template ref值

问题描述 投票:2回答:5

我正在尝试将多个ng模板传递给我的可重用组件(my-table组件),即内容投影。现在,我需要获取每个传递的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);
  }
}

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>

我可以为每个同伴创建指令,但是列的任何内容都不会更改,因此指令路由将不起作用。我认为,组件用户将以模板ref值作为列标题值传递每个列模板,例如,如果用户为“ firstName”列传递ng-template,则应类似于,

 <ng-template #firstName let-firstname>
            <h1>this template is for column firstName</h1>
        </ng-template> 

并且我需要一种方法来获取所有提供的ng-templates及其引用,以便我知道哪个模板属于哪一列。

angular angular-material
5个回答
2
投票

A Directive是一种很好的解决方案,因此您已经在正确的方向进行思考。指令还支持输入参数,因此您可以将列名或标题指定为指令的参数。也请检查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;
}

正如您所看到的,该指令具有一个输入属性,该属性将接收列名,并且还会注入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>

您的自定义表格组件需要更改。而不是接收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 });
  }
}

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


1
投票

我已经在我的库Easy Angular https://github.com/adriandavidbrand/ngx-ez/tree/master/projects/ngx-ez/src/lib/ez-table中构建了一个表格组件>

每个列都可以通过ViewChild获取模板

@ContentChild(TemplateRef)
template: TemplateRef<any>;

该表使用ContentChildren来获取列

@ContentChildren(EzColumnComponent)
columns: QueryList<EzColumnComponent>;

并且表组件在渲染时将当前项目与上下文一起传递

<ng-container *ngTemplateOutlet="column.template || defaultColumTemplate;context:{ $implicit: item, index: i }"></ng-container>

并且使用类似

<ez-table [data]="data">
  <ez-column heading="Heading" property="prop">
    <ng-template let-item>
      Use item view variable in template here
    </ng-template>
  </ez-column>
<ez-table>

这里是其工作原理的演示

https://stackblitz.com/edit/angular-npn1p1

此表有很多内容,但是所有源代码都在GitHub上。


1
投票

还有另一种创建自定义表组件的方法。您可以访问整个行,而不必只显示列。因此,您可以直接控制整个列。


0
投票

我在角形材质表组件的顶部创建了以下自定义表组件。


0
投票

我不得不构建许多使用Angular Material的MatTable的表组件,从长远来看,我决定通过构建一个动态且可重用的基表来节省一些时间。在讨论如何向其添加特定功能之前,我已经添加了一些有关如何启动和运行最小的动态可重用表的上下文/思考过程。

© www.soinside.com 2019 - 2024. All rights reserved.