Get multiple ng-template ref values using contentChildren in angular 5

前端 未结 5 2541
孤城傲影
孤城傲影 2021-02-20 15:11

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 ea

5条回答
  •  北海茫月
    2021-02-20 15:57

    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) { }
    
      @Input('tableColumn') columnName: string;
    }
    

    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:

    
       

    this template is for column firstName

    this template is for column lastName

    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.

    Here is the updated component:

    import { Component, OnInit, ContentChildren, QueryList, TemplateRef, AfterContentInit } from '@angular/core';
    
    
    @Component({
      selector: 'my-table',
      template: `

    This is the temp component

    `, styleUrls: ['./temp.component.scss'] }) export class TempComponent implements OnInit,AfterContentInit { constructor() { } @ContentChildren(TableColumnDirective) columnList: QueryList; 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;
    }
    

    Then your parent component template will change to this:

    
      
        
          
    {{item?.processedName}}
    {{item?.status | TextCaseConverter}}
    {{item?.lastname}}

    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 implements OnChanges, AfterViewInit {
      @ContentChildren(CustomMatColumnComponent) columnDefinitions: QueryList;
      templateNameList: { [key: string]: TemplateRef } {
        if (this.columnDefinitions != null) {
          const columnTemplates: { [key: string]: TemplateRef } = {};
          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 = new EventEmitter();
      @Output() onSort: EventEmitter = new EventEmitter();
      displayedColumns: string[] = [];
      tableDataSource: TableDataSource;
      @ViewChild(MatSort) sort: MatSort;
    
      constructor() {
          this.tableDataSource = new TableDataSource();
      }
    
      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 });
      }
    }
    

    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.

提交回复
热议问题