web-dev-qa-db-ja.com

Angularで@Input()の値が変化したことを検出する方法

私は親コンポーネント( CategoryComponent )、子コンポーネント( videoListComponent )、そしてApiServiceを持っています。

私はこの作業中の罰金のほとんどを持っています、すなわち、各コンポーネントはjson apiにアクセスして、観測可能なものを通してその関連データを得ることができます。

現在、ビデオリストコンポーネントはすべてのビデオを取得するだけです。これを特定のカテゴリのビデオのみにフィルタしたいのですが、@Input()を介してcategoryIdを子に渡すことでこれを実現しています。

CategoryComponent.html

<video-list *ngIf="category" [categoryId]="category.id"></video-list>

これは動作し、親のCategoryComponentカテゴリが変更されるとcategoryId値が@Input()を介して渡されますが、VideoListComponentでこれを検出し、APIServiceを介して(新しいcategoryIdを使用して)動画配列を再要求する必要があります。

AngularJSでは、変数に対して$watchを実行したはずです。これを処理するための最良の方法は何ですか?

299
Jon Catmull

実際には、入力がAngular 2 +で子コンポーネントで変化したときに検出して対処する方法は2つあります。

  1. あなたは ngOnChanges()ライフサイクルメソッド を使うことができます。
    @Input() categoryId: string;

    ngOnChanges(changes: SimpleChanges) {

        this.doSomething(changes.categoryId.currentValue);
        // You can also use categoryId.previousValue and 
        // categoryId.firstChange for comparing old and new values

    }

ドキュメントリンク: ngOnChanges、SimpleChanges、SimpleChange
デモの例: このplunkerを見てください

  1. 代わりに、 入力プロパティsetter を次のように使用することもできます。
    private _categoryId: string;

    @Input() set categoryId(value: string) {

       this._categoryId = value;
       this.doSomething(this._categoryId);

    }

    get categoryId(): string {

        return this._categoryId;

    }

ドキュメンテーションリンク: ここ を見てください。

デモの例: このplunker を見てください。

どのアプローチに使用しますか?

コンポーネントに複数の入力がある場合、ngOnChanges()を使用すると、ngOnChanges()内ですべての入力に対するすべての変更を一度に取得できます。この方法を使用して、変更された入力の現在値と以前の値を比較し、それに応じてアクションを実行することもできます。

ただし、特定の1つの入力だけが変更されたときに何かをしたい場合(および他の入力を気にしない場合)は、入力プロパティ設定メソッドを使用する方が簡単かもしれません。ただし、この方法では、変更された入力の以前の値と現在の値を比較するための組み込みの方法が提供されません(これはngOnChangesライフサイクルメソッドで簡単に実行できます)。

編集2017-07-25:角度変更検出は、いくつかの回路の下でまだ消えないかもしれません

通常、setterとngOnChangesの両方の変更検出は、親コンポーネントが子に渡すデータを変更するたびに発生します。 データがJSのプリミティブデータ型(string、number、boolean) の場合。ただし、次のシナリオでは起動しません。機能させるには追加のアクションを実行する必要があります。

  1. 親から子へデータを渡すために(JSプリミティブデータ型の代わりに)ネストされたオブジェクトまたは配列を使用している場合、user:muetzerichの回答でも述べられているように、変更検出(setterまたはngchangesを使用)は起動しません。解決策は ここ を見てください。

  2. 角度の範囲外(つまり外部)でデータを変更している場合、角度は変更を認識しません。外部の変更を角度的に認識させ、それによって変更検出をトリガーするために、コンポーネント内でChangeDetectorRefまたはNgZoneを使用する必要があります。 this を参照してください。

436
Alan C. S.

コンポーネントでngOnChanges()ライフサイクルメソッドを使用してください。

ngOnChangesは、データバインドプロパティがチェックされた直後と、ビューおよびコンテンツの子がチェックされる直前に、それらのうちの少なくとも1つが変更されている場合に呼び出されます。

これが Docs です。

90
muetzerich

関数のシグネチャにSimpleChanges型を使用すると、コンパイラとIDEの両方でコンソールにエラーが表示されました。エラーを防ぐには、代わりにシグネチャにanyキーワードを使用してください。

ngOnChanges(changes: any) {
    console.log(changes.myInput.currentValue);
}

編集:

Jonが以下で指摘したように、ドット記法ではなく大括弧表記を使用するときはSimpleChangesシグネチャを使用できます。

ngOnChanges(changes: SimpleChanges) {
    console.log(changes['myInput'].currentValue);
}
25
Darcy

最も安全な方法は@Inputパラメータのshared service代わり を使うことです。また、@Inputパラメータは複雑な入れ子になったオブジェクト型の変更を検出しません。

簡単なサービス例は次の通りです:

Service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class SyncService {

    private thread_id = new Subject<number>();
    thread_id$ = this.thread_id.asObservable();

    set_thread_id(thread_id: number) {
        this.thread_id.next(thread_id);
    }

}

Component.ts

export class ConsumerComponent implements OnInit {

  constructor(
    public sync: SyncService
  ) {
     this.sync.thread_id$.subscribe(thread_id => {
          **Process Value Updates Here**
      }
    }

  selectChat(thread_id: number) {  <--- How to update values
    this.sync.set_thread_id(thread_id);
  }
}

他のコンポーネントでも同様の実装を使用でき、すべてのコンポーネントが同じ共有値を共有します。

8
Sanket Berde

また、親コンポーネント(CategoryComponent)の変更をトリガーするオブザーバブルを持ち、子コンポーネントのサブスクリプションでやりたいことを実行することもできます。 (videoListComponent)

service.ts 
public categoryChange$ : ReplaySubject<any> = new ReplaySubject(1);

-----------------
CategoryComponent.ts
public onCategoryChange(): void {
  service.categoryChange$.next();
}

-----------------
videoListComponent.ts
public ngOnInit(): void {
  service.categoryChange$.subscribe(() => {
   // do your logic
});
}
2
Josf

@Inputの値がプリミティブな値ではない場合に便利なDoCheckというライフサイクルフックがもう1つあることを付け加えたいと思います。

私はInputとして配列を持っているので、内容が変わってもOnChangesイベントは発生しません(Angularが行うチェックは「単純」で深くないので、配列はまだ配列であるにもかかわらず)配列の内容が変更されました。

私はそれから私が私のビューを変更された配列で更新したいかどうか決定するためにいくつかのカスタムチェックコードを実装します。

2
Stevo
  @Input()
  public set categoryId(categoryId: number) {
      console.log(categoryId)
  }

この方法を使ってみてください。お役に立てれば

1
Akshay Rajput
0
Emmanuel John

ここで、ngOnChangesはあなたの入力プロパティが変更されたときに常にトリガーします:

ngOnChanges(changes: SimpleChanges): void {
 console.log(changes.categoryId.currentValue)
}
0
Chirag

NgOnChangeを実装したり、onChange()メソッドを実装したくない場合は、 valueChangesイベント 、ETCで特定の項目の変更を購読することもできます。

 myForm= new FormGroup({
first: new FormControl()   

;));

this.myForm.valueChanges.subscribe(formValue => {
  this.changeDetector.markForCheck();
});

この宣言で使用するために書かれたmarkForCheck()

  changeDetection: ChangeDetectionStrategy.OnPush
0
user1012506