如何通过嵌套的 ng-containers & ng-templates 传递数据?

fra*_*jay 9 javascript angular

我正在创建一个数据表模块,我的目标是特定的实现。我希望能够导入模块,并像这样使用它的组件:

randomcomponent.component.html

    <datatable [data]="tableData">
      <datatable-column>
        <ng-template let-row="row">
          <label> {{ row.value }} </label>
        </ng-template>
      </datatable-column>
    </datatable>
Run Code Online (Sandbox Code Playgroud)

以下是数据表模块中的组件:

datatable.component.html (<datatable>)

<table class="datatable">
  <thead>
    <tr>
      <th *ngFor="let header of tableData?.headers">
        {{ header?.title }}
      </th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let row of tableData?.data">
       <ng-container *ngTemplateOutlet="template; context: { row: row }"></ng-container>
    </tr>
  </tbody>
  <tfoot>
  </tfoot>
</table>
Run Code Online (Sandbox Code Playgroud)

column.component.html (<datatable-column>)

<ng-template let-row="row">
  <ng-container *ngTemplateOutlet="template; context: { row: row }"></ng-container>
</ng-template>
Run Code Online (Sandbox Code Playgroud)

虽然数据没有呈现。我使用了以前的方法,ng-content但无法遍历内容。仅显示 1 个。我怎样才能最终得到我正在寻找的实现?我应该寻求不同的方法吗?

更新:提供的是我的 StackBlitz https://stackblitz.com/edit/angular-4esuor

bry*_*n60 4

这里的关键是,您需要找到外部模板内的嵌套模板,然后迭代这些外部模板。

您可以在这里寻求另一个指令的帮助(推荐但不是强制性的,请参见下面的注释),我们将其称为datatable-cell指令,它非常简单,声明并导出它:

@Directive({
  selector: '[datatable-cell]'
})
export class DatatableCellDirective {
  constructor(public templateRef: TemplateRef<any>) { }
}
Run Code Online (Sandbox Code Playgroud)

仅公开模板引用,不执行任何其他操作。

接下来,如果使用该指令,则将其添加到模板的所有列中(let-row如果使用上下文,则只需声明...或者如果需要显式上下文,则$implicit坚持使用):let-row="row"

<datatable-column>
  <ng-template datatable-cell let-row>
    <label> {{ row.value }} </label>
  </ng-template>
</datatable-column>
Run Code Online (Sandbox Code Playgroud)

在您的列组件中,使用该指令来查找并公开单元格模板ContentChild

@ContentChild(DatatableCellDirective)
cell: DatatableCellDirective
Run Code Online (Sandbox Code Playgroud)

笔记:您可以在没有DatatableCellDirective, 的情况下执行此操作,只需@ContentChild(TemplateRef) cell: TemplateRef<any>在列中设置,但这有点不太灵活,我建议使用该指令(如下所述)。

在表格组件中,找到ContentChildren有很多列,并获取它们的单元格模板:

@ContentChildren(DatatableColumnComponent) 
columns: QueryList<DatatableColumnComponent>;
cellTemplates: TemplateRef<any>[] = []

// content children available in this hook
ngAfterContentInit() {
  this.cellTemplates = this.columns.toArray().map(c => c.cell.templateRef);

  // like this if not using the cell directive and using TemplateRef directly
  // this.cellTemplates = this.columns.toArray().map(c => c.cell);
}
Run Code Online (Sandbox Code Playgroud)

然后,在表 html 中,迭代数据行和单元格模板,因为您需要为每行中的每一列呈现一个单元格,将上下文设置到$implicit此处,但使用任何合适的上下文(例如{row: row}代替显式上下文):

<tr *ngFor="let row of tableData?.data">
  <ng-container *ngFor="let tpl of cellTemplates">
    <ng-container *ngTemplateOutlet="tpl; context: {$implicit: row}"></ng-container>
  </ng-container>
</tr>
Run Code Online (Sandbox Code Playgroud)

你也可以把<td></td>标签放入列迭代内,以具有强制列宽的实际表格单元格。

这是一个相当灵活的系统...您可以设想对其进行扩展,以便您可以在列中提供自定义列标题以及datatable-header方便的指令,并且您可以在列和表格组件中找到这些模板,类似于查找单元格的方式,以便迭代并显示标题行中的内容。使用该指令还允许您使用除ng-template使用该指令还允许您使用构建模板(如td,并且可能允许您通过输入向单元格添加更多上下文。

闪电战: https ://stackblitz.com/edit/angular-gj5ean

小编辑,意识到ContentChildren是可迭代的,因此您实际上可以跳过ngAfterContentInit表组件中的部分,执行以下操作:

@ContentChildren(DatatableColumnComponent) 
columns: QueryList<DatatableColumnComponent>;
Run Code Online (Sandbox Code Playgroud)

只需在模板中执行此操作...

<ng-container *ngFor="let col of columns">
  <ng-container *ngTemplateOutlet="col.cell.templateRef; context: {$implicit: row}"></ng-container>
</ng-container>
Run Code Online (Sandbox Code Playgroud)

如果列本身发生变化(例如添加/删除列),它可以提供更好的变化检测