Обнаружение изменения Angular2 ngFor OnPush с мутациями массива

у меня есть компонент таблицы данных ( angular2-данные-таблица ) проект, в котором мы изменили проект с традиционного обнаружения изменений Angular на OnPush для оптимизации скорости рендеринга.

как только была реализована новая стратегия обнаружения изменений, была подана ошибка, ссылающаяся на таблицу не обновляется, когда объект данных мутирует, например, ссылка обновления свойств объекта:https://github.com/swimlane/angular2-data-table/issues/255. Сильный прецедент может быть сделан для этого типа необходимости в таких вещах, как встроенное редактирование или внешние изменения данных в одном свойстве в большой коллекции данных, такой как биржевой тикер.

в попытке решить эту проблему, мы добавили пользовательский trackBy проверки свойств под названием trackByProp. Ссылка: фиксация. К сожалению, это решение не решило вопроса.

на демо-страницы в разделе live reloading вы можете увидеть демо, на которое ссылается выше зафиксировать запуск, но не обновлять таблицу, пока вы не нажмете, таким образом, вызывая обнаружение изменений.

структура компонента что-то вроде:

Table > Body > Row Group > Row > Cell

все эти компоненты реализуютOnPush. Я использую геттеры/сеттеры в сеттере строк для запуска перерасчетов страниц, как показано здесь.

мы хотели бы остаться с OnPushобнаружение изменений для тех, кто использует этот шаблон, однако, как проект с открытым исходным кодом с несколько потребителей можно утверждать, что какая-то пользовательская функция проверки для видимых значений строк на экране.

все, что сказал, trackBy не вызывает обнаружение изменений в значениях ячеек строк, каков наилучший способ выполнить это?

3 ответов


обнаружение изменений Angular2 не проверяет содержимое массивов или объектов.

хаки обходной путь состоит в том, чтобы просто создать копию массива после мутации

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

таким образом this.myArray ссылается на другой экземпляр массива, и Angular распознает изменение.

другой подход-использовать 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


вы можете использовать markForCheck метод ChangeDetectorRef.


у меня аналогичная проблема, где у меня есть компонент, который содержит много данных и перепроверить их все на каждом цикле обнаружения изменений-это не вариант. Но как мы смотрим некоторые свойства из URL-адресом и мы меняем вещи в представлении соответственно, с onPush наше представление не обновляется (автоматически).

Итак, в вашем конструкторе используйте DI получить пример changeDetectorRef : constructor(private changeDetectorRef: ChangeDetectorRef)

и везде, где вам нужно вызвать changeDetection : this.changeDetectorRef.markForCheck();


Я тоже столкнулся с подобной проблемой, где для оптимизации производительности моего приложения мне пришлось использовать changeDetection.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, такое как click, submit, mouseover.
  • любой вызов XHR
  • любые таймеры, такие как setTimeout () и т. д.

таким образом, это замедляет приложение, потому что даже когда мы перетаскиваем мышь, angular запускает changeDetection. Для сложного приложения, охватывающего несколько компонентов, это может быть основным узким местом производительности, так как angular имеет этот вид дерева от родителя к потомку изменить стратегию обнаружения. Чтобы избежать этого, лучше использовать стратегию OnPush и принудительно запускать обнаружение изменений angular, где мы видим, что есть ссылочное изменение.

во-вторых, в стратегии OnPush следует быть очень осторожным, что он будет вызывать изменения только при изменении ссылки на объект, а не только значения свойства объекта i.e в случае углового изменения "означает" новую ссылку".

например:-

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

значение свойства 'a' в "obj" может иметь изменение, но obj по-прежнему указывает на ту же ссылку, поэтому обнаружение угловых изменений не вызовет здесь (Если вы не заставите его); Чтобы создать новую ссылку, необходимо клонировать объект в другой объект и назначить его свойства соответственно.

для дальнейшего понимания, читать о Immmutable структур данных, обнаружения изменений здесь