web-dev-qa-db-ja.com

は何ですか Angular AngularJS $ウォッチと同等?

AngularJSでは、$watch$scope関数を使用してスコープ変数の変更を監視するウォッチャーを指定できました。 Angularで変数の変更(コンポーネント変数など)を監視するのと同じことは何ですか?

193
Erwin

Angular 2では、変更の検出は自動的に行われます... $scope.$watch()および$scope.$digest() R.I.P.

残念ながら、開発ガイドの変更検出セクションはまだ書かれていません( Architecture Overview ページのセクション "The Other Stuff"の下部にプレースホルダーがあります)。

変更検出の仕組みについての私の理解は次のとおりです。

  • Zone.jsは「猿が世界にパッチを当てる」-ブラウザーのすべての非同期APIをインターセプトします(Angularが実行されるとき)。 setTimeout()はモンキーパッチが適用されているため、$timeout...のようなものではなく、コンポーネント内でsetTimeout()を使用できる理由です。
  • 角度は、「変更検出器」のツリーを構築および維持します。コンポーネント/ディレクティブごとに、このような変更検出機能(クラス)が1つあります。 (このオブジェクトにアクセスするには、 ChangeDetectorRef を挿入します。)これらの変更検出器は、Angularがコンポーネントを作成するときに作成されます。ダーティチェックのために、すべてのバインディングの状態を追跡します。これらは、ある意味で、Angular 1が{{}}テンプレートバインディングに設定する自動$watches()に似ています。
    Angular 1とは異なり、変更検出グラフは有向ツリーであり、サイクルを持つことはできません(これにより、以下で説明するように、Angular 2のパフォーマンスが大幅に向上します)。
  • イベントが発生すると(Angularゾーン内)、記述したコード(イベントハンドラーコールバック)が実行されます。共有アプリケーションのモデル/状態やコンポーネントのビューステートなど、必要なデータを更新できます。
  • その後、Zone.jsが追加されたフックにより、Angularの変更検出アルゴリズムを実行します。デフォルトでは(つまり、コンポーネントでonPush変更検出戦略を使用していない場合)、ツリー内のすべてのコンポーネントが1回(TTL = 1)...上から深さ優先順に検査されます。 (まあ、開発モードの場合、変更検出は2回実行されます(TTL = 2)。詳細については ApplicationRef.tick() を参照してください。)すべてのバインディングに対してダーティチェックを実行します。これらの変更検出器オブジェクトを使用します。
    • ライフサイクルフックは、変更検出の一部として呼び出されます。
      監視するコンポーネントデータがプリミティブ入力プロパティ(String、boolean、number)である場合、変更を通知するngOnChanges()を実装できます。
      入力プロパティが参照型(オブジェクト、配列など)であるが、参照が変更されなかった場合(たとえば、既存の配列にアイテムを追加した場合)、ngDoCheck()this SO answer を参照してください)。
      コンポーネントのプロパティおよび/または子孫コンポーネントのプロパティのみを変更する必要があります(単一ツリーウォークの実装-つまり、単方向のデータフローのため)。 a plunker これは違反です。ステートフルパイプは trip you up こちらでも可能です。
  • 見つかったバインディングの変更については、コンポーネントが更新され、DOMが更新されます。これで、変更の検出は終了しました。
  • ブラウザは、DOMの変更を認識し、画面を更新します。

その他の参考資料:

262
Mark Rajcok

この動作は現在、コンポーネントライフサイクルの一部です。

コンポーネントは OnChanges インターフェースにngOnChangesメソッドを実装して、入力の変更にアクセスすることができます。

例:

import {Component, Input, OnChanges} from 'angular2/core';


@Component({
  selector: 'hero-comp',
  templateUrl: 'app/components/hero-comp/hero-comp.html',
  styleUrls: ['app/components/hero-comp/hero-comp.css'],
  providers: [],
  directives: [],

  pipes: [],
  inputs:['hero', 'real']
})
export class HeroComp implements OnChanges{
  @Input() hero:Hero;
  @Input() real:string;
  constructor() {
  }
  ngOnChanges(changes) {
      console.log(changes);
  }
}
85
toskv

自動双方向バインディングに加えて、値が変更されたときに関数を呼び出す場合は、双方向バインディングのショートカット構文をより冗長なバージョンに変更できます。

<input [(ngModel)]="yourVar"></input>

の省略形です

<input [ngModel]="yourVar" (ngModelChange)="yourVar=$event"></input>

(例: http://victorsavkin.com/post/119943127151/angular-2-template-syntax を参照)

あなたはこのようなことをすることができます:

<input [(ngModel)]="yourVar" (ngModelChange)="changedExtraHandler($event)"></input>

63
supermikko

getter functionまたはget accessorを使用して、角度2を監視することができます。

Demo here を参照してください。

import {Component} from 'angular2/core';

@Component({
  // Declare the tag name in index.html to where the component attaches
  selector: 'hello-world',

  // Location of the template for this component
  template: `
  <button (click)="OnPushArray1()">Push 1</button>
  <div>
    I'm array 1 {{ array1 | json }}
  </div>
  <button (click)="OnPushArray2()">Push 2</button>
  <div>
    I'm array 2 {{ array2 | json }}
  </div>
  I'm concatenated {{ concatenatedArray | json }}
  <div>
    I'm length of two arrays {{ arrayLength | json }}
  </div>`
})
export class HelloWorld {
    array1: any[] = [];
    array2: any[] = [];

    get concatenatedArray(): any[] {
      return this.array1.concat(this.array2);
    }

    get arrayLength(): number {
      return this.concatenatedArray.length;
    }

    OnPushArray1() {
        this.array1.Push(this.array1.length);
    }

    OnPushArray2() {
        this.array2.Push(this.array2.length);
    }
}
16
jmvtrinidad

モデルにゲッター関数とセッター関数を使用するもう1つの方法があります。

@Component({
  selector: 'input-language',
  template: `
  …
  <input 
    type="text" 
    placeholder="Language" 
    [(ngModel)]="query" 
  />
  `,
})
export class InputLanguageComponent {

  set query(value) {
    this._query = value;
    console.log('query set to :', value)
  }

  get query() {
    return this._query;
  }
}
11
select

双方向バインディングにしたい場合は、[(yourVar)]を使用できますが、変数が変わるたびにyourVarChangeイベントを実装して呼び出す必要があります。

このような何かが主人公の変化を追跡するために

@Output() heroChange = new EventEmitter();

そして、あなたのヒーローが変わったら、this.heroChange.emit(this.hero);を呼び出してください。

[(hero)]バインディングがあなたのために残りをします

ここの例を見なさい:

http://plnkr.co/edit/efOGIJ0POh1XQeRZctSx?p=preview

5
Mohy Eldeen

あなたのアプリケーションの still がAngularで$parse$eval$watchのような振る舞いを要求するときに試してみてください。

https://github.com/vinayk406/angular-expression-parser

3
Vinay K

これは直接質問に答えるものではありませんが、私はAngularJで$ watchを使用することになるものを解決するために、このStack Overflowの質問にさまざまな機会を与えました。私は現在の答えで説明されているのとは別のアプローチを使用することになりました、そして誰かがそれを有用であると思う場合に備えてそれを共有したいです。

私が$watchに似たようなものを実現するのに使うテクニックは、AngularサービスでBehaviorSubjectこちらのトピックの詳細 )を使い、私のコンポーネントにそれを購読させることです。変更を監視します。これはangularJの$watchに似ていますが、もう少しセットアップと理解が必要です。

私のコンポーネントでは:

export class HelloComponent {
  name: string;
  // inject our service, which holds the object we want to watch.
  constructor(private helloService: HelloService){
    // Here I am "watching" for changes by subscribing
    this.helloService.getGreeting().subscribe( greeting => {
      this.name = greeting.value;
    });
  }
}

私のサービスでは

export class HelloService {
  private helloSubject = new BehaviorSubject<{value: string}>({value: 'hello'});
  constructor(){}
  // similar to using $watch, in order to get updates of our object 
  getGreeting(): Observable<{value:string}> {
    return this.helloSubject;
  }
  // Each time this method is called, each subscriber will receive the updated greeting.
  setGreeting(greeting: string) {
    this.helloSubject.next({value: greeting});
  }
}

これが Stackblitz のデモです。

0
John