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_1
、myProperty_2
またはmyProperty_3
)の変更を追跡する場合、これは機能しません。
誰かがこの問題を解決するのを手伝ってくれますか?できればAngular 5と互換性のあるソリューション
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);
});
}
}
デモを参照してください。
あなたの問題に対する最も良い解決策は、元のフィールドをプロパティで自動的に置き換えるデコレータを使用することであり、セッター上で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プロパティを設定/取得するたびにこのメソッドが起動するように、変数の設定/取得中に必要なすべてのチェックを実行できます。
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
});
}
コンソールは、その要素の変更に対応します:
これは、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();
}
最後に、このリファレンスを参照できます。 AngularでMutationObserverを使用してDOMの変更をリッスンする
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();
}