私は次のようなテンプレートを持つディレクティブを持っています
<div>
<div ng-repeat="item in items" ng-click="updateModel(item)">
<div>
私のディレクティブは次のように宣言されています:
return {
templateUrl: '...',
restrict: 'E',
require: '^ngModel',
scope: {
items: '=',
ngModel: '=',
ngChange: '&'
},
link: function postLink(scope, element, attrs)
{
scope.updateModel = function(item)
{
scope.ngModel = item;
scope.ngChange();
}
}
}
私はを頂きたい ng-change
アイテムがクリックされ、foo
の値が既に変更されたときに呼び出されます。
つまり、私のディレクティブが次のように実装されている場合:
<my-directive items=items ng-model="foo" ng-change="bar(foo)"></my-directive>
bar
の値が更新されたら、foo
を呼び出すことを期待します。
上記のコードでは、ngChange
は正常に呼び出されますが、新しい更新値ではなく、古い値のfoo
を使用して呼び出されます。
この問題を解決する1つの方法は、ngChange
の値がすでに変更されているときに、将来のある時点でタイムアウト内でfoo
を呼び出して実行することです。しかし、この解決策は、物事が実行されることになっている順序を私にゆるやかに制御させます。
親スコープでfoo
を介してウォッチャーを使用することもできますが、このソリューションではngChange
メソッドを実装することはできず、ウォッチャーは優れたメモリコンシューマーであると言われています。
ngChange
をタイムアウトやウォッチャーなしで同期的に実行する方法はありますか?
ngModel
が必要な場合、ngModelController
で$setViewValue
を呼び出すだけで、ng-change
を暗黙的に評価できます。リンク関数の4番目のパラメーターはngModelCtrlである必要があります。以下のコードは、ディレクティブに対してng-change
を機能させます。
link : function(scope, element, attrs, ngModelCtrl){
scope.updateModel = function(item) {
ngModelCtrl.$setViewValue(item);
}
}
ソリューションが機能するためには、ngChangeとngModelをmyDirectiveの分離スコープから削除してください。
ここにプランクがあります: http://plnkr.co/edit/UefUzOo88MwOMkpgeX07?p=preview
tl; dr
私の経験では、 ngModelCtrl から継承する必要があります。メソッド_ng-change
_を使用すると、_ngModelCtrl.$setViewValue
_式が自動的に評価されます
_angular.module("myApp").directive("myDirective", function(){
return {
require:"^ngModel", // this is important,
scope:{
... // put the variables you need here but DO NOT have a variable named ngModel or ngChange
},
link: function(scope, elt, attrs, ctrl){ // ctrl here is the ngModelCtrl
scope.setValue = function(value){
ctrl.$setViewValue(value); // this line will automatically eval your ng-change
};
}
};
});
_
より正確に
_ng-change
_は、ngModelCtrl.$commitViewValue()
[〜#〜] if [〜#〜]の間に評価されますngModelが変更されました。トリガー引数を使用しない場合、または ngModelOptions を正確に指定していない場合、メソッド$commitViewValue()
は$setViewValue(value, trigger)
によって自動的に呼び出されます。
_ng-chage
_の参照が変更されると、_$viewValue
_が自動的にトリガーされるように指定しましたif。 ngModel
がstring
またはint
である場合、心配する必要はありません。 ngModel
がオブジェクトであり、そのプロパティの一部を変更するだけの場合、_$setViewValue
_はngChange
を評価しません。
投稿の最初からコード例を取り上げると
_scope.setValue = function(value){
ctrl.$setViewValue(value); // this line will automatically evalyour ng-change
};
scope.updateValue = function(prop1Value){
var vv = ctrl.$viewValue;
vv.prop1 = prop1Value;
ctrl.$setViewValue(vv); // this line won't eval the ng-change expression
};
_
いくつかの調査の後、最良のアプローチは$timeout(callback, 0)
を使用することであると思われます。
$digest
コールバックが実行された直後にサイクルします。
だから、私の場合、解決策は使用することでした
$timeout(scope.ngChange, 0);
この方法では、コールバックのシグネチャが何であるかは関係ありません。親スコープで定義したとおりに実行されます。
このような変更を加えたプランカーは次のとおりです:http://plnkr.co/edit/9MGptJpSQslk8g8tD2bZ?p=preview
Samuli UlmanenとlucienBertinの答えはそれを否定していますが、AngularJSのドキュメントを少し読むと、これを処理する方法についてさらにアドバイスが提供されます( https://docs.angularjs.org/api/ng/type/ngModelを参照してください。 NgModelController )。
特に、オブジェクトを$ setViewValue(myObj)に渡す場合。 AngularJS Documentatationの状態:
標準入力で使用すると、ビューの値は常に文字列になります(input [date]のDateオブジェクトなど、別の型に解析される場合もあります)。ただし、カスタムコントロールはこのメソッドにオブジェクトを渡すこともあります。この場合、$ setViewValueに渡す前にオブジェクトのコピーを作成する必要があります。これは、ngModelはオブジェクトの詳細な監視を実行せず、IDの変更のみを探すためです。オブジェクトのプロパティを変更するだけの場合、ngModelはオブジェクトが変更されたことを認識せず、$ parsersおよび$ validatorsパイプラインを呼び出しません。このため、$ setViewValueに渡されたコピーのプロパティを変更しないでください。そうしないと、スコープのモデル値が誤って変更される可能性があります。
私の特定のケースでは、私のモデルは瞬間日付オブジェクトなので、setViewValueを呼び出す前にオブジェクトを複製する必要があります。幸運なことに、ここでは簡単なクローンメソッドvar b = moment(a);
を提供しています。
link : function(scope, elements, attrs, ctrl) {
scope.updateModel = function (value) {
if (ctrl.$viewValue == value) {
var copyOfObject = moment(value);
ctrl.$setViewValue(copyOfObject);
}
else
{
ctrl.$setViewValue(value);
}
};
}