Angular. DoCheck и KeyValueDiffers

Если @Input является примитивом или объектом, у которого меняется ссылка, то изменения обнаруживаются в хуке ngOnChanges. Если объект мутирует, то такое изменение можно отследить через хук ngDoCheck. Вызывается чрезмерное количество раз, поэтому следует избегать его использования, чтобы не было проблем с производительностью.

Допустим имеется @Input() объект с одним свойством:

interface Data { name: string };

Отследить его изменение можно следующим образом:

  • Сохранить объект при изменении ссылки в ngOnChanges
  • Проверять name в ngDoCheck
export class DataComponent {
  @Input() data: Data;
  private localData: Data;

  constructor(private cdr: ChangeDetectorRef) {}

  ngOnChanges() {
    this.localData = this.data;
  }

  ngDoCheck() {
    if (this.localData.name !== this.data.name) {
      this.cdr.markForCheck();
    }
  }
}

Если интерфейс объекта более сложный или количество свойств неизвестно, то надо проходить по всем свойствам и их сравнивать. Это можно сделать с помощью функций сравнения объектов, например equals или isEqual. Однако в Angular есть встроенный механизм, который называется Differs.

  • IterableDiffer - сравнение итерируемых объектов, например массивов
  • KeyValueDiffers - сравнение свойств объектов

Первоначально требуется создать начальное значение, например пустой объект {}

constructor(private differs: KeyValueDiffers) {
    this.differ = this.differs.find({}).create();
}

Далее производить сравнение

const changes = this.differ.diff({ name: 'Вася' });
if (changes) {
    // объект изменился
}

Пример использования

import {
  Component,
  ChangeDetectorRef,
  Input,
  KeyValueDiffers,
  DoCheck,
  ChangeDetectionStrategy
} from "@angular/core";

@Component({
  selector: "app-hello",
  templateUrl: "./hello.component.html",
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class HelloComponent implements DoCheck {
  @Input() data: { name: string };
  private differ = this.differs.find({}).create();

  constructor(
    private cdr: ChangeDetectorRef,
    private differs: KeyValueDiffers
  ) {}

  ngDoCheck() {
    const changes = this.differ.diff(this.data);
    if (changes) {
      console.log("Object changed");
      changes.forEachChangedItem(item => console.log("Changed", item));
      changes.forEachAddedItem(item => console.log("Added", item));
      changes.forEachRemovedItem(item => console.log("Removed", item));
      this.cdr.markForCheck();
    } else {
      console.log("No changes");
    }
  }
}

StackBlitz

Похожие записи

RxJS Pipeable Operators

Начиная с версии rxjs 5.5 операторы вместо цепочки вызовов применяются как параметры функции pipe.

Angular. Can't set breakpoints in VS Code

Вариант решения проблемы, когда не срабатывают точки остановки при разработке Angular приложений в редакторе VS Code
10 апреля 2018 г. в Angular

Angular. Functions & getters в шаблонах

На каждый цикл механизма обнаружения изменений выполняется метод из шаблона. Если этого надо избежать, то следует использовать pure pipe или результат выполнения присвоить свойству компонента