在AngularJS中,我们可以使用$watch
,$digest
来监听变量...这对于新版本的Angular(5,6)来说已不再可能。
在Angular中,此行为现在是组件生命周期的一部分。
我检查了官方文档,文章,特别是Angular 5 change detection on mutable objects,以了解如何在TypeScript class / Angular
今天提议的是:
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
}
}
这仅适用于@Input()
属性!
如果我想跟踪组件自身属性的更改(myProperty_1
,myProperty_2
或myProperty_3
),则无效。
有人可以帮助我解决这个问题吗?优选地,与Angular 5相容的溶液
答案 0 :(得分:9)
您仍然可以通过KeyValueDiffers
lifehook DoCheck
检查组件的字段成员值变化。
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);
});
}
}
请参阅 demo 。
答案 1 :(得分:2)
我认为最好的解决方案是使用装饰器自动用属性替换原始字段,然后在setter上创建一个SimpleChanges
对象,类似于由angular创建的对象,以便使用与angular相同的通知回调(或者您可以为这些通知创建不同的接口,但同样的原则适用)
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
答案 2 :(得分:1)
如你所说,你可以使用
public set myProperty_2(value: type): void {
if(value) {
//doMyCheck
}
this._myProperty_2 = value;
}
然后如果你需要检索它
public get myProperty_2(): type {
return this._myProperty_2;
}
以这种方式,您可以在设置/获取变量时执行所需的所有检查,这样每次设置/获取myProperty_2属性时都会触发此方法。
答案 3 :(得分:1)
我认为我开始倾听DOM更改,您可以对您的元素进行任何更改,我真的希望这些提示和技巧可以帮助您解决问题,遵循以下简单步骤:
首先,您需要像这样引用您的元素:
HTML中的:
<section id="homepage-elements" #someElement>
....
</section>
在该组件的TS文件中:
@ViewChild('someElement')
public someElement: ElementRef;
第二次,您需要创建一个观察者来监听该元素的更改,您需要将您的组件ts
文件设置为implements AfterViewInit, OnDestroy
,然后实现{{1}那里(ngAfterViewInit()
稍后有工作):
OnDestroy
您将看到控制台将对该元素的任何更改起作用:
这是另一个例子,你会看到2个变异记录被触发,并且对于改变的类:
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
});
}
控制台日志:
只是看家事,// 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);
}
});
}
);
:
OnDestroy
最后,您可以查看此参考:Listening to DOM Changes Using MutationObserver in Angular
答案 4 :(得分:0)
您可以导入ChangeDetectorRef
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();
}