Angular2 ngFor OnPush变化检测与阵列突变

问题描述:

我有,我们改变了项目从角的传统变化检测到OnPush优化渲染速度数据表组件(angular2-data-table)项目。Angular2 ngFor OnPush变化检测与阵列突变

一旦新的变化检测战略的实施,一个bug被提起时引用数据对象,例如对象的属性更新突变表不更新参考:https://github.com/swimlane/angular2-data-table/issues/255。对于这种类型的需求,可以使用强大的用例,如内联编辑或将外部数据更改为大型数据集合(如股票行情)中的单个属性。

在努力解决这个问题,我们增加了一个叫做trackByProp定制trackBy属性检查器。参考:commit。不幸的是,这个解决方案没有解决这个问题。

demo page下实时重新加载,你可以看到在上面的提交运行引用的演示,但不更新表,直到你点击触发变化检测。

组件的结构是这样的:

这些组件的Table > Body > Row Group > Row > Cell

都实现OnPush。我在行设置器中使用getters/setter来触发页面重新计算,如here所示。

对于那些实现这种模式的人来说,我们希望继续使用OnPush更改检测,但是,作为一个具有多个消费者的开源项目,人们可能会为屏幕上可见的行值争辩某种自定义检查功能。

所有这一切说,trackBy不触发行单元格的值的变化检测,什么是实现这一目标的最佳方式是什么?

+0

问题应该包括允许重现该问题的代码。 –

Angular2变化检测不检查数组或对象的内容。

哈克解决方法是突变

this.myArray.push(newItem); 
this.myArray = this.myArray.slice(); 

这样this.myArray是指不同的数组实例和角度会认识到变化后,只需要创建一个数组的一个副本。

另一种方法是使用IterableDiffer(阵列)或KeyValueDiffer(的对象)

// inject a differ implementation 
constructor(differs: KeyValueDiffers) { 
    // store the initial value to compare with 
    this.differ = differs.find({}).create(null); 
} 

@Input() data: any; 

ngDoCheck() { 
    var changes = this.differ.diff(this.data); // check for changes 
    if (changes && this.initialized) { 
    // do something if changes were found 
    } 
} 

参见https://github.com/angular/angular/blob/14ee75924b6ae770115f7f260d720efa8bfb576a/modules/%40angular/common/src/directives/ng_class.ts#L122

+0

'ngDoCheck'在我调用像click这样的事件之前不会运行。另外,我注意到'* ngFor'已经存在自定义跟踪,为什么不能检测到它? https://github.com/angular/angular/blob/master/modules/%40angular/common/src/directives/ng_for.ts#L113 – amcdnl

+0

我也在研究如何使用Rx:http://*.com/questions/32683488/rxjs-observing-object-updates-and-changes – amcdnl

+0

如上所述,问题应该提供足够的信息来重现。我的回答只是一个拍摄而已。一个重生的重建将是有益的。也许这是一个区域问题。 –

你可能想使用markForCheck方法从ChangeDetectorRef


我有一个类似的问题,在这里我有一个包含大量数据和组件重新检查他们都在每一个变更检测周期是不是一种选择。但是,当我们从URL看一些属性,我们相应地在视图中改变的东西,与onPush我们的观点不刷新(自动)。

在构造函数

因此,使用DI获得的changeDetectorRef一个实例: constructor(private changeDetectorRef: ChangeDetectorRef)

何地您需要触发changeDetection: this.changeDetectorRef.markForCheck();

+0

我在父页面试过这个,没有结果。也许它需要在渲染它们的组件级别上完成,但在这一点上我不知道有什么改变:S – amcdnl

+0

我想某种自定义API来修改属性可以做到这一点?但从API的角度来看,这不太好。 – amcdnl

我也遇到了类似的问题,即优化我的应用程序的性能,我不得不使用012Dev.OnPush策略的。所以,我在我的两个父组件注入它,以及我的孩子组件的构造中,实例changeDetectorRef

export class Parentcomponent{ 
     prop1; 

     constructor(private _cd : ChangeDetectorRef){ 
      } 
     makeXHRCall(){ 
     prop1 = ....something new value with new reference; 
     this._cd.markForCheck(); // To force angular to trigger its change detection now 
     } 
    } 

同样的子组件,注入的实例changeDetectorRef

export class ChildComponent{ 
    @Input myData: myData[]; 
    constructor(private _cd : ChangeDetectorRef){ 
      } 
     changeInputVal(){ 
     this.myData = ....something new value with new reference; 
     this._cd.markForCheck(); // To force angular to trigger its change detection now 
     } 
    } 

角度变化检测在每个异步功能上触发: -

  • 任何DOM事件,如点击,提交,鼠标悬停。
  • 任何XHR呼叫
  • 像setTimeout的任何计时器()等

所以,这种减慢应用程序,因为即使当我们拖动鼠标,角度被触发changeDetection的。对于跨越多个组件的复杂应用程序,这可能是主要的性能瓶颈,因为角度具有这种树状父项到子项更改检测策略。 为了避免这种情况,最好使用OnPush策略,并在我们看到有参考变化的地方强制触发角度变化检测。其次,在OnPush策略中,应该非常小心,只有在对象引用发生变化时,才会触发变化,而不仅仅是对象属性值,即角度变化“意味着”新引用“。

对于如: -

obj = { a:'value1', b:'value2'}' 
    obj.a = value3; 

的“A”在“OBJ”可能有变化,但OBJ仍然指向相同的参考,所以角度变化检测不会在这里引发的属性值(除非你力它); 要创建一个新的引用,需要将该对象克隆到另一个对象中并相应地分配其属性。

做进一步的了解,了解Immmutable数据结构,改变检测here