web-dev-qa-db-ja.com

クラスプロパティTypeScriptからの値の変更をリッスンする方法-Angular

AngularJSでは、$watch$digest...を使用して変数の変更をリッスンできます。これは、新しいバージョンのAngular(5、6)では不可能です。

Angularでは、この動作はコンポーネントのライフサイクルの一部になりました。

公式のドキュメント、記事、特に 可変オブジェクトでのAngular 5変更検出 をチェックして、 TypeScriptクラス で変数(クラスプロパティ)の変更をリッスンする方法を見つけました=/ 角度

今日提案されているのは:

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_1myProperty_2またはmyProperty_3)の変更を追跡する場合、これは機能しません。

誰かがこの問題を解決するのを手伝ってくれますか?できればAngular 5と互換性のあるソリューション

6
Lyes CHIOUKH

KeyValueDiffersライフフックを介して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);
    });
  }
}

デモを参照してください。

4
Pengyy

あなたの問題に対する最も良い解決策は、元のフィールドをプロパティで自動的に置き換えるデコレータを使用することであり、セッター上で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

あなたが使用できると述べたように

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

2
Vale Steve

DOMの変更を聞いて、要素に変更を加えることができるようになったと思います。次の簡単な手順に従って、これらのヒントが問題の解決に役立つことを願っています。

最初に、次のように要素を参照する必要があります:

hTMLで:

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

そして、そのコンポーネントのTSファイルで:

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

Second、その要素の変更をリッスンするオブザーバを作成する必要があります。コンポーネント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
  });
}

コンソールは、その要素の変更に対応します:

enter image description here

これは、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);
      }
    });
  }
);

コンソールログ:

enter image description here

そして、ハウスキーピングなど、OnDestroy

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

最後に、このリファレンスを参照できます。 AngularでMutationObserverを使用してDOMの変更をリッスンする

1
Al-Mothafar

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
Sultan Khan