Angular Material Table - 行索引不一致
Angular Material Table - Row Index is Inconsistent
tl;博士; - 请参阅下面的答案;解决方案是提供trackBy函数
我有一个使用 mat-table 的简单时间表应用程序,用户可以在其中添加一周中每一天的工作时间。
按特定顺序添加和删除添加行无效。
重现问题的步骤:
添加一个新行,将星期日设置为 1
添加另一行并将星期日设置为 2
删除第一行 - 现在将有一行 Sunday=2 符合预期
现在添加另一个新行。
预期行为 - 添加新行且第一行星期日值仍为 2。
实际行为 - 添加了新行,但第一行已重置为 0,这就像我得到了 2 行新行。
我知道有许多推荐的方法来设置 table 数据源 - 我已经尝试了所有方法,但没有任何区别。下面显示的版本会在您每次 add/remove 时重新实例化数据源,但我也尝试过设置 datasource.data 属性 但这没有区别。底层数组总是包含预期值,只是在上面概述的场景中,table 似乎没有正确绑定。
我已经从这个应用程序中剥离了核心功能,并制作了一个简单的问题演示并上传到这里:Github。这是 Angular 8 和 angular-material 8。同样的事情发生在 angular 7.
这是标记
<div class='timesheets'>
<div>Timesheets</div>
<form #formControl="ngForm" autocomplete="off">
<mat-table [dataSource]="timesheetEntryDataSource" #timesheetTable matSort>
<ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef class='actions'>
<button mat-icon-button color="primary" (click)="add()" [disabled]="!!attendance?.approvalDate">
<mat-icon aria-label="Add">add</mat-icon>
</button>
</mat-header-cell>
<mat-cell *matCellDef="let entry" class='actions'>
<button mat-icon-button color="primary" (click)="remove(entry)" >
<mat-icon aria-label="Delete">delete</mat-icon>
</button>
</mat-cell>
<mat-footer-cell *matFooterCellDef class='actions'></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="sunday">
<mat-header-cell *matHeaderCellDef>Sunday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.sundayHours" name="sunday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="monday">
<mat-header-cell *matHeaderCellDef>Monday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.mondayHours" name="monday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="tuesday">
<mat-header-cell *matHeaderCellDef>Tuesday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.tuesdayHours" name="tuesday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="wednesday">
<mat-header-cell *matHeaderCellDef>Wednesday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.wednesdayHours" name="wednesday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="thursday">
<mat-header-cell *matHeaderCellDef>Thursday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.thursdayHours" name="thursday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="friday">
<mat-header-cell *matHeaderCellDef>Friday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.fridayHours" name="friday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="saturday">
<mat-header-cell *matHeaderCellDef>Saturday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.saturdayHours" name="saturday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="timesheetColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: timesheetColumns;"></mat-row>
<mat-footer-row *matFooterRowDef="timesheetColumns"></mat-footer-row>
</mat-table>
</form>
</div>
代码:
import { Component } from '@angular/core';
import { MatTableDataSource } from '@angular/material';
export class TimesheetEntry {
mondayHours:string = '0'
tuesdayHours:string = '0'
wednesdayHours:string = '0'
thursdayHours:string = '0'
fridayHours:string = '0'
saturdayHours:string = '0'
sundayHours:string = '0'
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'tabledemo';
timesheetEntryDataSource: MatTableDataSource<TimesheetEntry>;
timesheetColumns = ['actions', 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' ];
tmpTimesheet:TimesheetEntry[] = [];
constructor( ) { }
ngOnInit() {
this.timesheetEntryDataSource = new MatTableDataSource<TimesheetEntry>([]);
}
add(){
this.tmpTimesheet.push(new TimesheetEntry());
this.timesheetEntryDataSource = new MatTableDataSource<TimesheetEntry>(this.tmpTimesheet.slice());
}
remove(exception: TimesheetEntry){
this.tmpTimesheet.splice(this.tmpTimesheet.findIndex(e => e === exception),1);
this.timesheetEntryDataSource = new MatTableDataSource<TimesheetEntry>(this.tmpTimesheet.slice());
}
typeComparatorFn(select, option) {
return select && option ? select.code === option.code : select === option;
}
}
问题是使用索引创建唯一名称属性(在表单标记中使用 ngModel 时需要名称属性 - 我正在使用模板化表单)。
*matCellDef="let entry;let i = index;"
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.sundayHours" name="sunday{{i}}">
似乎添加和删除项目 to/from 数据源不会一致地更新更新索引,在我的场景中,我最终得到两个具有相同索引的条目。
解决方案是指定一个 trackBy 函数,如 documentation
中指定的
就我而言,我将其添加到 .ts 文件中:
trackByFn(index, item) {
return index;
}
在标记中:
<mat-table [dataSource]="tmpTimesheet" [trackBy]="trackByFn">
这似乎可以解决问题,但这本来可以更清楚地记录下来,因为我确定 adding/removing 行 to/from mat-table 在 Angular模板化表单并不是一个不常见的操作,我敢肯定还有许多其他场景需要处理索引。乍一看,使用 let i= index 结构似乎暗示所有工作都已为您完成!
tl;博士; - 请参阅下面的答案;解决方案是提供trackBy函数
我有一个使用 mat-table 的简单时间表应用程序,用户可以在其中添加一周中每一天的工作时间。
按特定顺序添加和删除添加行无效。
重现问题的步骤:
添加一个新行,将星期日设置为 1 添加另一行并将星期日设置为 2 删除第一行 - 现在将有一行 Sunday=2 符合预期 现在添加另一个新行。 预期行为 - 添加新行且第一行星期日值仍为 2。 实际行为 - 添加了新行,但第一行已重置为 0,这就像我得到了 2 行新行。
我知道有许多推荐的方法来设置 table 数据源 - 我已经尝试了所有方法,但没有任何区别。下面显示的版本会在您每次 add/remove 时重新实例化数据源,但我也尝试过设置 datasource.data 属性 但这没有区别。底层数组总是包含预期值,只是在上面概述的场景中,table 似乎没有正确绑定。
我已经从这个应用程序中剥离了核心功能,并制作了一个简单的问题演示并上传到这里:Github。这是 Angular 8 和 angular-material 8。同样的事情发生在 angular 7.
这是标记
<div class='timesheets'>
<div>Timesheets</div>
<form #formControl="ngForm" autocomplete="off">
<mat-table [dataSource]="timesheetEntryDataSource" #timesheetTable matSort>
<ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef class='actions'>
<button mat-icon-button color="primary" (click)="add()" [disabled]="!!attendance?.approvalDate">
<mat-icon aria-label="Add">add</mat-icon>
</button>
</mat-header-cell>
<mat-cell *matCellDef="let entry" class='actions'>
<button mat-icon-button color="primary" (click)="remove(entry)" >
<mat-icon aria-label="Delete">delete</mat-icon>
</button>
</mat-cell>
<mat-footer-cell *matFooterCellDef class='actions'></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="sunday">
<mat-header-cell *matHeaderCellDef>Sunday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.sundayHours" name="sunday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="monday">
<mat-header-cell *matHeaderCellDef>Monday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.mondayHours" name="monday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="tuesday">
<mat-header-cell *matHeaderCellDef>Tuesday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.tuesdayHours" name="tuesday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="wednesday">
<mat-header-cell *matHeaderCellDef>Wednesday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.wednesdayHours" name="wednesday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="thursday">
<mat-header-cell *matHeaderCellDef>Thursday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.thursdayHours" name="thursday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="friday">
<mat-header-cell *matHeaderCellDef>Friday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.fridayHours" name="friday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<ng-container matColumnDef="saturday">
<mat-header-cell *matHeaderCellDef>Saturday</mat-header-cell>
<mat-cell *matCellDef="let entry;let i = index;" class="hours">
<mat-form-field>
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.saturdayHours" name="saturday{{i}}">
</mat-form-field>
</mat-cell>
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="timesheetColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: timesheetColumns;"></mat-row>
<mat-footer-row *matFooterRowDef="timesheetColumns"></mat-footer-row>
</mat-table>
</form>
</div>
代码:
import { Component } from '@angular/core';
import { MatTableDataSource } from '@angular/material';
export class TimesheetEntry {
mondayHours:string = '0'
tuesdayHours:string = '0'
wednesdayHours:string = '0'
thursdayHours:string = '0'
fridayHours:string = '0'
saturdayHours:string = '0'
sundayHours:string = '0'
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'tabledemo';
timesheetEntryDataSource: MatTableDataSource<TimesheetEntry>;
timesheetColumns = ['actions', 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' ];
tmpTimesheet:TimesheetEntry[] = [];
constructor( ) { }
ngOnInit() {
this.timesheetEntryDataSource = new MatTableDataSource<TimesheetEntry>([]);
}
add(){
this.tmpTimesheet.push(new TimesheetEntry());
this.timesheetEntryDataSource = new MatTableDataSource<TimesheetEntry>(this.tmpTimesheet.slice());
}
remove(exception: TimesheetEntry){
this.tmpTimesheet.splice(this.tmpTimesheet.findIndex(e => e === exception),1);
this.timesheetEntryDataSource = new MatTableDataSource<TimesheetEntry>(this.tmpTimesheet.slice());
}
typeComparatorFn(select, option) {
return select && option ? select.code === option.code : select === option;
}
}
问题是使用索引创建唯一名称属性(在表单标记中使用 ngModel 时需要名称属性 - 我正在使用模板化表单)。
*matCellDef="let entry;let i = index;"
<input min=0 max=23.75 pattern="^(?!0\d)\d+(?:[.](?:25|5|75|0)0*)?$" required #today="ngModel" matInput [(ngModel)]="entry.sundayHours" name="sunday{{i}}">
似乎添加和删除项目 to/from 数据源不会一致地更新更新索引,在我的场景中,我最终得到两个具有相同索引的条目。
解决方案是指定一个 trackBy 函数,如 documentation
中指定的就我而言,我将其添加到 .ts 文件中:
trackByFn(index, item) {
return index;
}
在标记中:
<mat-table [dataSource]="tmpTimesheet" [trackBy]="trackByFn">
这似乎可以解决问题,但这本来可以更清楚地记录下来,因为我确定 adding/removing 行 to/from mat-table 在 Angular模板化表单并不是一个不常见的操作,我敢肯定还有许多其他场景需要处理索引。乍一看,使用 let i= index 结构似乎暗示所有工作都已为您完成!