如何从类属性 TypeScript 中侦听值更改 - Angular
Posted
技术标签:
【中文标题】如何从类属性 TypeScript 中侦听值更改 - Angular【英文标题】:How to listen for value changes from class property TypeScript - Angular 【发布时间】:2018-09-08 04:28:44 【问题描述】:在 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 兼容的解决方案
【问题讨论】:
你可以使用 Observable 参考:***.com/questions/41915625/… @Elliott08 在您分享的链接中,答案指定它仅在我们使用@Input
时才有效:至于您对 @Input() 的评论,它有效好吧,当父组件与子交互时,在这种情况下这对您不起作用
【参考方案1】:
您仍然可以通过KeyValueDiffers
通过DoCheck
lifehook 检查组件的字段成员值变化。
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。
【讨论】:
这会起作用,但我会说这不是最好的解决方案,因为 lifeCycle 挂钩真的经常运行。 @ValeSteveDoCheck
仅在更改检测后运行。
好吧,如果您查看它,更改检测会经常运行 :)
在我看来,这个解决方案是最接近我想要的,也是最合乎逻辑的,它利用了 Angular 框架的优点,而无需重新发明***并重写 angular 已经定义的内容。不过,我对 ChangeDetectorRef 或 DoCkeck 的使用有点困惑! github.com/angular/angular/blob/5.2.9/packages/core/src/…
@LyesCHIOUKH ChangeDetectorRef
让您手动触发角度的变化检测。 DoCheck
是生命钩子,检测到变化后会触发。这就是他们的关系。【参考方案2】:
我认为对您的问题最好的解决方案是使用一个装饰器,该装饰器自动将原始字段替换为一个属性,然后在设置器上,您可以创建一个类似于 angular 创建的 SimpleChanges
对象,以便使用与 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
【讨论】:
感谢您的回复。在我看来,在我们有几个类属性要跟踪的情况下,这个解决方案很难实现。如果您想在两个类属性同时更改时启动操作,您如何使用此解决方案进行设置? @LyesCHIOUKH 有几种方法可以做到这一点,我想到了两种方法:第一种是将通知放在计时器上,而不是立即触发。另一种是使用一些方法调用显式地批量通知。我可以将示例更改为我们的任一版本,第一个版本更易于实现。您对这两种解决方案都感兴趣吗? 感谢您的回复。您如何看待#Pengyy 提出的结合 KeyValueDiffer + DoCheck 的解决方案? @LyesCHIOUKH 它似乎工作正常,而且它似乎是一种更 Angular 的做事方式,在你的情况下这可能是更好的选择。我的解决方案也可以使用几乎相同的外部角度:)。 谢谢@Titian Cernicova-Dragomir。实际上,我会选择最适合 Angular 的解决方案 :)【参考方案3】:如你所说,你可以使用
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 属性时都会触发此方法。
小演示:https://stackblitz.com/edit/angular-n72qlu
【讨论】:
此解决方案有效,但不是最优的。举个例子,假设我们想要跟踪两个变量的变化来触发一个动作!设置起来会更复杂.. 不幸的是,你必须使用 angular 提供的 doCheck【参考方案4】:我想我是来听 DOM 变化的,你可以得到对你的元素所做的任何变化,我真的希望这些提示和技巧能帮助你解决你的问题,遵循以下简单的步骤:
首先,您需要像这样引用您的元素:
在 HTML 中:
<section id="homepage-elements" #someElement>
....
</section>
并且在该组件的 TS 文件中:
@ViewChild('someElement')
public someElement: ElementRef;
第二,你需要创建一个观察者来监听那个元素的变化,你需要把你的组件ts
文件变成implements AfterViewInit, OnDestroy
,然后在那里实现ngAfterViewInit()
(@987654331 @以后有工作):
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
);
您将看到控制台将适用于对该元素的任何更改:
这是另一个示例,您将看到 2 个突变记录被触发并且类发生了变化:
// 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
:
ngOnDestroy(): void
this.changes.disconnect();
最后,您可以查看此参考:Listening to DOM Changes Using MutationObserver in Angular
【讨论】:
感谢您的回复。在我看来,在我们有多个类属性要跟踪的情况下,这个解决方案很难实现! @LyesCHIOUKH 如果您想单独跟踪每个属性,您可以过滤所需的属性并为每个属性添加 observable,这只是提示或提示。例如... mutations.filter((mutation: MutationRecord) => mutation.attributeName == 'myProperty1').forEach( ...
【参考方案5】:
你可以导入 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();
【讨论】:
以上是关于如何从类属性 TypeScript 中侦听值更改 - Angular的主要内容,如果未能解决你的问题,请参考以下文章