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");
}
}
}