Javascript 如何从类属性 TypeScript 中侦听值更改 - Angular

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/49552258/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:24:05  来源:igfitidea点击:

How to listen for value changes from class property TypeScript - Angular

javascriptangulartypescriptecmascript-6angular5

提问by Lyes CHIOUKH

In AngularJS, we can listen variable change using $watch, $digest... which is no longer possible with the new versions of Angular (5, 6).

在 AngularJS 中,我们可以使用$watch, $digest...来监听变量变化,这在新版本的 Angular (5, 6) 中不再可能。

In Angular, this behaviour is now part of the component lifecycle.

在 Angular 中,这种行为现在是组件生命周期的一部分。

I checked on the official documention, articles and especially on Angular 5 change detection on mutable objects, to find out how to listen to a variable (class property) change in a TypeScript class/ Angular

我查看了官方文档、文章,尤其是关于可变对象的 Angular 5 更改检测,以了解如何监听TypeScript 类/ Angular 中的变量(类属性)更改

What is proposed today is :

今天提议的是:

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
  }
}

This is only true for @Input()property !

这仅适用于@Input()财产!

If I want to track changes of my component's own properties (myProperty_1, myProperty_2or myProperty_3), this will not work.

如果我想跟踪组件自身属性(myProperty_1,myProperty_2myProperty_3)的更改,这将不起作用。

Can someone help me to solve this problematic ? Preferably a solution that is compatible with Angular 5

有人可以帮我解决这个问题吗?最好是与 Angular 5 兼容的解决方案

采纳答案by Pengyy

You can still check component's field members value change by KeyValueDiffersvia DoChecklifehook.

您仍然可以KeyValueDiffers通过DoChecklifehook检查组件的字段成员值更改。

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

see demo.

演示

回答by Titian Cernicova-Dragomir

I think the nicest solution to your issue is to use a decorator that replaces the original field with a property automatically, then on the setter you can create a SimpleChangesobject similar to the one created by angular in order to use the same notification callback as for angular (alternatively you could create a different interface for these notifications, but the same principle applies)

我认为对您的问题最好的解决方案是使用装饰器自动用属性替换原始字段,然后在设置器上您可以创建一个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

回答by Al-Mothafar

I think I came into the way to listen to DOM changes that you can get any changes that do to your element, I really hope these hints and tips will help you to fix your problem, following the following simple step:

我想我开始聆听 DOM 更改,您可以获得对元素所做的任何更改,我真的希望这些提示和技巧可以帮助您解决问题,遵循以下简单步骤:

First, you need to reference your element like this:

首先,您需要像这样引用您的元素:

in HTML:

在 HTML 中:

<section id="homepage-elements" #someElement>
....
</section>

And in your TS file of that component:

在该组件的 TS 文件中:

@ViewChild('someElement')
public someElement: ElementRef;

Second, you need to create an observer to listen to that element changes, you need to make your component tsfile to implements AfterViewInit, OnDestroy, then implement that ngAfterViewInit()there (OnDestroyhas a job later):

其次,您需要创建一个观察者来监听该元素的变化,您需要将组件ts文件设置为implements AfterViewInit, OnDestroy,然后在ngAfterViewInit()那里实现(OnDestroy稍后有工作):

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

You will see the console will work with any changes on that element:

您将看到控制台将处理该元素的任何更改:

enter image description here

在此处输入图片说明

This is another example here that you will see 2 mutation records fired and for the class that changed:

这是此处的另一个示例,您将看到 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);
      }
    });
  }
);

Console log:

控制台日志:

enter image description here

在此处输入图片说明

And just housekeeping stuff, OnDestroy:

而只是家政的东西,OnDestroy

ngOnDestroy(): void {
  this.changes.disconnect();
}

Finally, you can look into this Reference: Listening to DOM Changes Using MutationObserver in Angular

最后,您可以查看此参考资料:在 Angular 中使用 MutationObserver 监听 DOM 更改

回答by Vale Steve

as said you can use

如上所述,您可以使用

public set myProperty_2(value: type): void {
 if(value) {
  //doMyCheck
 }

 this._myProperty_2 = value;
}

and then if you need to retrieve it

然后如果你需要检索它

public get myProperty_2(): type {
  return this._myProperty_2;
}

in that way you can do all the checks that you want while setting/ getting your variables such this methods will fire every time you set/get the myProperty_2 property.

通过这种方式,您可以在设置/获取变量时执行所需的所有检查,这样每次设置/获取 myProperty_2 属性时都会触发此方法。

small demo: https://stackblitz.com/edit/angular-n72qlu

小演示:https: //stackblitz.com/edit/angular-n72qlu

回答by Sultan Khan

You can import ChangeDetectorRef

您可以导入 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();
}