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

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

let-* $implicit in Angular template

Синтаксис let-* позволяет объявить переменную в шаблоне , использования ключа $implicit позволяет устанавливать значение по-умолчанию для объявленной переменной.

29 августа 2018 г. в Angular

RxJs Subjects

Выдержки из доклада Андрея Алексеева (Tinkoff) про RxJs (Subject, Behaviour Subject, Replay Subject, Async Subject). Применение в Angular.

Angular. Редирект по условию

Пример условного перенаправления пользователя в зависимости от некого количества

  • 0 - dashboard
  • 1 - карточка
  • 2 и более - список
09 января 2019 г. в Angular

Angular & MVVM

  • Model - just file like user.class.ts
  • View - HTML template of component
  • ViewModel - Typescript part of a component
14 апреля 2019 г. в Angular