Angular OnPush 策略不适用于 ngFor - 总是重新创建所有子项
Angular OnPush strategy not working with ngFor - always recreates all children
我正在尝试构建一个小型框架,该框架根据提供的 JSON 描述表单结构的数据呈现表单。数据中指定的每个表单元素在运行时由一个 angular 组件表示。
表单中的元素可以使用 'container' 表单元素进行嵌套。相应的组件在数组上使用 *ngFor 循环。
我选择使用 OnPush Change Detection,效果很好 - except 用于容器组件,当数组项发生变化,因此当对@inputs 进行变化检测时,没有什么可比较的。
我试着在这里做了一个简化的例子,希望你明白这一点。
https://stackblitz.com/github/rpnoll1977/TestNgArray
为了能够分辨出哪些部分是重新创建的,我添加了一些输入字段。您可以输入一些随机文本。当您点击按钮时,一个新元素将添加到列表中。列表中的所有元素都被重新创建——而不是其他元素,尤其是与 *ngfor 并行的输入。
在 *ngFor 中提供跟踪功能后,我能够稍微调试一下。在 Angular 的 DefaultIterableDiffer.check 中,它总是将 mayBeDirty=true 设置为要比较的记录等于 null。
实际上我做了另一个例子,而不是 *ngfor 使用一系列 *ngIfs(条件是数组的长度小于 *ngif 的位置)。这显示了正确的行为,仅重新呈现添加的项目。
您应该注意如何更新模型以及如何使用 trackBy
功能。
您希望 ngFor 模型中的项目保持不变。
tracker(index:number, item:any):any{
return item;
}
对于深层嵌套的项目也是如此:
controls: [
...this.model.controls[0].controls, <=== they stay the same
{id:"labelX", type:"l"},
]
this.model.controls[0].controls[0] === newModel.controls[0].controls[0]); => true
但上面的一个容器并非如此:
var newModel = {
...this.model,
controls: [ <=== here you create a completely new array with new objects
{
...
this.model.controls[0] === newModel.controls[0] // false
所以解决方案是为 trackBy
使用一些标识符,在您的情况下是 id
:
tracker(index:number, item:any):any{
return item.id;
}
你在trackBy
函数中使用的tracker
函数是错误的
是
tracker(index:number, item:any):any{
return item;/// wrong
}
应该是
https://stackblitz.com/edit/github-qhynwq?file=src%2Fapp%2Fapp.component.ts
tracker(index:number, item:any):any{
return item.id;
}
我正在尝试构建一个小型框架,该框架根据提供的 JSON 描述表单结构的数据呈现表单。数据中指定的每个表单元素在运行时由一个 angular 组件表示。
表单中的元素可以使用 'container' 表单元素进行嵌套。相应的组件在数组上使用 *ngFor 循环。
我选择使用 OnPush Change Detection,效果很好 - except 用于容器组件,当数组项发生变化,因此当对@inputs 进行变化检测时,没有什么可比较的。
我试着在这里做了一个简化的例子,希望你明白这一点。
https://stackblitz.com/github/rpnoll1977/TestNgArray
为了能够分辨出哪些部分是重新创建的,我添加了一些输入字段。您可以输入一些随机文本。当您点击按钮时,一个新元素将添加到列表中。列表中的所有元素都被重新创建——而不是其他元素,尤其是与 *ngfor 并行的输入。
在 *ngFor 中提供跟踪功能后,我能够稍微调试一下。在 Angular 的 DefaultIterableDiffer.check 中,它总是将 mayBeDirty=true 设置为要比较的记录等于 null。
实际上我做了另一个例子,而不是 *ngfor 使用一系列 *ngIfs(条件是数组的长度小于 *ngif 的位置)。这显示了正确的行为,仅重新呈现添加的项目。
您应该注意如何更新模型以及如何使用 trackBy
功能。
您希望 ngFor 模型中的项目保持不变。
tracker(index:number, item:any):any{
return item;
}
对于深层嵌套的项目也是如此:
controls: [
...this.model.controls[0].controls, <=== they stay the same
{id:"labelX", type:"l"},
]
this.model.controls[0].controls[0] === newModel.controls[0].controls[0]); => true
但上面的一个容器并非如此:
var newModel = {
...this.model,
controls: [ <=== here you create a completely new array with new objects
{
...
this.model.controls[0] === newModel.controls[0] // false
所以解决方案是为 trackBy
使用一些标识符,在您的情况下是 id
:
tracker(index:number, item:any):any{
return item.id;
}
你在trackBy
函数中使用的tracker
函数是错误的
是
tracker(index:number, item:any):any{
return item;/// wrong
}
应该是
https://stackblitz.com/edit/github-qhynwq?file=src%2Fapp%2Fapp.component.ts
tracker(index:number, item:any):any{
return item.id;
}