7

我正在尝试通过使用在 turbo 表元素中注入标题ngTemplateOutlet模板,如以下代码片段所示:

    <p-table [value]="cars1">
      <ng-template pTemplate="header">
        <ng-container *ngTemplateOutlet="defaultHeader"></ng-container>
      </ng-template>
      <ng-template pTemplate="body" let-rowData>
        <tr>
          <td *ngFor="let col of cols">
            {{rowData[col.field]}}
          </td>
        </tr>
      </ng-template>
    </p-table>

可以在这里看到前面提到的带有排序的标题模板:

    <ng-template #defaultHeader>
      <tr>
        <th *ngFor="let col of cols" [pSortableColumn]="col.field">
            {{col.header}}
            <p-sortIcon [field]="col.field"></p-sortIcon>
        </th>
      </tr>
    </ng-template>

页面加载后,会抛出以下错误:

Error: StaticInjectorError(AppModule)[ScrollableView -> Table]: 
StaticInjectorError(Platform: core)[ScrollableView -> Table]: 
NullInjectorError: No provider for Table

Converting circular structure to JSON

这是一个有效的StackBlitz 示例

由于在 an 中使用模板标题ngTemplateOutlet是我的用例的要求,所以我想指出我在这里做错了什么?

谢谢 !

4

2 回答 2

4

我知道这是一个老问题,但我遇到了同样的问题,在任何地方都找不到任何有用的东西。

我发现如果我将“表”类添加到我的包装组件的提供程序中,错误就会消失。但是我还需要 DomHandler、ObjectUtils 和 TableService 的提供程序。

这使错误消失了,并允许将模板传递到包装器中并向下传递到 p 表中。但是,pSortableColumn 和 pSelectableRow 现在只能在包装组件内的默认模板内工作。

没有错误,因为将 Table 类放入我的包装器的提供程序列表中会给 pSortableColumn 和 pSelectableRow 一个新的表实例以满足它们的依赖关系。他们有效地选择和排序一个永远不会被渲染的空表。

我通过将表提供程序更改为 useFactory 来解决此问题。我的工厂函数需要我的包装器组件作为依赖项,然后将它的子 turbo 表作为提供的表返回。所以现在当 pSortableColumn 和 pSelectableRow 请求一个表时,它们会在我的包装器组件中得到正确的表。

    ...
    import { DomHandler } from 'primeng/api';
    import { Table, TableService } from 'primeng/table';
    import { ObjectUtils } from 'primeng/components/utils/objectutils';
    ...

    export function tableFactory(datalist: DataListComponent) {
      return datalist.datatable;
    }
    
    @Component({
      selector: 'app-data-list',
      templateUrl: './data-list.component.html',
      styleUrls: ['./data-list.component.scss'],
      providers: [ DomHandler, ObjectUtils, TableService, {
        provide: Table,
        useFactory: tableFactory,
        deps: [DataListComponent]
      }],
    })
    export class DataListComponent implements OnInit {
      @ViewChild(('datatable')) datatable: Table;
    ...
    }

我应该提到,如果您使用属性从父组件传入模板,而不是 ContentChildren 和 QueryList,则需要在包装组件的内部定义模板,否则您将再次遇到相同的问题。

例如,将模板作为属性传递给包装器内的模板,如下所示

    <wrapper-component [headerTemplate]="defaultHeader">
      <ng-template #defaultHeader>
        ...
      </ng-template>
    </wrapper-component>

但是像这样将模板作为属性传递给包装器之外的模板是行不通的:

    <ng-template #defaultHeader>
      ...
    </ng-template>
    <wrapper-component [headerTemplate]="defaultHeader">
    </wrapper-component>

如果您使用 ContentChildren 和 QueryList,那么您显然需要包装器内的模板。

于 2018-12-28T16:13:50.840 回答
2

这是基于 C. Witt 的回答,但已更新为更新的 primeng(在 Angular 10.1.6 和 PrimeNG 11.3.1 上测试)。

import { Component, ContentChild, Input, TemplateRef, ViewChild } from '@angular/core';
import { Table, TableService } from 'primeng/table';

export function tableFactory(tableComponent: TableComponent) {
  return tableComponent.primengTable;
}

@Component({
  selector: 'my-table',
  templateUrl: './table.component.html',
  styleUrls: ['./table.component.scss'],
  providers: [
    TableService,
    {
      provide: Table,
      useFactory: tableFactory,
      deps: [TableComponent]
    }
  ]
})
export class TableComponent {

  @Input()
  data: any[];

  @ContentChild('header', { static: false })
  public headerTemplate: TemplateRef<any>;
  
  @ContentChild('body', { static: false })
  public bodyTempate: TemplateRef<any>;

  @ViewChild('primengTable', { static: true }) public primengTable: Table;
}
<div class="my-table">
    <p-table #primengTable [value]="data">
      <ng-template pTemplate="header">
        <ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
      </ng-template>
      <ng-template pTemplate="body" let-item let-rowIndex="rowIndex">
        <ng-container *ngTemplateOutlet="bodyTempate;context:{ $implicit: item, rowIndex: rowIndex }"></ng-container> 
      </ng-template>
    </p-table>
</div>

注意:primengTable公开只是因为我想从外部访问它以进行复杂的操作。此代码仍然可以工作,它可以是私有的。

用法:

<my-table [data]="data">
    <ng-template #header>
        <tr>
            <th>Index</th>
            <th pSortableColumn="name">Name <p-sortIcon field="name"></p-sortIcon></th>
            <th pSortableColumn="age">Age <p-sortIcon field="age"></p-sortIcon></th>
        </tr>
    </ng-template>
    <ng-template #body let-item let-rowIndex="rowIndex">
        <tr>
            <td>{{ rowIndex }}</td>
            <td>{{ item.name }}</td>
            <td>{{ item.age }}</td>
        </tr>
    </ng-template>
</my-table>
于 2021-05-01T22:40:09.910 回答