In AngularJS können Sie Variablenänderungen mit $watch
, $digest
... abhören, was mit den neuen Versionen von Angular (5, 6) nicht mehr möglich ist.
In Angular ist dieses Verhalten nun Teil des Komponentenlebenszyklus.
Ich habe die offizielle Dokumentation, Artikel und insbesondere die Angular 5-Änderungserkennung für veränderliche Objekte geprüft, um herauszufinden, wie man eine Variable (Klasseneigenschaft) in einer TypeScript-Klasse abhört / Angular
Was heute vorgeschlagen wird, ist:
import { OnChanges, SimpleChanges, DoCheck } from '@angular/core';
@Component({
selector: 'my-comp',
templateUrl: 'my-comp.html',
styleUrls: ['my-comp.css'],
inputs:['input1', 'input2']
})
export class MyClass implements OnChanges, DoCheck, OnInit{
//I can track changes for this properties
@Input() input1:string;
@Input() input2:string;
//Properties what I want to track !
myProperty_1: boolean
myProperty_2: ['A', 'B', 'C'];
myProperty_3: MysObject;
constructor() { }
ngOnInit() { }
//Solution 1 - fired when Angular detects changes to the @Input properties
ngOnChanges(changes: SimpleChanges) {
//Action for change
}
//Solution 2 - Where Angular fails to detect the changes to the input property
//the DoCheck allows us to implement our custom change detection
ngDoCheck() {
//Action for change
}
}
Dies gilt nur für @Input()
-Eigenschaft!
Wenn ich Änderungen der eigenen Eigenschaften meiner Komponente (myProperty_1
, myProperty_2
oder myProperty_3
) verfolgen möchte, funktioniert dies nicht.
Kann mir jemand helfen, dieses Problem zu lösen? Vorzugsweise eine Lösung, die mit Angular 5 kompatibel ist
Sie können die Wertänderung der Feldelemente der Komponenten weiterhin über KeyValueDiffers
über DoCheck
lifehook überprüfen.
import { DoCheck, KeyValueDiffers, KeyValueDiffer } from '@angular/core';
differ: KeyValueDiffer<string, any>;;
constructor(private differs: KeyValueDiffers) {
this.differ = this.differs.find({}).create();
}
ngDoCheck() {
const change = this.differ.diff(this);
if (change) {
change.forEachChangedItem(item => {
console.log('item changed', item);
});
}
}
siehe demo.
Ich denke, die schönste Lösung für Ihr Problem ist die Verwendung eines Dekorators, der das ursprüngliche Feld automatisch durch eine Eigenschaft ersetzt. Anschließend können Sie auf dem Setter ein SimpleChanges
-Objekt erstellen, das dem von angle erstellten ähnlich ist, um denselben Benachrichtigungsrückruf wie für zu verwenden eckig (alternativ können Sie eine andere Schnittstelle für diese Benachrichtigungen erstellen, es gilt jedoch dasselbe Prinzip)
import { OnChanges, SimpleChanges, DoCheck, SimpleChange } from '@angular/core';
function Watch() : PropertyDecorator & MethodDecorator{
function isOnChanges(val: OnChanges): val is OnChanges{
return !!(val as OnChanges).ngOnChanges
}
return (target : any, key: string | symbol, propDesc?: PropertyDescriptor) => {
let privateKey = "_" + key.toString();
let isNotFirstChangePrivateKey = "_" + key.toString() + 'IsNotFirstChange';
propDesc = propDesc || {
configurable: true,
enumerable: true,
};
propDesc.get = propDesc.get || (function (this: any) { return this[privateKey] });
const originalSetter = propDesc.set || (function (this: any, val: any) { this[privateKey] = val });
propDesc.set = function (this: any, val: any) {
let oldValue = this[key];
if(val != oldValue) {
originalSetter.call(this, val);
let isNotFirstChange = this[isNotFirstChangePrivateKey];
this[isNotFirstChangePrivateKey] = true;
if(isOnChanges(this)) {
var changes: SimpleChanges = {
[key]: new SimpleChange(oldValue, val, !isNotFirstChange)
}
this.ngOnChanges(changes);
}
}
}
return propDesc;
}
}
// Usage
export class MyClass implements OnChanges {
//Properties what I want to track !
@Watch()
myProperty_1: boolean = true
@Watch()
myProperty_2 = ['A', 'B', 'C'];
@Watch()
myProperty_3 = {};
constructor() { }
ngOnChanges(changes: SimpleChanges) {
console.log(changes);
}
}
var myInatsnce = new MyClass(); // outputs original field setting with firstChange == true
myInatsnce.myProperty_2 = ["F"]; // will be notified on subsequent changes with firstChange == false
wie gesagt kannst du verwenden
public set myProperty_2(value: type): void {
if(value) {
//doMyCheck
}
this._myProperty_2 = value;
}
und dann, wenn Sie es abrufen müssen
public get myProperty_2(): type {
return this._myProperty_2;
}
auf diese Weise können Sie alle gewünschten Überprüfungen durchführen, während Sie Ihre Variablen einstellen/abrufen. Diese Methoden werden jedes Mal ausgelöst, wenn Sie die Eigenschaft myProperty_2 festlegen bzw. abrufen.
kleine Demo: https://stackblitz.com/edit/angular-n72qlu
Ich glaube, ich bin auf den Weg gekommen, DOM-Änderungen anzuhören, damit Sie Änderungen an Ihrem Element vornehmen können. Ich hoffe wirklich, dass diese Hinweise und Tipps Ihnen dabei helfen werden, Ihr Problem zu lösen, indem Sie den folgenden einfachen Schritt ausführen:
Zuerst müssen Sie Ihr Element folgendermaßen referenzieren:
in HTML:
<section id="homepage-elements" #someElement>
....
</section>
Und in Ihrer TS-Datei dieser Komponente:
@ViewChild('someElement')
public someElement: ElementRef;
Second, Sie müssen einen Beobachter erstellen, um die Elementänderungen zu überwachen. Sie müssen die ts
-Datei der Komponente in implements AfterViewInit, OnDestroy
ändern und dann die ngAfterViewInit()
dort implementieren (OnDestroy
hat später einen Job):
private changes: MutationObserver;
ngAfterViewInit(): void {
console.debug(this.someElement.nativeElement);
// This is just to demo
setInterval(() => {
// Note: Renderer2 service you to inject with constructor, but this is just for demo so it is not really part of the answer
this.renderer.setAttribute(this.someElement.nativeElement, 'my_custom', 'secondNow_' + (new Date().getSeconds()));
}, 5000);
// Here is the Mutation Observer for that element works
this.changes = new MutationObserver((mutations: MutationRecord[]) => {
mutations.forEach((mutation: MutationRecord) => {
console.debug('Mutation record fired', mutation);
console.debug(`Attribute '${mutation.attributeName}' changed to value `, mutation.target.attributes[mutation.attributeName].value);
});
}
);
// Here we start observer to work with that element
this.changes.observe(this.someElement.nativeElement, {
attributes: true,
childList: true,
characterData: true
});
}
Sie werden sehen, dass die Konsole mit allen Änderungen an diesem Element funktioniert:
Dies ist ein weiteres Beispiel, in dem Sie zwei Mutationsdatensätze und die geänderte Klasse sehen:
// This is just to demo
setTimeout(() => {
// Note: Renderer2 service you to inject with constructor, but this is just for demo so it is not really part of the answer
this.renderer.addClass(this.someElement.nativeElement, 'newClass' + (new Date().getSeconds()));
this.renderer.addClass(this.someElement.nativeElement, 'newClass' + (new Date().getSeconds() + 1));
}, 5000);
// Here is the Mutation Observer for that element works
this.changes = new MutationObserver((mutations: MutationRecord[]) => {
mutations.forEach((mutation: MutationRecord) => {
console.debug('Mutation record fired', mutation);
if (mutation.attributeName == 'class') {
console.debug(`Class changed, current class list`, mutation.target.classList);
}
});
}
);
Konsolenprotokoll:
Und nur Housekeeping-Sachen, OnDestroy
:
ngOnDestroy(): void {
this.changes.disconnect();
}
Zum Schluss können Sie diese Referenz betrachten: DOM-Änderungen mit MutationObserver in Angular anhören
Sie können ChangeDetectorRef importieren
constructor(private cd: ChangeDetectorRef) {
// detect changes on the current component
// this.cd is an injected ChangeDetector instance
this.cd.detectChanges();
// or run change detection for the all app
// this.appRef is an ApplicationRef instance
this.appRef.tick();
}