Angular6 Materials al6错误: “只能有一个默认行,没有 when 谓词函数。”

我试图从 https://material.angular.io/components/table/examples实现 Table with expandable rows,但是我得到了这个错误:

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

这个错误源于这样一个事实: 我有两个 <tr mat-row ...,它们都没有 when 谓词(例如)。如果删除一个,那么错误就会消失。

通过我自己的调查,我发现这个特性对于材料来说是新的。我升级到了“棱角/材料”最新版本和“棱角/cdk”最新版本,但仍然出现了错误。

有人知道我需要做什么才能像文档中的例子那样实现表吗

我的版本:

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
41335 次浏览

It turns out I was missing directive multiTemplateDataRows from <table> tag:

<table mat-table [dataSource]="licenses" multiTemplateDataRows>

i had same problem. i updated @angular/cdk & @angular/material to 6.4.7. And all work fine. Of course you need multiTemplateDataRows attr as well

This error could also appear if the function in your when predicate is undefined. So if you have

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

and hasChild is undefined, you could get that error.

Try like this:

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>

companies.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>

Its working pretty cool

Your template

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

and your component should have a method called hasChild

hasChild = (_: number, node: FlatNode) => node.expandable;