web-dev-qa-db-ja.com

AngularJSでは$ scope、$ watch、および$ scope。$をどのように適用できますか?

$scope.$watch$scope.$applyの使い方がわかりません。公式文書は役に立ちません。

私が特に理解していないこと:

  • 彼らはDOMに接続していますか?
  • モデルに対するDOMの変更を更新する方法
  • それらの間の接続点は何ですか?

このチュートリアル を試しましたが、当然のことながら$watch$applyの理解が必要です。

$apply$watchは何をしていますか、そしてどのようにそれらを適切に使用しますか?

1057
ilyo

AngularJSでは、モデルを更新し、ビュー/テンプレートでDOMを「自動的に」(組み込みディレクティブまたはカスタムディレクティブを介して)更新します。

Scopeメソッドである$ applyと$ watchはDOMとは関係ありません。

概念 ページ(「ランタイム」セクション)には、$ダイジェストループ、$ apply、$ evalAsyncキュー、および$ watchリストのかなり良い説明があります。これがテキストに付随する図です。

$digest loop

スコープにアクセスできるコード(通常はコントローラとディレクティブ(それらのリンク関数またはコントローラ、あるいはその両方))がなんであれ、AngularJSがそのスコープに対して評価する「 watchExpression 」を設定できます。この評価はAngularJSがその$ダイジェストループ(特に "$ watch list"ループ)に入るたびに行われます。個々のスコーププロパティを見ることができます、あなたは一緒に2つのプロパティを見るために関数を定義することができます、あなたは配列の長さなどを見ることができます。

AngularJSの双方向データバインディングが有効になっている(つまりng-modelを使用している)、$ httpコールバックが起動するなど、テキストボックスにAngularJSが入力された場合など、$ applyは既に呼び出されているので上の図の "AngularJS"長方形の内側にあります。すべてのwatchExpressionsが評価されます(おそらく複数回 - それ以上の変更が検出されなくなるまで)。

"AngularJSの外部"で問題が発生した場合 - たとえば、ディレクティブでbind()を使用した後にそのイベントが発生し、コールバックが呼び出されたり、jQueryの登録済みコールバックが発生したりします。コールバックコードが、任意の$ watchが監視しているものを変更した場合、$ applyを呼び出してAngularJSの矩形領域に入り、$ digestループが実行されるため、AngularJSはその変更に気付いて魔法を行います。

157
Mark Rajcok

このブログ は例を作成し理解できる説明を作成することすべてをカバーしました。

AngularJSの$scope関数$watch(), $digest()および$apply()は、AngularJSの中心的な関数です。 AngularJSを理解するには、$watch()$digest()、および$apply()を理解することが不可欠です。

ビュー内のどこかから$ scopeオブジェクトの変数にデータバインディングを作成すると、AngularJSは内部的に「ウォッチ」を作成します。監視は、AngularJSが$scope objectの変数の変更を監視することを意味します。フレームワークは変数を「監視」しています。時計は、このテキストの後半で説明する$scope.$watch()関数を使って作成されます。

アプリケーションの重要なポイントで、AngularJSは$scope.$digest()関数を呼び出します。この関数はすべての監視を繰り返し、監視されている変数のいずれかが変更されているかどうかを確認します。監視対象の変数が変更されている場合は、対応するリスナー関数が呼び出されます。リスナー関数は必要な作業をすべて行います。たとえば、監視されている変数の新しい値を反映するようにHTMLテキストを変更します。したがって、$digest()関数はデータバインディングを更新するきっかけとなります。

ほとんどの場合、AngularJSは$ scope。$ watch()および$scope.$digest()関数を呼び出しますが、場合によっては自分で呼び出す必要があります。したがって、それらがどのように機能するのかを知ることは本当に良いことです。

$scope.$apply()関数を使用してコードを実行してから$scope.$digest()を呼び出すと、すべてのウォッチがチェックされ、対応するウォッチリスナー関数が呼び出されます。 $apply()関数はAngularJSを他のコードと統合するときに便利です。

このテキストの残りの部分で、$watch(), $digest()および$apply()関数についてさらに詳しく説明します。

$ watch()

$scope.watch()関数はいくつかの変数の監視を作成します。ウォッチを登録するとき、$watch()関数へのパラメータとして2つの関数を渡します。

  • 価値関数
  • リスナー関数

これが一例です。

$scope.$watch(function() {},
              function() {}
             );

最初の関数は値関数で、2番目の関数はリスナー関数です。

Value関数は監視されている値を返すべきです。その後AngularJSは、watch関数が最後に返した値に対して、返された値を確認できます。そうすることでAngularJSは値が変更されたかどうかを判断できます。これが一例です。

$scope.$watch(function(scope) { return scope.data.myVar },
              function() {}
             );

この例のvalule関数は、$scope変数scope.data.myVarを返します。この変数の値が変わると、異なる値が返され、AngularJSはリスナー関数を呼び出します。

Value関数がスコープをパラメータとしてとることに注意してください(名前に$を付けずに)。このパラメータを介して、value関数は$scopeとその変数にアクセスできます。 value関数は、必要に応じて代わりにグローバル変数を監視することもできますが、ほとんどの場合は$scope変数を監視します。

値が変更された場合、リスナー関数は必要なことは何でもしなければなりません。おそらく、他の変数の内容を変更するか、HTML要素の内容などを設定する必要があります。これが一例です。

$scope.$watch(function(scope) { return scope.data.myVar },
              function(newValue, oldValue) {
                  document.getElementById("").innerHTML =
                      "" + newValue + "";
              }
             );

この例では、HTML要素の内部HTMLを、値を太字にするb要素に埋め込まれた変数の新しい値に設定します。もちろん、コード{{ data.myVar }を使用してこれを行うこともできましたが、これはリスナー関数の内部でできることのほんの一例です。

$ digest()

$scope.$digest()関数は、$scope object内のすべてのウォッチ、およびその子$ scopeオブジェクト(存在する場合)を繰り返し処理します。 $digest()がウォッチを反復処理するときに、各ウォッチのvalue関数を呼び出します。 value関数によって返された値が最後に呼び出されたときに返された値と異なる場合は、そのウォッチのリスナー関数が呼び出されます。

$digest()関数はAngularJSが必要と判断したときに呼び出されます。たとえば、ボタンクリックハンドラが実行された後、またはAJAX呼び出しが戻った後(done()/ fail()コールバック関数が実行された後)。

あなたはAngularJSが$digest()関数をあなたに代わって呼び出さないいくつかの稀なケースに遭遇するかもしれません。通常、データバインディングが表示された値を更新しないことに気付くことでそれを検出します。その場合は$scope.$digest()を呼び出してください。あるいは、代わりに$scope.$apply()を使用することもできます。これについては次のセクションで説明します。

$ apply()

$scope.$apply()関数は実行されるパラメータとして関数を取り、その後$scope.$digest()が内部的に呼び出されます。これにより、すべてのウォッチがチェックされ、したがってすべてのデータバインディングが更新されていることを確認しやすくなります。これが$apply()の例です。

$scope.$apply(function() {
    $scope.data.myVar = "Another value";
});

$apply()関数にパラメータとして渡された関数は、$scope.data.myVarの値を変更します。この関数が終了するとAngularJSは$scope.$digest()関数を呼び出しますので、すべてのウォッチはウォッチされた値の変更をチェックされます。

$watch()$digest()、および$apply()の機能を説明するために、次の例を見てください。

<div ng-controller="myController">
    {{data.time}}

    <br/>
    <button ng-click="updateTime()">update time - ng-click</button>
    <button id="updateTimeButton"  >update time</button>
</div>


<script>
    var module       = angular.module("myapp", []);
    var myController1 = module.controller("myController", function($scope) {

        $scope.data = { time : new Date() };

        $scope.updateTime = function() {
            $scope.data.time = new Date();
        }

        document.getElementById("updateTimeButton")
                .addEventListener('click', function() {
            console.log("update time clicked");
            $scope.data.time = new Date();
        });
    });
</script>

彼の例では、変数の値をHTMLページにマージする補間ディレクティブに$scope.data.time変数をバインドしています。このバインディングは$scope.data.time variableの内部にウォッチを作成します。

例には2つのボタンも含まれています。最初のボタンにはng-clickリスナーが付いています。そのボタンがクリックされると$scope.updateTime()関数が呼び出され、その後AngularJSは$scope.$digest()を呼び出してデータバインディングが更新されるようにします。

2番目のボタンは、コントローラ関数内からそれにアタッチされている標準のJavaScriptイベントリスナを取得します。 2番目のボタンがクリックされると、そのリスナー機能が実行されます。ご覧のとおり、両方のボタンのリスナー関数はほぼ同じですが、2番目のボタンのリスナー関数が呼び出されてもデータバインディングは更新されません。これは、2番目のボタンのイベントリスナが実行された後に$scope.$digest()が呼び出されないためです。したがって、2番目のボタンをクリックすると、$scope.data.time変数の時刻が更新されますが、新しい時刻は表示されません。

これを修正するには、次のようにボタンイベントリスナーの最後の行に$scope.$digest()呼び出しを追加します。

document.getElementById("updateTimeButton")
        .addEventListener('click', function() {
    console.log("update time clicked");
    $scope.data.time = new Date();
    $scope.$digest();
});

ボタンリスナー関数内で$digest()を呼び出す代わりに、次のように$apply()関数を使用することもできます。

document.getElementById("updateTimeButton")
        .addEventListener('click', function() {
    $scope.$apply(function() {
        console.log("update time clicked");
        $scope.data.time = new Date();
    });
});

$scope.$apply()関数がボタンイベントリスナーの内側からどのように呼び出されるのか、そして$scope.data.time変数の更新が$apply()関数にパラメータとして渡される関数の中でどのように実行されるのかに注目してください。 $apply()関数呼び出しが終了すると、AngularJSは内部的に$digest()を呼び出します。そのため、すべてのデータバインディングが更新されます。

61
Alex Jolig

AngularJSはこのevents-loopを拡張して、_ AngularJS contextという名前のものを作成します。

$ watch()

UIで何かをバインドするたびに$watchリストの$watchを挿入します。

User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

ここに、最初の入力にバインドされている$scope.userがあり、2番目の入力にバインドされている$scope.passがあります。こうすることで、2つの$watchリストへの$watchesを追加します。

AKAがリンク段階でtemplateがロードされると、コンパイラはすべてのディレクティブを探し、必要なすべての$watchesを作成します。

AngularJSは$watch$watchcollectionおよび$watch(true)を提供します。以下は ウォッチャーから抜粋した3つすべてを説明しているきちんとした図です

Enter image description here

angular.module('MY_APP', []).controller('MyCtrl', MyCtrl)
function MyCtrl($scope,$timeout) {
  $scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}];

  $scope.$watch("users", function() {
    console.log("**** reference checkers $watch ****")
  });

  $scope.$watchCollection("users", function() {
    console.log("**** Collection  checkers $watchCollection ****")
  });

  $scope.$watch("users", function() {
    console.log("**** equality checkers with $watch(true) ****")
  }, true);

  $timeout(function(){
     console.log("Triggers All ")
     $scope.users = [];
     $scope.$digest();

     console.log("Triggers $watchCollection and $watch(true)")
     $scope.users.Push({ name: 'Thalaivar'});
     $scope.$digest();

     console.log("Triggers $watch(true)")
     $scope.users[0].name = 'Superstar';
     $scope.$digest();
  });
}

http://jsfiddle.net/2Lyn0Lkb/ /

$digestループ

ブラウザがAngularJSコンテキストで管理できるイベントを受け取ると、$digestループが発生します。このループは2つの小さいループから作られています。一方は$evalAsyncキューを処理し、もう一方は$watch listを処理します。 $digestは、持っている$watchのリストをループします。

app.controller('MainCtrl', function() {
  $scope.name = "vinoth";

  $scope.changeFoo = function() {
      $scope.name = "Thalaivar";
  }
});

{{ name }}
<button ng-click="changeFoo()">Change the name</button>

Ng-clickはウォッチを作成しないため、ここでは$watchは1つだけです。

ボタンを押します。

  1. ブラウザはAngularJSコンテキストに入るイベントを受け取ります
  2. $digestループが実行され、すべての$ watchに変更を要求します。
  3. $ scope.nameの変更を監視していた$watchは変更を報告するので、別の$digestループを強制します。
  4. 新しいループは何も報告しません。
  5. ブラウザはコントロールを取り戻し、$ scope.nameの新しい値を反映してDOMを更新します。
  6. ここで重要なことは、AngularJSコンテキストに入るEVERYイベントが$digestループを実行することです。つまり、入力に文字を書くたびに、このページのすべての$watchをチェックしながらループが実行されます。

$ apply()

イベントが発生したときに$applyを呼び出すと、それはAngular-contextを通過しますが、呼び出さないと、外部で実行されます。それはそれと同じくらい簡単です。 $applyは内部的に$digest()ループを呼び出し、DOMが新しく更新された値で更新されるようにすべての監視を繰り返します。

$apply()メソッドは現在の$scopeとそのchildrenに対してのみウォッチャーをトリガーしますが、$digest()メソッドは$scopeチェーン全体でウォッチャーをトリガーします。 上位の$scopeオブジェクトがローカルの変更について知る必要がない場合は、$digest()を使用できます。

43
Thalaivar

$watchGroup$watchCollectionもあります。特に、$watchGroupは、domオブジェクトではないビュー内の複数のプロパティを持つオブジェクトを更新するために関数を呼び出す場合に非常に役立ちます。キャンバス内の他のビュー、webGLまたはサーバー要求。ここでは、ドキュメント link

17

私は、$watch$apply$digestそしてダイジェストサイクルをカバーする非常に詳細なビデオを見つけました:

以下は、概念を説明するためにこれらのビデオで使用されているスライドです(念のために、上記のリンクが削除されているか機能していない場合)。

Enter image description here

上の画像では、 "$ scope.c"は(マークアップで)どのデータバインディングでも使用されていないため、監視されていません。他の2つ($scope.a$scope.b)は監視されます。

Enter image description here

上記の画像から:それぞれのブラウザイベントに基づいて、AngularJSはイベントをキャプチャし、ダイジェストサイクルを実行し(変更を監視する)、監視機能を実行し、DOMを更新します。ブラウザイベントでない場合は、$applyまたは$digestを使用してダイジェストサイクルを手動で起動できます。

$apply$digestの詳細:

Enter image description here

17
user203687

退屈で眠そうな上記のすべてを読み終えてください(ごめんなさい、本当です)。非常に技術的で、詳細で、詳細で、ドライです。なぜ書いているのですか? AngularJSは大規模であるため、相互接続された概念の多くは誰もが夢中になります。私はよく自問しましたが、私はそれらを理解するほど頭が良くないのですか?いや!これは、技術をfor-dummie言語すべての用語なしで説明できる人がほとんどいないからです!さて、試してみましょう:

1)それらはすべてイベント駆動型のものです。(笑いは聞こえますが、読み進めてください)

イベントドリブンが何であるかわからない場合は、ページにボタンを配置し、「オンクリック」を使用して関数と接続し、ユーザーがそれをクリックして、内部に配置するアクションをトリガーするのを待ってください関数。または、SQL Server/Oracleの「トリガー」を考えてください。

2)$ watchは「オンクリック」です。

特別なのは、2つの関数をパラメーターとして使用することです。最初の関数はイベントからの値を指定し、2番目の関数は値を考慮に入れます...

)$ digestは、たゆまずチェックする上司です、bla-bla-blaですが、上司です。

4)$ applyは、手動で実行したい場合の方法を提供します、フェイルプルーフのように(オンクリックが開始されない場合、強制的に実行します)

今、視覚化しましょう。これを想像して、アイデアをつかみやすくします:

レストランで

-WAITERSは顧客から注文を受け取ることになっています。これは

$watch(
  function(){return orders;},
  function(){Kitchen make it;}
);

-MANAGERは、すべてのウェイターが起きていることを確認するために走り回り、顧客からの変化の兆候に対応します。これは$digest()です

-OWNERは、リクエストに応じて全員をドライブする究極のパワーを持ちます。これは$apply()です

12
Jeb50