2013-05-24 10 views
76

どのように$scope.$watch複数の変数を角度にして、そのうちの1つが変更されたときにコールバックをトリガします。

$scope.name = ... 
$scope.age = ... 
$scope.$watch('???',function(){ 
    //called when name or age changed 
}) 
+0

$ watch vsのトピックに関する[Ben Nadelの非常に良いブログ記事](http://www.bennadel.com/blog/2566-Scope-watch-vs-watchCollection-In-AngularJS.htm) $ watchCollectionが役に立つかもしれません。 –

答えて

113

UPDATE

角度今オファー(1.3以降)は、2つのスコープ方法$watchGroup$watchCollection。それらは@blazemongerと@kargoldによって言及されています。これは、タイプと値とは独立して動作するはず


$scope.$watch('[age,name]', function() { ... }, true); 

あなたは、この場合にはtrueに三番目のパラメータを設定する必要があります。

文字列の連結'age + name'は、このような場合に失敗します:ユーザーがボタンをクリックする前に見て価値が42foo42 + foo)だろう

<button ng-init="age=42;name='foo'" ng-click="age=4;name='2foo'">click</button> 

とクリック42foo4 + 2foo)後。したがって、時計機能は呼び出されませんでした。そのような場合は表示されないことを保証できない場合は、配列式を使用することをお勧めします。

<!DOCTYPE html> 
<html> 
    <head> 
     <meta charset="UTF-8"> 
     <link href="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css" rel="stylesheet" /> 
     <script src="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script> 
     <script src="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script> 
     <script src="http://code.angularjs.org/1.2.0-rc.2/angular.js"></script> 
     <script src="http://code.angularjs.org/1.2.0-rc.2/angular-mocks.js"></script> 
     <script> 

angular.module('demo', []).controller('MainCtrl', function ($scope) { 

    $scope.firstWatchFunctionCounter = 0; 
    $scope.secondWatchFunctionCounter = 0; 

    $scope.$watch('[age, name]', function() { $scope.firstWatchFunctionCounter++; }, true); 
    $scope.$watch('age + name', function() { $scope.secondWatchFunctionCounter++; }); 
}); 

describe('Demo module', function() { 
    beforeEach(module('demo')); 
    describe('MainCtrl', function() { 
     it('watch function should increment a counter', inject(function ($controller, $rootScope) { 
      var scope = $rootScope.$new(); 
      scope.age = 42; 
      scope.name = 'foo'; 
      var ctrl = $controller('MainCtrl', { '$scope': scope }); 
      scope.$digest(); 

      expect(scope.firstWatchFunctionCounter).toBe(1); 
      expect(scope.secondWatchFunctionCounter).toBe(1); 

      scope.age = 4; 
      scope.name = '2foo'; 
      scope.$digest(); 

      expect(scope.firstWatchFunctionCounter).toBe(2); 
      expect(scope.secondWatchFunctionCounter).toBe(2); // This will fail! 
     })); 
    }); 
}); 


(function() { 
    var jasmineEnv = jasmine.getEnv(); 
    var htmlReporter = new jasmine.HtmlReporter(); 
    jasmineEnv.addReporter(htmlReporter); 
    jasmineEnv.specFilter = function (spec) { 
     return htmlReporter.specFilter(spec); 
    }; 
    var currentWindowOnload = window.onload; 
    window.onload = function() { 
     if (currentWindowOnload) { 
      currentWindowOnload(); 
     } 
     execJasmine(); 
    }; 
    function execJasmine() { 
     jasmineEnv.execute(); 
    } 
})(); 

     </script> 
    </head> 
    <body></body> 
</html> 

http://plnkr.co/edit/2DwCOftQTltWFbEDiDlA?p=preview

PS:

コメントに@reblaceで述べたように、値にアクセスすることはもちろん可能です:

$scope.$watch('[age,name]', function (newValue, oldValue) { 
    var newAge = newValue[0]; 
    var newName = newValue[1]; 
    var oldAge = oldValue[0]; 
    var oldName = oldValue[1]; 
}, true); 
+0

Plunker not working –

+0

@ Ajax3.14:正確には機能していないのは何ですか?私はその問題を再現できません。あなたはジャスミー出力 "Failing 1 spec"を見ることができますか?テストが失敗するため、期待される出力になります(テストのソースコードのコメントを参照)。 – stofl

+1

この回答は私を助けました、ありがとう!このアプローチの美しさは、監視される配列の要素としてnewValue/oldValueにアクセスできることです(例えば、newValue [0]、newValue [1]、oldValue [0]、oldValue [1]) – reblace

43
$scope.$watch('age + name', function() { 
    //called when name or age changed 
}); 
+2

これはJSが文字列としてconcatinatingするために動作します。良い答えと正しい1 btw。 – Heinrich

+3

誰かに同じ質問への答えを見つけること - 私の答えは間違っています、この答えは正しいものです。 – callmekatootie

+4

この場合、角度が変更されたフィールドだけでなく結果の連結になるので、コールバックに渡す 'newValue'と 'oldValue'引数を使用しないようにしてください。 –

6

複数の値を見るために多くの方法があります:

//angular 1.1.4 
$scope.$watchCollection(['foo', 'bar'], function(newValues, oldValues){ 
    // do what you want here 
}); 

またはより新しいバージョン

//angular 1.3 
$scope.$watchGroup(['foo', 'bar'], function(newValues, oldValues, scope) { 
    //do what you want here 
}); 

は、より多くの情報のための公式ドキュメントを読む:https://docs.angularjs.org/api/ng/type/ $ rootScope.Scope

+1

これが投票されたのはなぜですか?これには何が問題なのですか? – MGot90

+0

'watchCollection'は文字列でなければならないと思います。私はその後、編集を確認するつもりです。 – KthProg

+0

新しい値と古い値がこれらの関数の間でさまざまな方法でアクセスされると私は信じることにも言及する価値があります。 – KthProg

5

誰も持っていません

var myCallback = function() { console.log("name or age changed"); }; 
$scope.$watch("name", myCallback); 
$scope.$watch("age", myCallback); 

これは、少しポーリングを意味するかもしれません。 name + age(これ用)とname(別の場所)の両方を見ると、Angularは効果的にnameを2回見て、汚れていないかどうかを確認します。

コールバックをインライン展開するのではなく、名前で使用するほうが読みやすくなります。特に私の例よりも良い名前を付けることができれば。

そして、あなたがする必要がある場合には、さまざまな方法で値を見ることができます:あなたはそれを使用することができますが、私の知る限り、それはあなたが見てみましょうしない場合

$scope.$watch("buyers", myCallback, true); 
$scope.$watchCollection("sellers", myCallback); 

$watchGroupがいいですグループメンバをコレクションとして、またはオブジェクトが等価であるようにします。

という古い値と新しい値が両方とも同じコールバック関数呼び出し内の式内にある必要がある場合、おそらく他の提案されている解決策が便利です。

+0

コントローラがロードされて初めてコールバック関数を呼び出しますか?コントローラがロードされて初めて呼び出すのを止めることはできませんか? –

関連する問題