Tonton beberapa atribut $ scope

441

Apakah ada cara untuk berlangganan acara menggunakan banyak objek $watch

Misalnya

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

Jawaban:

585

Mulai dari AngularJS 1.3 ada metode baru yang disebut $watchGroupuntuk mengamati serangkaian ekspresi.

$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 
});
Paolo Moretti
sumber
7
apa bedanya dengan $ watchCollection ('[a, b]') ??
Kamil Tomšík
28
Perbedaannya adalah bahwa Anda dapat menggunakan array yang tepat alih-alih string yang terlihat seperti array
Paolo Moretti
2
Saya memiliki masalah yang sama tetapi menggunakan pola controllerAs. Dalam kasus saya, perbaikannya terdiri atas variabel dengan nama kontroler:$scope.$watchGroup(['mycustomctrl.foo', 'mycustomctrl.bar'],...
morels
1
Ini sepertinya tidak berfungsi untuk saya pada Angular 1.6. Jika saya menempatkan konsol log pada fungsi, saya dapat melihat bahwa itu hanya berjalan sekali dengan newValuesdan oldValuessebagai undefined, sementara individual menonton sifat bekerja seperti biasa.
Alejandro García Iglesias
290

Dimulai dengan AngularJS 1.1.4 Anda dapat menggunakan $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
});

Contoh plunker di sini

Dokumentasi di sini

Răzvan Flavius ​​Panda
sumber
109
Perhatikan bahwa parameter pertama bukan array. Ini adalah string yang terlihat seperti array.
MK Safi
1
Terima kasih untuk ini. jika itu bekerja untuk saya, saya akan memberi Anda seribu koin emas - yang virtual, tentu saja :)
AdityaSaxena
5
Agar jelas (butuh beberapa menit untuk saya sadari) $watchCollectiondimaksudkan untuk menyerahkan objek dan memberikan arloji untuk perubahan pada salah satu properti objek, sedangkan $watchGroupdimaksudkan untuk menyerahkan serangkaian properti individual untuk mengawasi perubahan. Sedikit berbeda untuk mengatasi masalah serupa namun berbeda. Fiuh!
Mattygabe
1
Entah bagaimana, Nilai baru dan nilai lama selalu sama ketika Anda menggunakan metode ini: plnkr.co/edit/SwwdpQcNf78pQ80hhXMr
mostruash
Terima kasih. Itu memecahkan masalah saya. Apakah ada masalah kinerja / penggunaan $ watch?
Syed Nasir Abbas
118

$watch parameter pertama juga bisa menjadi fungsi.

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

Jika dua nilai gabungan Anda sederhana, parameter pertama hanyalah ekspresi sudut secara normal. Misalnya, firstName dan lastName:

$scope.$watch('firstName + lastName', function() {
  //stuff
});
Andrew Joslin
sumber
8
Ini seperti kasus penggunaan yang menyerukan untuk dimasukkan dalam kerangka kerja secara default. Bahkan properti yang dihitung sesederhana fullName = firstName + " " + lastNamemengharuskan lewatnya fungsi sebagai argumen pertama (daripada ekspresi sederhana seperti 'firstName, lastName'). Angular sangat kuat, tetapi ada beberapa area di mana ia bisa sangat ramah pengembang dengan cara yang tidak (tampaknya) mengganggu kinerja atau prinsip-prinsip desain.
XML
4
Well $ watch hanya mengambil ekspresi sudut, yang dievaluasi pada ruang lingkup yang dipanggil. Jadi kamu bisa melakukannya $scope.$watch('firstName + lastName', function() {...}). Anda juga bisa hanya melakukan dua jam tangan: function nameChanged() {}; $scope.$watch('firstName', nameChanged); $scope.$watch('lastName', nameChanged);. Saya menambahkan kasus sederhana ke jawabannya.
Andrew Joslin
Nilai tambah dalam 'firstName + lastName' adalah rangkaian string. Jadi angular hanya memeriksa apakah hasil dari rangkaian berbeda sekarang.
mb21
16
Penggabungan string membutuhkan sedikit perhatian - jika Anda mengubah "paran orman" menjadi "para norman" daripada Anda tidak akan memicu respons; terbaik untuk meletakkan pembagi seperti "\ t | \ t" antara istilah pertama dan kedua, atau hanya berpegang pada JSON yang pasti
Dave Edelhart
Meskipun amberjs menyebalkan, tetapi ia memiliki fitur ini built in! mendengar bahwa orang sudut. Saya kira melakukan jam tangan adalah cara yang paling rasional yang bisa dilakukan.
Aladdin Mhemed
70

Inilah solusi yang sangat mirip dengan kode semu asli Anda yang benar-benar berfungsi:

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

EDIT: Oke, saya pikir ini lebih baik:

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

Pada dasarnya kita melewatkan langkah json, yang tampaknya bodoh untuk memulai, tetapi tidak berhasil tanpa itu. Kuncinya adalah parameter ke-3 yang sering dihilangkan yang mengaktifkan kesetaraan objek dan bukan kesetaraan referensi. Kemudian perbandingan antara objek array yang kami buat benar-benar berfungsi dengan baik.

Karen Zilles
sumber
Saya punya pertanyaan serupa. Dan ini jawaban yang tepat untuk saya.
Calvin Cheng
Keduanya memiliki sedikit overhead kinerja jika item dalam ekspresi array bukan tipe sederhana.
null
Itu benar .. itu melakukan perbandingan kedalaman penuh dari objek komponen. Yang mungkin atau mungkin tidak apa yang Anda inginkan. Lihat jawaban yang saat ini disetujui untuk versi menggunakan sudut modern yang tidak melakukan perbandingan kedalaman penuh.
Karen Zilles
Untuk beberapa alasan, ketika saya mencoba menggunakan $ watchCollection melalui array, saya mendapatkan kesalahan TypeError: Object #<Object> has no method '$watchCollection'ini tetapi solusi ini membantu saya untuk menyelesaikan masalah saya!
abottoni
Keren, Anda bahkan dapat menonton nilai di dalam objek:$scope.$watch('[chaptercontent.Id, anchorid]', function (newValues, oldValues) { ... }, true);
Serge van den Oever
15

Anda dapat menggunakan fungsi dalam $ watchGroup untuk memilih bidang objek dalam ruang lingkup.

        $scope.$watchGroup(
        [function () { return _this.$scope.ViewModel.Monitor1Scale; },   
         function () { return _this.$scope.ViewModel.Monitor2Scale; }],  
         function (newVal, oldVal, scope) 
         {
             if (newVal != oldVal) {
                 _this.updateMonitorScales();
             }
         });
Yang Zhang
sumber
1
Mengapa Anda menggunakan _this? Bukankah ruang lingkup dimasukkan ke dalam fungsi tanpa secara eksplisit mendefinisikannya?
sg.cc
1
Saya menggunakan naskah, kode yang disajikan adalah hasil kompilasi.
Yang Zhang
12

Mengapa tidak membungkusnya dengan forEach?

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

Ini tentang overhead yang sama dengan menyediakan fungsi untuk nilai gabungan, tanpa benar-benar harus khawatir tentang komposisi nilai .

Erik Aigner
sumber
Dalam hal ini log () akan dieksekusi beberapa kali, bukan? Saya pikir dalam kasus fungsi $ nested menonton itu tidak akan. Dan seharusnya tidak dalam solusi untuk menonton beberapa atribut sekaligus.
John Doe
Tidak, log hanya dijalankan sekali per perubahan per properti yang ditonton. Mengapa itu diminta beberapa kali?
Erik Aigner
Benar, maksud saya itu tidak akan berhasil jika kita ingin menonton semuanya secara bersamaan.
John Doe
1
Tentu, mengapa tidak? Saya melakukannya dengan cara seperti itu dalam proyek saya. changed()dipanggil setiap kali ada perubahan di salah satu properti. Itu perilaku yang sama persis seolah-olah fungsi untuk nilai gabungan disediakan.
Erik Aigner
1
Ini sedikit berbeda karena jika beberapa item dalam array berubah pada saat yang sama, $ watch hanya akan memecat handler sekali alih-alih satu kali per item berubah.
bingles
11

Solusi yang sedikit lebih aman untuk menggabungkan nilai mungkin menggunakan yang berikut ini sebagai $watchfungsi Anda :

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

atau

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

Parameter $ watch first dapat berupa ekspresi atau fungsi sudut . Lihat dokumentasi pada $ scope. $ Watch . Ini berisi banyak info berguna tentang cara kerja metode $ watch: ketika watchExpression dipanggil, cara angular membandingkan hasil, dll.

Artem Andreev
sumber
4

bagaimana tentang:

scope.$watch(function() { 
   return { 
      a: thing-one, 
      b: thing-two, 
      c: red-fish, 
      d: blue-fish 
   }; 
}, listener...);
wacamo
sumber
2
Tanpa trueparameter ketiga to to scope.$watch(seperti pada contoh Anda), listener()akan diaktifkan setiap kali, karena hash anonim memiliki referensi yang berbeda setiap saat.
Peter V. Mørch
Saya menemukan solusi ini berfungsi untuk situasi saya. Bagi saya, itu lebih seperti: return { a: functionA(), b: functionB(), ... }. Saya kemudian memeriksa objek yang dikembalikan nilai baru dan lama terhadap satu sama lain.
RevNoah
4
$scope.$watch('age + name', function () {
  //called when name or age changed
});

Di sini fungsi akan dipanggil saat usia dan nilai nama diubah.

Akash Shinde
sumber
2
Juga pastikan dalam hal ini tidak menggunakan argumen newValue dan oldValue bahwa sudut masuk ke dalam callback karena mereka akan menjadi gabungan yang dihasilkan dan bukan hanya bidang yang diubah
Akash Shinde
1

Sudut diperkenalkan $watchGroupdalam versi 1.3 menggunakan yang kita dapat menonton beberapa variabel, dengan satu $watchGroupblok $watchGroupmengambil array sebagai parameter pertama di mana kita dapat memasukkan semua variabel kita untuk menonton.

$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]);
});
Vinit Solanki
sumber