为什么带有过滤器管道的 ngFor 会导致无限循环?
Why ngFor with filter pipe cause infinite loop?
我有这种情况。
复选框组件
<section
*ngFor="let group of model.groups | myfilter:term; let index = index">
<div>
<mat-checkbox
[checked]="group.allChecked"
[ngModel]="group.allChecked"
color="primary">
{{ group.name }}
</mat-checkbox>
</div>
<div>
<ul>
<li *ngFor="let checkbox of groups.checkboxes;">
<mat-checkbox
[checked]="checkbox.check"
[(ngModel)]="checkbox.check"
color="primary">
{{ checkbox.displayName }}
</mat-checkbox>
</li>
</ul>
</div>
</section>
在第二个组件中我有
<mat-form-field
appearance="outline">
<mat-label>
Search by
</mat-label>
<input
matInput
type="text"
[(ngModel)]="filter">
<mat-icon matPrefix fontIcon="icon-search"></mat-icon>
</mat-form-field>
<app-checkbox-group
[datasource]="claims"
[term]="filter"
>
</app-checkbox>
和这个管道
@Pipe({
name: 'roleFilter',
pure: false
})
export class myfilter implements PipeTransform {
transform(groups: [], term: string): [] {
if (!term) {
return groups;
}
return groups
.map(obj => {
return Object.assign({}, obj, {
checkboxes: obj.checkboxes.filter(el => el.displayName.includes(term))
})
})
.filter(obj => obj.checkboxes.length > 0)
}
}
groups 是这样的数据集
[
{
name: 'Group 1',
allChecked: false,
isIndeterminate: false,
checkboxes: [
{
check: true,
displayName: 'first',
name: 'first',
id: '1',
isMandatory: false
},
{
check: false,
displayName: 'second',
name: 'second',
id: '2',
isMandatory: false
},
{
check: false,
displayName: 'third',
name: 'third',
id: '3',
isMandatory: false
},
{
check: false,
displayName: 'fourth',
name: 'fourth',
id: '4',
isMandatory: false
},
{
check: false,
displayName: 'fifth',
name: 'fifth',
id: '5',
isMandatory: false
},
{
check: false,
displayName: 'sixth',
name: 'sixth',
id: '6',
isMandatory: false
},
{
check: false,
displayName: 'seventh',
name: 'seventh',
id: '7',
isMandatory: false
},
{
check: false,
displayName: 'eighth',
name: 'eighth',
id: '8',
isMandatory: false
},
]
}
]
当我开始在输入过滤器上打字时,为了减少数据集,如果我开始打字时使用的字母与任何 displayName 都不匹配,则所有组都将被隐藏,并且它按预期工作。
当我开始输入与复选框的某些显示名称 属性 匹配的字母时,问题就出现了。
我不明白为什么,但是所有页面都冻结并且地图功能被无限次调用。
问题似乎出在
Object.assign
在地图方法中。因为如果我改变这一行
obj.checkboxes = checkboxes: obj.checkboxes.filter(el => el.displayName.includes(term))
它有效,但用过滤后的复选框替换原来的复选框。
根据@Andrei 的评论
try to add trackByIndex(idx) {return idx;} method and pass it to both ngFor insatnces like this *ngFor="let checkbox of groups.checkboxes; trackBy: trackByIndex"
解决方案是在两个 *ngFor 指令上使用 trackBy 管道。
我有这种情况。 复选框组件
<section
*ngFor="let group of model.groups | myfilter:term; let index = index">
<div>
<mat-checkbox
[checked]="group.allChecked"
[ngModel]="group.allChecked"
color="primary">
{{ group.name }}
</mat-checkbox>
</div>
<div>
<ul>
<li *ngFor="let checkbox of groups.checkboxes;">
<mat-checkbox
[checked]="checkbox.check"
[(ngModel)]="checkbox.check"
color="primary">
{{ checkbox.displayName }}
</mat-checkbox>
</li>
</ul>
</div>
</section>
在第二个组件中我有
<mat-form-field
appearance="outline">
<mat-label>
Search by
</mat-label>
<input
matInput
type="text"
[(ngModel)]="filter">
<mat-icon matPrefix fontIcon="icon-search"></mat-icon>
</mat-form-field>
<app-checkbox-group
[datasource]="claims"
[term]="filter"
>
</app-checkbox>
和这个管道
@Pipe({
name: 'roleFilter',
pure: false
})
export class myfilter implements PipeTransform {
transform(groups: [], term: string): [] {
if (!term) {
return groups;
}
return groups
.map(obj => {
return Object.assign({}, obj, {
checkboxes: obj.checkboxes.filter(el => el.displayName.includes(term))
})
})
.filter(obj => obj.checkboxes.length > 0)
}
}
groups 是这样的数据集
[
{
name: 'Group 1',
allChecked: false,
isIndeterminate: false,
checkboxes: [
{
check: true,
displayName: 'first',
name: 'first',
id: '1',
isMandatory: false
},
{
check: false,
displayName: 'second',
name: 'second',
id: '2',
isMandatory: false
},
{
check: false,
displayName: 'third',
name: 'third',
id: '3',
isMandatory: false
},
{
check: false,
displayName: 'fourth',
name: 'fourth',
id: '4',
isMandatory: false
},
{
check: false,
displayName: 'fifth',
name: 'fifth',
id: '5',
isMandatory: false
},
{
check: false,
displayName: 'sixth',
name: 'sixth',
id: '6',
isMandatory: false
},
{
check: false,
displayName: 'seventh',
name: 'seventh',
id: '7',
isMandatory: false
},
{
check: false,
displayName: 'eighth',
name: 'eighth',
id: '8',
isMandatory: false
},
]
}
]
当我开始在输入过滤器上打字时,为了减少数据集,如果我开始打字时使用的字母与任何 displayName 都不匹配,则所有组都将被隐藏,并且它按预期工作。 当我开始输入与复选框的某些显示名称 属性 匹配的字母时,问题就出现了。 我不明白为什么,但是所有页面都冻结并且地图功能被无限次调用。
问题似乎出在
Object.assign
在地图方法中。因为如果我改变这一行
obj.checkboxes = checkboxes: obj.checkboxes.filter(el => el.displayName.includes(term))
它有效,但用过滤后的复选框替换原来的复选框。
根据@Andrei 的评论
try to add trackByIndex(idx) {return idx;} method and pass it to both ngFor insatnces like this *ngFor="let checkbox of groups.checkboxes; trackBy: trackByIndex"
解决方案是在两个 *ngFor 指令上使用 trackBy 管道。