AngularJS
ダイジェストループのウォークスルー
サーチ…
構文
- $ scope。$ watch(watchExpression、callback、[deep compare])
- $ scope。$ digest()
- $ scope。$ apply([exp])
双方向データバインディング
Angularはそのフードの下でいくつかの魔法を持っています。 DOMを実際のjs変数にバインドすることができます。
Angularは、 " ダイジェストループ "という名前のループを使用します。これは、DOMを更新する変数呼び出しコールバックの変更後に呼び出されます。
たとえば、 ng-model
ディレクティブは、 keyup
eventListenerをこの入力に添付します。
<input ng-model="variable" />
keyup
イベントが発生するたびに、 ダイジェストループが開始されます。
ある時点で、 ダイジェストループはこのスパンの内容を更新するコールバックを反復します:
<span>{{variable}}</span>
この例の基本的なライフサイクルは、(非常に概略的に)角度の働きをまとめたものです::
- 角度スキャンhtml
-
ng-model
ディレクティブは、入力時にkeyup
リスナーを作成します - span内の
expression
がダイジェストサイクルへのコールバックを追加する
-
- ユーザーは入力と対話する
-
keyup
リスナーがダイジェストサイクルを開始する - ダイジェストサイクルはコールバックを呼び出します
- コールバック更新内容のスパン
-
$ digestと$ watch
前の例の結果を得るために双方向データバインディングを実装するには、次の2つのコア関数を使用できます。
- $ digestはユーザーのやり取りの後に呼び出されます(DOM =>変数をバインドします)
- $ watchは、変数の変更後に呼び出されるコールバックを設定します(変数variable => DOM)
注:実際の角度コードではなくデモです
<input id="input"/>
<span id="span"></span>
必要な2つの機能:
var $watches = [];
function $digest(){
$watches.forEach(function($w){
var val = $w.val();
if($w.prevVal !== val){
$w.callback(val, $w.prevVal);
$w.prevVal = val;
}
})
}
function $watch(val, callback){
$watches.push({val:val, callback:callback, prevVal: val() })
}
これで、これらの関数を使用して変数をDOMにフックできます(角には組み込みディレクティブが付属しています)。
var realVar;
//this is usually done by ng-model directive
input1.addEventListener('keyup',function(e){
realVar=e.target.value;
$digest()
}, true);
//this is usually done with {{expressions}} or ng-bind directive
$watch(function(){return realVar},function(val){
span1.innerHTML = val;
});
もちろん、実際の実装はより複雑で、バインドする要素や使用する変数などのパラメータをサポートしています
実行中の例がここにあります: https : //jsfiddle.net/azofxd4j/
$スコープツリー
前の例は、単一のhtml要素を単一の変数にバインドする必要がある場合に十分です。
実際には、多くの要素を多くの変数にバインドする必要があります。
<span ng-repeat="number in [1,2,3,4,5]">{{number}}</span>
このng-repeat
は、5要素をnumber
という5つの変数にバインドします。それぞれの変数には異なる値が設定されます。
角度がこの振る舞いを達成する方法は、別々の変数を必要とする各要素に別のコンテキストを使用しています。このコンテキストはスコープと呼ばれます。
各スコープにはDOMにバインドされた変数であるプロパティがあり、 $digest
関数と$watch
関数はスコープのメソッドとして実装されています。
DOMはツリーであり、変数はツリーの異なるレベルで使用する必要があります。
<div>
<input ng-model="person.name" />
<span ng-repeat="number in [1,2,3,4,5]">{{number}} {{person.name}}</span>
</div>
しかし、われわれが見たように、 ng-repeat
内の変数のコンテキスト(またはスコープ)は、それを上回るコンテキストとは異なります。これを解決するために、角度はスコープをツリーとして実装します。
各スコープには子の配列があり、 $digest
メソッドを呼び出すと、すべての子の$digest
メソッドが実行されます。
この方法では、入力を変更した後、 $digest
がdivのスコープに対して呼び出され、5人の子供の$digest
が実行され、コンテンツが更新されます。
スコープの簡単な実装は、次のようになります。
function $scope(){
this.$children = [];
this.$watches = [];
}
$scope.prototype.$digest = function(){
this.$watches.forEach(function($w){
var val = $w.val();
if($w.prevVal !== val){
$w.callback(val, $w.prevVal);
$w.prevVal = val;
}
});
this.$children.forEach(function(c){
c.$digest();
});
}
$scope.prototype.$watch = function(val, callback){
this.$watches.push({val:val, callback:callback, prevVal: val() })
}
注:実際の角度コードではなくデモです