Angular6 Material6 Error: "There can only be one default row without a when predicate function."
Angular6 Material6 Error: "There can only be one default row without a when predicate function."
我正在尝试从 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 谓词(根据示例)。如果我删除一个,错误就会消失。
根据我自己的调查,我发现此功能是 Material 的新功能。我升级到@angular/material@latest 和@angular/cdk@latest 但我仍然遇到错误。
有谁知道我必须做什么才能实现文档示例中的 table? 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
原来我缺少 <table>
标签中的指令 multiTemplateDataRows
:
<table mat-table [dataSource]="licenses" multiTemplateDataRows>
我有同样的问题。我将@angular/cdk & @angular/material 更新到 6.4.7。一切正常。当然你也需要 multiTemplateDataRows 属性
如果 when
谓词中的函数未定义,也可能出现此错误。所以如果你有
<mat-tree-node *matTreeNodeDef="let node; when: hasChild">
和 hasChild
未定义,您可能会遇到该错误。
这样试试:
包装器-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`;
}
}
包装器-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>
它的工作很酷
您的模板
mat-tree-node(*matTreeNodeDef="let node;when: hasChild", matTreeNodePadding)
并且您的组件应该有一个名为 hasChild
的方法
hasChild = (_: number, node: FlatNode) => node.expandable;
我正在尝试从 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 谓词(根据示例)。如果我删除一个,错误就会消失。
根据我自己的调查,我发现此功能是 Material 的新功能。我升级到@angular/material@latest 和@angular/cdk@latest 但我仍然遇到错误。
有谁知道我必须做什么才能实现文档示例中的 table? 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
原来我缺少 <table>
标签中的指令 multiTemplateDataRows
:
<table mat-table [dataSource]="licenses" multiTemplateDataRows>
我有同样的问题。我将@angular/cdk & @angular/material 更新到 6.4.7。一切正常。当然你也需要 multiTemplateDataRows 属性
如果 when
谓词中的函数未定义,也可能出现此错误。所以如果你有
<mat-tree-node *matTreeNodeDef="let node; when: hasChild">
和 hasChild
未定义,您可能会遇到该错误。
这样试试:
包装器-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`;
}
}
包装器-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>
它的工作很酷
您的模板
mat-tree-node(*matTreeNodeDef="let node;when: hasChild", matTreeNodePadding)
并且您的组件应该有一个名为 hasChild
hasChild = (_: number, node: FlatNode) => node.expandable;