Angular6 Material6 Ошибка: может быть только одна строка по умолчанию без функции предиката when.

Я пытаюсь реализовать Table with expandable rows из https://material.angular.io/components/table/examples, но я получаю эту ошибку:

There can only be one default row without a when predicate function.

Эта ошибка возникает из-за того, что у меня есть два <tr mat-row ..., оба без предиката when (как в примере). Если удалить один, ошибка исчезнет.

Из собственного расследования я обнаружил, что эта функция является новой для Материала. Я обновился до @ angular / material @ latest и @ angular / cdk @ latest, но все равно получаю ошибку.

Кто-нибудь знает, что мне нужно сделать, чтобы реализовать таблицу, как в примере из документации? https://material.angular.io/components/table/examples

Мои версии:

Angular CLI: 6.0.8
Node: 8.11.2
OS: win32 x64
Angular: 6.0.4
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.6.8
@angular-devkit/build-angular     0.6.8
@angular-devkit/build-optimizer   0.6.8
@angular-devkit/core              0.6.8
@angular-devkit/schematics        0.6.8
@angular/cdk                      6.3.1
@angular/cli                      6.0.8
@angular/flex-layout              6.0.0-beta.16
@angular/material                 6.3.1
@ngtools/webpack                  6.0.8
@schematics/angular               0.6.8
@schematics/update                0.6.8
rxjs                              6.2.0
typescript                        2.7.2
webpack                           4.8.3

person GoTo    schedule 29.06.2018    source источник


Ответы (5)


Оказывается, мне не хватало директивы multiTemplateDataRows из тега <table>:

<table mat-table [dataSource]="licenses" multiTemplateDataRows>
person GoTo    schedule 29.06.2018
comment
если вы столкнулись с проблемой с "липким" или неопределенным. проверьте определение столбца - person pcnate; 05.12.2018
comment
как в matColumnDef? - person Ben Taliadoros; 17.04.2019
comment
@BenTaliadoros asa вы можете увидеть в теге ‹table› выше, в конце. - person GoTo; 29.05.2019

Эта ошибка также может появиться, если функция в вашем предикате when не определена. Итак, если у вас есть

<mat-tree-node *matTreeNodeDef="let node; when: hasChild">

и hasChild не определено, вы можете получить эту ошибку.

person adamdport    schedule 29.11.2018
comment
Это действительно помогло мне после принятого ответа. Спасибо! - person MaxAxeHax; 12.02.2020

у меня была такая же проблема. Я обновил @ angular / cdk & @ angular / material до 6.4.7. И все нормально работает. Конечно, вам также нужен атрибут multiTemplateDataRows

person Виктор Могилевский    schedule 10.09.2018
comment
Спасибо, multiTemplateDataRows не работал без обновления. - person MDave; 06.12.2018

Попробуйте вот так:

wrapper-table.ts

import { SelectionModel } from '@angular/cdk/collections';
import { AfterContentInit, Component, ContentChildren, Input, QueryList, TemplateRef, ViewChild } from '@angular/core';
import { MatColumnDef, MatHeaderRowDef, MatRowDef, MatTable, MatTableDataSource } from '@angular/material/table';

@Component({
  selector: 'ui-wrapper-table',
  templateUrl: './wrapper-table.component.html',
  styleUrls: ['./wrapper-table.component.scss']
})
export class WrapperTable<T> implements AfterContentInit {

  @ContentChildren(MatHeaderRowDef) headerRowDefs: QueryList<MatHeaderRowDef>;
  @ContentChildren(MatRowDef) rowDefs: QueryList<MatRowDef<T>>;
  @ContentChildren(MatColumnDef) columnDefs: QueryList<MatColumnDef>;

  @ViewChild(MatTable, { static: true }) table: MatTable<T>;

  @Input() columns: string[];

  @Input() dataSource: MatTableDataSource<T>;

  @Input() contextTemplate: TemplateRef<any>;

  @Input() selection: SelectionModel<T>;

  ngAfterContentInit() {
    this.columnDefs.forEach(columnDef => this.table.addColumnDef(columnDef));
    this.rowDefs.forEach(rowDef => this.table.addRowDef(rowDef));
    this.headerRowDefs.forEach(headerRowDef => this.table.addHeaderRowDef(headerRowDef));
  }

  isAllSelected() {
    const numSelected = this.selection && this.selection.selected.length || 0;
    const numRows = this.dataSource && this.dataSource.data.length || 0;
    return numSelected === numRows;
  }
  masterToggle() {
    this.isAllSelected() ?
      this.selection.clear() :
      this.dataSource.data.forEach(row => this.selection.select(row));
  }
  checkboxLabel(row?: T): string {
    if (!row) {
      return `${this.isAllSelected() ? 'select' : 'deselect'} all`;
    }
    return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row`;
  }
}

wrapper-table.html

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
  <ng-content></ng-content>


  <!-- Select Column -->
  <ng-container matColumnDef="select">
    <th mat-header-cell *matHeaderCellDef>
      <mat-checkbox (change)="$event ? masterToggle() : null" [checked]="selection?.hasValue() && isAllSelected()"
        [indeterminate]="selection?.hasValue() && !isAllSelected()" [aria-label]="checkboxLabel()">
      </mat-checkbox>
    </th>
    <td mat-cell *matCellDef="let row">
      <mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? selection.toggle(row) : null"
        [checked]="selection?.isSelected(row)" [aria-label]="checkboxLabel(row)">
      </mat-checkbox>
    </td>
  </ng-container>

  <!-- Id Column -->
  <ng-container matColumnDef="id">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> # </th>
    <td mat-cell *matCellDef="let element"> {{element.id}} </td>
  </ng-container>

  <!-- Created Column -->
  <ng-container matColumnDef="created">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Criação </th>
    <td mat-cell *matCellDef="let element"> {{element.created.toDate() | date}} </td>
  </ng-container>

  <!-- Context Column -->
  <ng-container matColumnDef="context">
    <th mat-header-cell *matHeaderCellDef> Contexto </th>
    <td mat-cell *matCellDef="let element">
      <ng-container *ngTemplateOutlet="contextTemplate;context:{row:element}"></ng-container>
    </td>
  </ng-container>
</table>

company.component.ts

import { SelectionModel } from '@angular/cdk/collections';
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { MatSort, MatTableDataSource } from '@angular/material';
import { CompanyModel, GetAllCompaniesUsecase } from '@apps-waves/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';


@Component({
  selector: 'wave-companies',
  templateUrl: './companies.component.html',
  styleUrls: ['./companies.component.scss']
})
export class CompaniesComponent implements OnInit, OnDestroy {
  displayedColumns: string[] = ['select', 'name', 'companyType', 'created', 'context'];
  dataSource: MatTableDataSource<CompanyModel>;
  selection: SelectionModel<CompanyModel>;

  @ViewChild('sort', { static: false }) sort: MatSort;

  onDestroy = new Subject<void>();


  constructor(
    private getAllCompanies: GetAllCompaniesUsecase
  ) { }

  ngOnInit() {
    this.getAllCompanies.execute()
      .pipe(takeUntil(this.onDestroy))
      .subscribe(companies => {
        this.dataSource = new MatTableDataSource(companies);
        this.selection = new SelectionModel<CompanyModel>(true, []);
        this.dataSource.sort = this.sort;
      })
  }

  onClick(company: CompanyModel) {
    console.log(company);
  }
  ngOnDestroy() {
    this.onDestroy.next();
    this.onDestroy.complete();
  }
}

companies.html

<ui-wrapper-table
  [dataSource]="dataSource"
  [columns]="displayedColumns"
  [selection]="selection"
  matSort #sort="matSort"
  [contextTemplate]="rowContext">

  <!-- Custom column definition to be provided to the wrapper table. -->
  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef> Name </th>
    <td mat-cell *matCellDef="let element"> {{element.name}} </td>
  </ng-container>

  <ng-container matColumnDef="companyType">
    <th mat-header-cell *matHeaderCellDef> Segmento </th>
    <td mat-cell *matCellDef="let element"> {{element.companyType}} </td>
  </ng-container>

  <!-- Custom row definitions to be provided to the wrapper table. -->
  <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns; "></tr>
</ui-wrapper-table>

<ng-template #rowContext let-element="row">
  <button type="button" mat-icon-button (click)="onClick(element)">
    <mat-icon>edit</mat-icon>
  </button>
</ng-template>

<pre>{{selection?.selected | json}}</pre>

Работает довольно круто

person Gui Seek    schedule 15.01.2020

Ваш шаблон

mat-tree-node(*matTreeNodeDef="let node;when: hasChild", matTreeNodePadding)

и ваш компонент должен иметь метод с именем hasChild

hasChild = (_: number, node: FlatNode) => node.expandable;
person Bertwin Romero    schedule 14.04.2020