$scope.$watch
と$scope.$apply
の使い方がわかりません。公式文書は役に立ちません。
私が特に理解していないこと:
このチュートリアル を試しましたが、当然のことながら$watch
と$apply
の理解が必要です。
$apply
と$watch
は何をしていますか、そしてどのようにそれらを適切に使用しますか?
AngularJSでは、モデルを更新し、ビュー/テンプレートでDOMを「自動的に」(組み込みディレクティブまたはカスタムディレクティブを介して)更新します。
Scopeメソッドである$ applyと$ watchはDOMとは関係ありません。
概念 ページ(「ランタイム」セクション)には、$ダイジェストループ、$ apply、$ evalAsyncキュー、および$ watchリストのかなり良い説明があります。これがテキストに付随する図です。
スコープにアクセスできるコード(通常はコントローラとディレクティブ(それらのリンク関数またはコントローラ、あるいはその両方))がなんであれ、AngularJSがそのスコープに対して評価する「 watchExpression 」を設定できます。この評価はAngularJSがその$ダイジェストループ(特に "$ watch list"ループ)に入るたびに行われます。個々のスコーププロパティを見ることができます、あなたは一緒に2つのプロパティを見るために関数を定義することができます、あなたは配列の長さなどを見ることができます。
AngularJSの双方向データバインディングが有効になっている(つまりng-modelを使用している)、$ httpコールバックが起動するなど、テキストボックスにAngularJSが入力された場合など、$ applyは既に呼び出されているので上の図の "AngularJS"長方形の内側にあります。すべてのwatchExpressionsが評価されます(おそらく複数回 - それ以上の変更が検出されなくなるまで)。
"AngularJSの外部"で問題が発生した場合 - たとえば、ディレクティブでbind()を使用した後にそのイベントが発生し、コールバックが呼び出されたり、jQueryの登録済みコールバックが発生したりします。コールバックコードが、任意の$ watchが監視しているものを変更した場合、$ applyを呼び出してAngularJSの矩形領域に入り、$ digestループが実行されるため、AngularJSはその変更に気付いて魔法を行います。
このブログ は例を作成し理解できる説明を作成することすべてをカバーしました。
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()
関数についてさらに詳しく説明します。
$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 }
を使用してこれを行うこともできましたが、これはリスナー関数の内部でできることのほんの一例です。
$scope.$digest()
関数は、$scope object
内のすべてのウォッチ、およびその子$ scopeオブジェクト(存在する場合)を繰り返し処理します。 $digest()
がウォッチを反復処理するときに、各ウォッチのvalue関数を呼び出します。 value関数によって返された値が最後に呼び出されたときに返された値と異なる場合は、そのウォッチのリスナー関数が呼び出されます。
$digest()
関数はAngularJSが必要と判断したときに呼び出されます。たとえば、ボタンクリックハンドラが実行された後、またはAJAX
呼び出しが戻った後(done()/ fail()コールバック関数が実行された後)。
あなたはAngularJSが$digest()
関数をあなたに代わって呼び出さないいくつかの稀なケースに遭遇するかもしれません。通常、データバインディングが表示された値を更新しないことに気付くことでそれを検出します。その場合は$scope.$digest()
を呼び出してください。あるいは、代わりに$scope.$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()
を呼び出します。そのため、すべてのデータバインディングが更新されます。
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
リストへの$watch
esを追加します。
AKAがリンク段階でtemplateがロードされると、コンパイラはすべてのディレクティブを探し、必要なすべての$watch
esを作成します。
AngularJSは$watch
、$watchcollection
および$watch(true)
を提供します。以下は ウォッチャーから抜粋した3つすべてを説明しているきちんとした図です 。
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つだけです。
ボタンを押します。
$digest
ループが実行され、すべての$ watchに変更を要求します。$watch
は変更を報告するので、別の$digest
ループを強制します。$digest
ループを実行することです。つまり、入力に文字を書くたびに、このページのすべての$watch
をチェックしながらループが実行されます。イベントが発生したときに$apply
を呼び出すと、それはAngular-contextを通過しますが、呼び出さないと、外部で実行されます。それはそれと同じくらい簡単です。 $apply
は内部的に$digest()
ループを呼び出し、DOMが新しく更新された値で更新されるようにすべての監視を繰り返します。
$apply()
メソッドは現在の$scope
とそのchildren
に対してのみウォッチャーをトリガーしますが、$digest()
メソッドは$scope
チェーン全体でウォッチャーをトリガーします。 上位の$scope
オブジェクトがローカルの変更について知る必要がない場合は、$digest()
を使用できます。
$watchGroup
と$watchCollection
もあります。特に、$watchGroup
は、domオブジェクトではないビュー内の複数のプロパティを持つオブジェクトを更新するために関数を呼び出す場合に非常に役立ちます。キャンバス内の他のビュー、webGLまたはサーバー要求。ここでは、ドキュメント link 。
私は、$watch
、$apply
、$digest
そしてダイジェストサイクルをカバーする非常に詳細なビデオを見つけました:
AngularJS - ウォッチャーの理解、$ watch、$ watchGroup、$ watchCollection、ng-change
AngularJS - ダイジェストサイクル(ダイジェストフェーズまたはダイジェストプロセスまたはダイジェストループ)を理解する
以下は、概念を説明するためにこれらのビデオで使用されているスライドです(念のために、上記のリンクが削除されているか機能していない場合)。
上の画像では、 "$ scope.c"は(マークアップで)どのデータバインディングでも使用されていないため、監視されていません。他の2つ($scope.a
と$scope.b
)は監視されます。
上記の画像から:それぞれのブラウザイベントに基づいて、AngularJSはイベントをキャプチャし、ダイジェストサイクルを実行し(変更を監視する)、監視機能を実行し、DOMを更新します。ブラウザイベントでない場合は、$apply
または$digest
を使用してダイジェストサイクルを手動で起動できます。
$apply
と$digest
の詳細:
退屈で眠そうな上記のすべてを読み終えてください(ごめんなさい、本当です)。非常に技術的で、詳細で、詳細で、ドライです。なぜ書いているのですか? 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()
です