web-dev-qa-db-ja.com

Angular.jsは特定のオブジェクトプロパティのみを監視します

基本的にこれが欲しい http://plnkr.co/edit/3yfXbo1c0llO40HZ8WNP?p=preview でも、何かを変更しても時計が起動しない。

私はこれがうまくいったことを知っています

$scope.$watch('stuff', function (newVal, oldVal) {
    console.log(oldVal, newVal);

}, true);

しかし、私は時計の内部でいくつかの要約を行いたいので、変更されなかった値を不必要にループしたり、再開したりしたくありません。

//編集-plnkrの例は実際のアプリからの抜粋であり、合計数(何かと何かの合計)の変更など、行の追加と削除などを行うことができます。 ng-repeatの外部の別の入力から..

16
fxck

アレイの大きさによっては、非常に負担がかかる可能性があるため、監視は行いません。代わりに、フィルターを作成します。

HTML:

Sum of something is: {{ stuff | sum:'something' }}<br/>
Sum of somethingelse is: {{ stuff | sum:'somethingelse' }}

Javascript:

.filter("sum", function(){
    return function(input, params){
        var totalSum = 0;
        for(var x = 0; x < input.length; x++){
            totalSum += input[x][params];
        }
        return totalSum;
    }
})

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

6
Mathew Berg

あなたの問題に対するこの解決策を思いついた後、私はあちこちで笑っています。このソリューションは、個々のオブジェクトを監視するだけでなく、合計のために残りのオブジェクトを完全にループすることもありません。

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

app.controller('MainCtrl', function($scope) {
  $scope.stuff = STUFF;
  var i;
  $scope.sumOfSomething = 0;
  $scope.sumOfSomethingElse = 0;

  for(i=0;i < $scope.stuff.length;i++ ){
    $scope.$watch('stuff['+i+'].something', function (newVal,oldVal) {
      // happens when the watch is registered.
      if(oldVal === newVal){
        $scope.sumOfSomething += +newVal;
        return;
      }
      if(newVal){
        $scope.sumOfSomething += + (newVal - oldVal);
      }
    });

    $scope.$watch('stuff['+i+'].somethingelse', function (newVal,oldVal) {
      // happens when the watch is registered. 
      if(oldVal === newVal){
        $scope.sumOfSomethingElse += +newVal;
        return;
      }

      if(newVal){
        $scope.sumOfSomethingElse += + (newVal - oldVal);
      }

    });  
  }
});

ちなみに、STUFFに多数のオブジェクトがある場合、このソリューションがどれほど最適になるかはわかりません。また、STUFF内のオブジェクトの数が変更される場合、これはそのままでは機能しません。基本的に行っているのは、angularのダーティチェックループを使用して、合計を行うことです。

また、ウォッチリスナーのnewValとoldValの順序にも注意してください。あなたの注文は間違っています。

4
ganaraj

$watch()はangular式を使用するため、カスタムウォッチャー関数を使用できます。あなたの場合、変更を監視する最も簡単な方法は、現在のすべてのフィールドを合計することです。興味がある場合は、angularに対して適切で効率的な単一の数値を返すと、合計をレンダリングする準備が整います。

_var sumThings = function(obj, field) {
  var val = 0;
  obj.forEach(function(o) {
    val += parseInt(o[field]);
  });
  return val;
};

$scope.$watch(function() { return sumThings($scope.stuff, "something")}, function (newVal, oldVal) {    //console.log("watchExpression('something') fired!", oldVal, newVal);
  $scope.somethingSum = newVal;
});

$scope.$watch(function() { return sumThings($scope.stuff, "somethingelse")}, function (newVal, oldVal) {
  $scope.somethingelseSum = newVal;
});
_

基本的に、あなたの総和をすることによって、あなたはあなた自身の汚いチェックをしているのです。オブジェクトがここに示したよりも複雑な場合は、$watch(STUFF, true)よりも効率的です。

更新されたplnkr: http://plnkr.co/edit/d7CvERu3XGkub4l6f1yw?p=preview

3
cirrus

ええと、もし私がそれを手に入れたら、あなたはそれらが変化したときにsomethingを内部オブジェクトのsomethingelseと合計したいだけです。また、変更がそれらの1つにある場合は、他のすべての内部オブジェクトをループする必要はありません。

_$watch_がないため、_stuff.property_は起動していません。また、_stuff.*.something_のようなパターンもありません。

オブジェクトが配列からプッシュまたはプルされる瞬間がわかっている場合は、オブジェクトを配列に挿入する前後に、個別に$scope.$watch(object, function(){}, true)を適用できます。この瞬間がわからない場合は、配列全体を_$watch_する必要があります。変更が発生した場合は、すべての登録解除関数を実行し、それらすべてを1つずつ_$watch_します。

とにかく、最近のブラウザのループはかなり高速であり、重い計算を行っていて、何百万ものオブジェクトがある場合(すでにメモリの問題であったはずです)を除いて、おそらくそれでうまくいくでしょう。したがって、それらをすべて個別に_$watch_する努力は、システムのパフォーマンスを構成する可能性のある非常に重い計算がある場合にのみ必要です。

1
Caio Cunha