Xem nhiều thuộc tính phạm vi $


441

Có cách nào để đăng ký các sự kiện trên nhiều đối tượng bằng cách sử dụng $watch

Ví dụ

$scope.$watch('item1, item2', function () { });

Câu trả lời:


585

Bắt đầu từ AngularJS 1.3, có một phương thức mới được gọi là $watchGroupđể quan sát một tập hợp các biểu thức.

$scope.foo = 'foo';
$scope.bar = 'bar';

$scope.$watchGroup(['foo', 'bar'], function(newValues, oldValues, scope) {
  // newValues array contains the current values of the watch expressions
  // with the indexes matching those of the watchExpression array
  // i.e.
  // newValues[0] -> $scope.foo 
  // and 
  // newValues[1] -> $scope.bar 
});

7
sự khác biệt so với $ watchCollection ('[a, b]') ??
Kamil Tomšík

28
Sự khác biệt là bạn có thể sử dụng một mảng thích hợp thay vì một chuỗi trông giống như một mảng
Paolo Moretti

2
Tôi đã có một vấn đề tương tự nhưng sử dụng mẫu điều khiểnAs. Trong trường hợp của tôi, bản sửa lỗi bao gồm thêm vào các biến của tên của bộ điều khiển:$scope.$watchGroup(['mycustomctrl.foo', 'mycustomctrl.bar'],...
morels

1
Điều này dường như không hoạt động đối với tôi trên Angular 1.6. Nếu tôi đặt một bản ghi giao diện điều khiển trên chức năng, tôi có thể thấy rằng nó chỉ chạy một lần với newValuesoldValuesnhư undefined, trong khi xem từng thuộc tính hoạt động như bình thường.
Alejandro García Iglesias

290

Bắt đầu với AngularJS 1.1.4, bạn có thể sử dụng $watchCollection:

$scope.$watchCollection('[item1, item2]', function(newValues, oldValues){
    // do stuff here
    // newValues and oldValues contain the new and respectively old value
    // of the observed collection array
});

Ví dụ Plunker ở đây

Tài liệu ở đây


109
Lưu ý rằng tham số đầu tiên không phải là một mảng. Đó là một chuỗi trông giống như một mảng.
MK Safi

1
cám ơn vì cái này. nếu nó hoạt động với tôi, tôi sẽ cho bạn một ngàn đồng vàng - tiền ảo, dĩ nhiên :)
AdityaSaxena

5
Để rõ ràng (tôi mất vài phút để nhận ra) $watchCollectiondự định sẽ trao một đối tượng và cung cấp đồng hồ để thay đổi bất kỳ thuộc tính nào của đối tượng, trong khi $watchGroupdự định sẽ trao một loạt các thuộc tính riêng lẻ để theo dõi các thay đổi. Hơi khác nhau để giải quyết các vấn đề tương tự nhưng khác nhau. Phù!
Mattygabe

1
Bằng cách nào đó, newValues ​​và oldValues ​​luôn bằng nhau khi bạn sử dụng phương thức này: plnkr.co/edit/SwwdpQcNf78pQ80hhXMr
mostruash

Cảm ơn. Nó giải quyết vấn đề của tôi. Có bất kỳ hit / vấn đề hiệu suất của việc sử dụng $ watch?
Syed Nasir Abbas

118

$watch tham số đầu tiên cũng có thể là một hàm.

$scope.$watch(function watchBothItems() {
  return itemsCombinedValue();
}, function whenItemsChange() {
  //stuff
});

Nếu hai giá trị kết hợp của bạn đơn giản, tham số đầu tiên chỉ là biểu thức góc bình thường. Ví dụ: FirstName và LastName:

$scope.$watch('firstName + lastName', function() {
  //stuff
});

8
Điều này có vẻ giống như một trường hợp sử dụng đang khóc để đưa vào khung theo mặc định. Ngay cả một thuộc tính được tính đơn giản như fullName = firstName + " " + lastNameyêu cầu chuyển một hàm làm đối số đầu tiên (chứ không phải là một biểu thức đơn giản như 'firstName, lastName'). Angular là SO mạnh mẽ, nhưng có một số lĩnh vực mà nó có thể thân thiện với nhà phát triển hơn rất nhiều theo những cách mà (dường như) không thỏa hiệp các nguyên tắc thiết kế hoặc hiệu suất.
XML

4
Đồng hồ $ chỉ có một biểu hiện góc cạnh, được đánh giá trên phạm vi mà nó được gọi. Vì vậy, bạn có thể làm $scope.$watch('firstName + lastName', function() {...}). Bạn cũng có thể chỉ cần làm hai chiếc đồng hồ : function nameChanged() {}; $scope.$watch('firstName', nameChanged); $scope.$watch('lastName', nameChanged);. Tôi đã thêm trường hợp đơn giản để trả lời.
Andrew Joslin

Điểm cộng trong 'FirstName + lastName' là nối chuỗi. Vì vậy, góc cạnh chỉ kiểm tra xem kết quả của việc ghép có khác bây giờ không.
mb21

16
Nối chuỗi đòi hỏi một chút cẩn thận - nếu bạn thay đổi "paran orman" thành "para Norman" thì bạn sẽ không kích hoạt phản hồi; tốt nhất để đặt một bộ chia như "\ t | \ t" giữa thuật ngữ thứ nhất và thứ hai hoặc chỉ bám vào JSON rõ ràng
Dave Edelhart

Mặc dù amberjs hút, nhưng nó có tính năng này tích hợp! Nghe mà kẻ góc cạnh. Tôi đoán làm cho đồng hồ là cách có thể làm hợp lý nhất.
Aladdin đã hát

70

Đây là một giải pháp rất giống với mã giả ban đầu của bạn thực sự hoạt động:

$scope.$watch('[item1, item2] | json', function () { });

EDIT: Được rồi, tôi nghĩ điều này thậm chí còn tốt hơn:

$scope.$watch('[item1, item2]', function () { }, true);

Về cơ bản, chúng tôi bỏ qua bước json, có vẻ như bắt đầu ngu ngốc, nhưng nó không hoạt động mà không có nó. Khóa của chúng là tham số thứ 3 thường bị bỏ qua, bật tính bằng nhau của đối tượng trái ngược với đẳng thức tham chiếu. Sau đó, các so sánh giữa các đối tượng mảng được tạo của chúng tôi thực sự hoạt động đúng.


Tôi đã có một câu hỏi tương tự. Và đây là câu trả lời chính xác cho tôi.
Calvin Cheng

Cả hai đều có chi phí hoạt động nhẹ nếu các mục trong biểu thức mảng không phải là loại đơn giản.
null

Đó là sự thật .. nó đang thực hiện một so sánh đầy đủ về các đối tượng thành phần. Mà có thể hoặc không thể là những gì bạn muốn. Xem câu trả lời hiện được phê duyệt cho một phiên bản sử dụng góc cạnh hiện đại không thực hiện so sánh toàn bộ chiều sâu.
Karen Zilles

Vì một số lý do, khi tôi đang cố gắng sử dụng $ watchCollection trên một mảng, tôi đã gặp lỗi TypeError: Object #<Object> has no method '$watchCollection'này nhưng giải pháp này giúp tôi giải quyết vấn đề của mình!
abippii

Thật tuyệt, bạn thậm chí có thể xem các giá trị trong các đối tượng:$scope.$watch('[chaptercontent.Id, anchorid]', function (newValues, oldValues) { ... }, true);
Serge van den Oever

15

Bạn có thể sử dụng các hàm trong $ watchgroup để chọn các trường của một đối tượng trong phạm vi.

        $scope.$watchGroup(
        [function () { return _this.$scope.ViewModel.Monitor1Scale; },   
         function () { return _this.$scope.ViewModel.Monitor2Scale; }],  
         function (newVal, oldVal, scope) 
         {
             if (newVal != oldVal) {
                 _this.updateMonitorScales();
             }
         });

1
Tại sao bạn sử dụng _this? Không phạm vi được đưa vào các chức năng mà không xác định rõ ràng nó?
sg.cc

1
Tôi sử dụng bản thảo, mã trình bày được biên dịch kết quả.
Yang Zhang

12

Tại sao không chỉ đơn giản là bọc nó trong một forEach?

angular.forEach(['a', 'b', 'c'], function (key) {
  scope.$watch(key, function (v) {
    changed();
  });
});

Đó là về chi phí tương tự như việc cung cấp một hàm cho giá trị kết hợp, mà không thực sự phải lo lắng về thành phần giá trị .


Trong trường hợp này, log () sẽ được thực thi nhiều lần, phải không? Tôi nghĩ rằng trong trường hợp các chức năng đồng hồ $ lồng nhau, nó sẽ không. Và nó không nên trong giải pháp để xem một số thuộc tính cùng một lúc.
John Doe

Không, nhật ký chỉ được thực hiện một lần cho mỗi thay đổi trên mỗi thuộc tính được xem. Tại sao nó sẽ được gọi nhiều lần?
Erik Aigner

Phải, ý tôi là nó sẽ không hoạt động tốt nếu chúng ta muốn xem tất cả chúng cùng một lúc.
John Doe

1
Chắc chắn tại sao không? Tôi làm theo cách đó trong một dự án của tôi. changed()được gọi bất cứ khi nào một cái gì đó thay đổi trong một trong hai thuộc tính. Đó là hành vi chính xác giống như khi một hàm cho giá trị kết hợp được cung cấp.
Erik Aigner

1
Điều khác biệt một chút là nếu nhiều mục trong mảng thay đổi cùng một lúc, đồng hồ $ sẽ chỉ kích hoạt trình xử lý một lần thay vì một lần cho mỗi mục thay đổi.
trận đấu

11

Một giải pháp an toàn hơn một chút để kết hợp các giá trị có thể là sử dụng như sau dưới dạng $watchhàm của bạn :

function() { return angular.toJson([item1, item2]) }

hoặc là

$scope.$watch(
  function() {
    return angular.toJson([item1, item2]);
  },
  function() {
    // Stuff to do after either value changes
  });

5

$ xem tham số đầu tiên có thể là biểu thức góc hoặc hàm. Xem tài liệu về $ scope. $ Watch . Nó chứa rất nhiều thông tin hữu ích về cách phương thức $ watch hoạt động: khi watchExpression được gọi, cách so sánh kết quả, v.v.


4

làm thế nào về:

scope.$watch(function() { 
   return { 
      a: thing-one, 
      b: thing-two, 
      c: red-fish, 
      d: blue-fish 
   }; 
}, listener...);

2
Nếu không có truetham số thứ ba tới scope.$watch(như trong ví dụ của bạn), listener()sẽ kích hoạt mọi lúc, bởi vì hàm băm ẩn danh có một tham chiếu khác nhau mỗi lần.
Peter V. Mørch

Tôi tìm thấy giải pháp này làm việc cho tình huống của tôi. Đối với tôi, nó giống như : return { a: functionA(), b: functionB(), ... }. Sau đó tôi kiểm tra các đối tượng được trả về của các giá trị mới và cũ đối với nhau.
RevNoah

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

Ở đây hàm sẽ được gọi khi cả giá trị tuổi và tên được thay đổi.


2
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 cạnh chuyển vào cuộc gọi lại bởi vì chúng sẽ là kết nối kết quả và không chỉ là trường đã được thay đổi
Akash Shinde

1

Angular được giới thiệu $watchGrouptrong phiên bản 1.3 sử dụng chúng ta có thể xem nhiều biến, với một $watchGroupkhối duy nhất $watchGrouplấy mảng làm tham số đầu tiên trong đó chúng ta có thể bao gồm tất cả các biến để xem.

$scope.$watchGroup(['var1','var2'],function(newVals,oldVals){
   console.log("new value of var1 = " newVals[0]);
   console.log("new value of var2 = " newVals[1]);
   console.log("old value of var1 = " oldVals[0]);
   console.log("old value of var2 = " oldVals[1]);
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.