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

前端 未结 5 1202
情话喂你
情话喂你 2020-12-13 06:57

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

I

相关标签:
5条回答
  • 2020-12-13 07:23

    You can import 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();
    }
    
    0 讨论(0)
  • 2020-12-13 07:24

    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.

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

    0 讨论(0)
  • 2020-12-13 07:24

    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:

    First, you need to reference your element like this:

    in HTML:

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

    And in your TS file of that component:

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

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

    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:

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

    // 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:

    And just housekeeping stuff, OnDestroy:

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

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

    0 讨论(0)
  • 2020-12-13 07:31

    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 SimpleChanges object 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)

    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
    
    0 讨论(0)
  • 2020-12-13 07:45

    You can still check component's field members value change by KeyValueDiffers via 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);
        });
      }
    }
    

    see demo.

    0 讨论(0)
提交回复
热议问题