2013-05-24 21 views
76

Cách $scope.$watch nhiều biến trong Góc và kích hoạt gọi lại khi một trong số chúng đã thay đổi.

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

Có [một bài đăng blog rất hay của Ben Nadel] (http://www.bennadel.com/blog/2566-Scope-watch-vs-watchCollection-In-AngularJS.htm) về chủ đề của $ watch vs $ watchCollection bạn có thể thấy hữu ích. –

Trả lời

113

CẬP NHẬT

góc Mời nay là hai phương pháp phạm vi $watchGroup (từ 1.3) và $watchCollection. Những người đã được đề cập bởi @blazemonger và @kargold.


này nên hoạt động độc lập với các chủng loại và giá trị:

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

Bạn cần phải thiết lập các tham số thứ ba là true trong trường hợp này.

Chuỗi nối 'age + name' sẽ thất bại trong một trường hợp như thế này:

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

Trước khi người dùng nhấp chuột vào nút giá trị quan sát sẽ là 42foo (42 + foo) và sau khi nhấp chuột 42foo (4 + 2foo) . Vì vậy, chức năng đồng hồ sẽ không được gọi. Vì vậy, tốt hơn sử dụng một biểu thức mảng nếu bạn không thể đảm bảo, rằng trường hợp như vậy sẽ không xuất hiện.

<!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:

Như đã trình bày bởi @reblace trong một chú thích, nó là tất nhiên có thể truy cập các giá trị:

$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 không hoạt động –

+0

@ Ajax3.14: Chính xác thì những gì không hoạt động? Tôi không thể tái tạo vấn đề đó. Nó có thể được, mà bạn thấy đầu ra jasmie "Thất bại 1 spec"? Đó sẽ là đầu ra mong đợi, bởi vì kiểm tra không thành công (xem chú thích trong mã nguồn của bài kiểm tra). – stofl

+1

Câu trả lời này đã giúp tôi, cảm ơn! Vẻ đẹp của phương pháp này là bạn có thể truy cập newValue/oldValue như các phần tử của mảng đã xem (ví dụ: newValue [0], newValue [1] và oldValue [0], oldValue [1]) – reblace

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

Điều này sẽ làm việc do JS concatinating chúng như Strings. Câu trả lời hay và btw đúng. – Heinrich

+3

Để bất cứ ai tìm câu trả lời cho cùng một câu hỏi - câu trả lời của tôi là không chính xác, câu trả lời này là đúng. – callmekatootie

+4

Ngoài ra hãy chắc chắn trong trường hợp này không sử dụng các đối số 'newValue' và' oldValue' mà góc chuyển vào gọi lại vì chúng sẽ là kết quả nối và không chỉ là trường đã được thay đổi. –

6

Có nhiều cách để xem nhiều giá trị:

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

trở lên phiên bản mới

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

đọc doc chính thức để biết thêm thông tin: https://docs.angularjs.org/api/ng/type/ $ rootScope.Scope

+1

Tại sao điều này lại bị bỏ phiếu? Chuyện gì thế này? – MGot90

+0

Tôi tin rằng 'watchCollection' phải là một chuỗi. Tôi sẽ xác nhận rồi chỉnh sửa. – KthProg

+0

Điều đáng nói là tôi tin rằng các giá trị mới và cũ được truy cập theo nhiều cách khác nhau giữa các hàm này. – KthProg

5

Không ai có đã đề cập rõ ràng:

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

Điều này có thể có nghĩa là ít bỏ phiếu hơn một chút. Nếu bạn xem cả hai name + age (cho điều này) và name (ở nơi khác) thì tôi giả sử Góc sẽ xem xét hiệu quả name hai lần để xem liệu nó có bẩn không.

Có thể đọc được nhiều hơn để sử dụng gọi lại theo tên thay vì gạch chân nó. Đặc biệt là nếu bạn có thể cho nó một cái tên tốt hơn trong ví dụ của tôi.

Và bạn có thể xem các giá trị theo những cách khác nhau nếu bạn cần phải:

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

$watchGroup là tốt đẹp nếu bạn có thể sử dụng nó, nhưng như xa như tôi có thể nói, nó không cho phép bạn xem các nhóm thành viên như một bộ sưu tập hoặc với sự bình đẳng đối tượng.

Nếu bạn cần các giá trị cũ và mới của cả biểu thức bên trong một và giống nhau gọi hàm callback, thì có lẽ một số các giải pháp đề xuất khác là thuận tiện hơn.

+0

nó gọi chức năng gọi lại ngay cả lần đầu tiên khi bộ điều khiển của tôi được nạp? sao tôi không thể dừng nó để gọi lần đầu tiên khi bộ điều khiển được nạp? –

Các vấn đề liên quan