简体   繁体   English

Angular2 ngFor OnPush 更改检测与阵列突变

[英]Angular2 ngFor OnPush Change Detection with Array Mutations

I have a data table component ( angular2-data-table ) project where we changed the project from Angular's traditional change detection to OnPush for optimized rendering speeds.我有一个数据表组件( angular2-data-table )项目,我们将项目从 Angular 的传统更改检测更改为OnPush以优化渲染速度。

Once the new change detection strategy was implemented, a bug was filed referencing the table is not updating when the data object is mutated such as object's property updates Reference: https://github.com/swimlane/angular2-data-table/issues/255 .一旦实施了新的变更检测策略,当数据对象发生变异(例如对象的属性更新)时,引用表未更新的错误被提交参考: https : //github.com/swimlane/angular2-data-table/issues/ 255 . A strong use case can be made for this type of need for things such as inline editing or external data changes to a single property in a large data collection like a stock ticker.对于此类需求,例如对股票行情等大型数据集合中的单个属性进行内联编辑或外部数据更改,可以为此类需求创建一个强大的用例。

In an effort to resolve the issue, we added a custom trackBy property checker called trackByProp .为了解决这个问题,我们添加了一个名为trackByProp的自定义 trackBy 属性检查器。 Reference: commit .参考: 提交 Unfortunately, this solution did not resolve the matter.不幸的是,这个解决方案并没有解决问题。

On the demo page under live reloading you can see the demo referenced in the above commit running but not updating the table until you click thus triggering change detection.在实时重新加载下的演示页面上,您可以看到上述提交中引用的演示正在运行,但在单击之前不会更新表,从而触发更改检测。

The structure of the component is something like:组件的结构类似于:

Table > Body > Row Group > Row > Cell

all of these components implement OnPush .所有这些组件都实现了OnPush I'm using getters/setters in the row setter to trigger page recalculations like shown here .我使用的getter / setter方法在该行设定器触发页面重新计算像显示在这里

We'd like to stay with the OnPush change detection for those implementing this pattern, however, as a open-source project with multiple consumers one could argue some sort of custom checking function for the visible row values on the screen.对于实现这种模式的人,我们希望继续使用OnPush更改检测,但是,作为一个具有多个消费者的开源项目,人们可能会争论对屏幕上可见行值的某种自定义检查功能。

All that said, trackBy is not triggering change detection in row cell values, what is the best way to accomplish this? trackBytrackBy不会触发行单元格值的更改检测,实现此目的的最佳方法是什么?

Angular2 change detection doesn't check the contents of arrays or object. Angular2 更改检测不检查数组或对象的内容。

A hacky workaround is to just create a copy of the array after mutation一个hacky解决方法是在突变后创建数组的副本

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

This way this.myArray refers a different array instance and Angular will recognize the change.这样this.myArray引用了一个不同的数组实例,Angular 就会识别出这个变化。

Another approach is to use an IterableDiffer (for arrays) or KeyValueDiffer (for objects)另一种方法是使用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
  }
}

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

You might want to use markForCheck method from ChangeDetectorRef .你可能想使用markForCheck方法从ChangeDetectorRef


I do have a similar issue, where I do have a component that contains a lot of data and re-check them all on every change detection cycle is not an option.我确实有一个类似的问题,我确实有一个包含大量数据的组件,并且不能在每个变更检测周期重新检查它们。 But as we watch some properties from URL and we change things in the view accordingly, with onPush our view is not refreshed (automatically).但是当我们从URL观察一些属性并相应地更改视图中的内容时, onPush我们的视图不会(自动)刷新。

So in your constructor, use DI to get an instance of changeDetectorRef : constructor(private changeDetectorRef: ChangeDetectorRef)因此,在您的构造函数中,使用DI获取changeDetectorRef的实例: constructor(private changeDetectorRef: ChangeDetectorRef)

And wherever you need to trigger a changeDetection : this.changeDetectorRef.markForCheck();无论您需要在何处触发 changeDetection : this.changeDetectorRef.markForCheck();

I too faced the similar issue where to optimize my app performance, I had to use the changeDetection.OnPush strategy.我也遇到了优化应用程序性能的类似问题,我不得不使用changeDetection.OnPush策略。 So I injected it in both my parent component as well as my child component's constructor , the instance of changeDetectorRef所以我将它注入到我的父组件以及我的子组件的构造函数中,即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
       }
    }

Similarly in child component, injected the instance of changeDetectorRef同样在子组件中,注入了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
       }
    }

Angular change Detection is triggered on every asynchronous function :-每个异步函数都会触发角度变化检测:-

  • any DOM event like click,submit, mouseover.任何 DOM 事件,如点击、提交、鼠标悬停。
  • any XHR call任何 XHR 电话
  • any Timers like setTimeout(), etc.任何计时器,如 setTimeout() 等。

So, this kind of slows down the app because even when we are dragging the mouse, angular was triggering changeDetection.因此,这种类型会减慢应用程序的速度,因为即使我们拖动鼠标,angular 也会触发 changeDetection。 For a complex app spanning over multiple components, this could be a major performance bottleneck since angular has this tree kind of parent to child change detection strategy.对于跨越多个组件的复杂应用程序,这可能是一个主要的性能瓶颈,因为 angular 具有这种树类型的父子更改检测策略。 To avoid, this it is better we use the OnPush strategy and forcefully trigger angular's change detection where we see there is a reference change.为了避免这种情况,我们最好使用 OnPush 策略并在我们看到有参考变化的地方强行触发 angular 的变化检测。

Secondly, in OnPush strategy, one should be very careful that it will only trigger change when there is a change in object reference and not just the object property value ie in Angular change” means “new reference”.其次,在 OnPush 策略中,应该非常小心,它只会在对象引用发生变化时才会触发变化,而不仅仅是对象属性值,即在 Angular 中,“变化”意味着“新引用”。

For eg:-例如:-

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

The property value of 'a' in 'obj' might have change but obj still points to the same reference, so Angular change detection will not trigger here (unless you force it to); 'obj' 中 'a' 的属性值可能发生了变化,但 obj 仍然指向同一个引用,所以这里不会触发 Angular 变化检测(除非你强制它); To create a new reference , need to clone the object into another object and assign its properties accordingly.要创建新的引用,需要将对象克隆到另一个对象并相应地分配其属性。

for further understanding, read about Immmutable Data structures, change Detection here为了进一步理解,请阅读不可变数据结构, 在此处更改检测

Late Answer but another workaround is by using the spread operator after mutation.. myArr = [...myArr] or myObj = {...myObj} Late Answer 但另一种解决方法是在变异后使用扩展运算符.. myArr = [...myArr]myObj = {...myObj}

This can even be done while mutating: myArr = myMutatingArr([...myArr]) since the parameter is taken as a new reference of an Array, making the variable take a new reference, and therefore the Angular check is called.这甚至可以在变异时完成: myArr = myMutatingArr([...myArr])因为参数被当作数组的新引用,使变量采用新引用,因此调用 Angular 检查。

As mentioned, if you change the reference, the check will be made, the spread operator can be used in any case to do exactly that.如前所述,如果您更改引用,将进行检查,在任何情况下都可以使用扩展运算符来执行此操作。

Be wary though that nested structures of data inside structures of data require the reference change up to the nested level.但请注意,数据结构内的数据嵌套结构需要将引用更改到嵌套级别。 You would have to do an iteration that returns a spread inside spread, as such:您必须进行迭代以返回价差内的价差,如下所示:

myObj = {...myObj, propToChange: { ...myObj.propToChange,
        nestedPropArr: [ ...myObj.propToChange.nestedPropArr ]
    }
}

which might become complicated if you need iteration over objects and such.如果您需要对对象等进行迭代,这可能会变得复杂。 Hope this helps someone!希望这可以帮助某人!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM