Bagaimana cara menonton array dalam angularjs?

320

Ada array objek dalam lingkup saya, saya ingin menonton semua nilai dari setiap objek.

Ini kode saya:

function TodoCtrl($scope) {
  $scope.columns = [
      { field:'title', displayName: 'TITLE'},
      { field: 'content', displayName: 'CONTENT' }
  ];
   $scope.$watch('columns', function(newVal) {
       alert('columns changed');
   });
}

Tapi ketika saya mengubah nilai-nilai, misalnya saya perubahan TITLEuntuk TITLE2, yang alert('columns changed')tidak pernah muncul.

Bagaimana cara mengamati objek di dalam array secara mendalam?

Ada demo langsung: http://jsfiddle.net/SYx9b/

Freewind
sumber

Jawaban:

529

Anda dapat mengatur argumen ke-3 $watchmenjadi true:

$scope.$watch('data', function (newVal, oldVal) { /*...*/ }, true);

Lihat https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch

Sejak Angular 1.1.x Anda juga dapat menggunakan $ watchCollection untuk menonton arloji dangkal (hanya "level pertama" dari) koleksi.

$scope.$watchCollection('data', function (newVal, oldVal) { /*...*/ });

Lihat https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchCollection

Piran
sumber
2
Mengapa Anda menggunakan angular.equalsketika argumen ketiga mengambil nilai boolean ?
JayQuerie.com
Terima kasih Trevor, salah membaca dokumen. Saya telah memperbarui kode di atas, dan memperbarui kode saya untuk mencocokkan.
Piran
36
di 1.1.x sekarang Anda miliki$watchCollection
Jonathan Rowny
39
$watchCollectionhanya akan menonton "level pertama" dari array atau objek, seperti yang saya mengerti. Jawaban di atas benar jika Anda perlu menonton lebih dalam dari itu. bennadel.com/blog/…
Blazemonger
1
Jawaban yang bagus ... akan memberi Anda lebih dari satu suara jika saya bisa ... :) terima kasih
Jony-Y
50

Ada konsekuensi kinerja untuk menyelam dalam suatu objek di $ watch Anda. Terkadang (misalnya, ketika perubahan hanya mendorong dan muncul), Anda mungkin ingin $ menonton nilai yang mudah dihitung, seperti array.length.

wizardwerdna
sumber
1
Ini harus memiliki lebih banyak suara. Menonton yang dalam itu mahal. Saya mengerti OP sedang mencari pengamatan mendalam, tetapi orang-orang mungkin datang ke sini hanya ingin tahu apakah array itu sendiri berubah. Menonton panjangnya jauh lebih cepat, dalam hal ini.
Scott Silvi
42
Ini harus berupa komentar, bukan jawaban.
Blazemonger
1
Masalah kinerja akan diminimalkan dengan menggunakan $ watchCollection, seperti yang disebutkan oleh komentar @Blazemonger ( stackoverflow.com/questions/14712089#comment32440226_14713978 ).
Sean the Bean
Nasihat rapi Mengenai komentar ini tidak menjadi jawaban, menurut pendapat saya akan lebih baik untuk menguraikan saran untuk memenuhi kriteria penerimaan untuk jawaban. Saya pikir dengan pendekatan ini solusi elegan untuk pertanyaan dapat dicapai.
Amy Pellegrini
43

Jika Anda hanya akan menonton satu array, Anda dapat menggunakan sedikit kode ini:

$scope.$watch('columns', function() {
  // some value in the array has changed 
}, true); // watching properties

contoh

Tetapi ini tidak akan berfungsi dengan banyak array:

$scope.$watch('columns + ANOTHER_ARRAY', function() {
  // will never be called when things change in columns or ANOTHER_ARRAY
}, true);

contoh

Untuk menangani situasi ini, saya biasanya mengonversi banyak array yang ingin saya tonton menjadi JSON:

$scope.$watch(function() { 
  return angular.toJson([$scope.columns, $scope.ANOTHER_ARRAY, ... ]); 
},
function() {
  // some value in some array has changed
}

contoh

Seperti @jssebastian tunjukkan dalam komentar, JSON.stringifymungkin lebih disukai angular.toJsonkarena dapat menangani anggota yang mulai dengan '$' dan kemungkinan kasus lainnya juga.

JayQuerie.com
sumber
Saya juga menemukan ada parameter ke-3 $watch, apakah bisa melakukan hal yang sama? Pass true as a third argument to watch an object's properties too.Lihat: cheatography.com/proloser/cheat-sheets/angularjs
Freewind
@Freewind Jika ada kasus di mana Anda perlu menonton beberapa array, itu akan gagal seperti yang terlihat di sini . Tapi ya, itu akan bekerja juga & menyediakan fungsionalitas yang sama seperti menggunakan angular.toJsonpada satu array.
JayQuerie.com
2
Berhati-hatilah karena angular.toJson tampaknya tidak menyertakan anggota yang mulai dengan '$': angular.toJson ({"$ hello": "world"}) hanyalah "{}". Saya menggunakan JSON.stringify () sebagai alternatif
jssebastian
@ jssebastian Terima kasih - Saya telah memperbarui jawaban untuk memasukkan informasi itu.
JayQuerie.com
1
dapatkah kita tahu properti mana yang telah berubah?
Ashwin
21

Perlu dicatat bahwa dalam Angular 1.1.x dan di atasnya, Anda sekarang dapat menggunakan $ watchCollection daripada $ watch. Meskipun $ watchCollection tampaknya membuat arloji dangkal sehingga tidak akan berfungsi dengan array objek seperti yang Anda harapkan. Ia dapat mendeteksi penambahan dan penghapusan array, tetapi bukan properti objek di dalam array.

Jonathan Rowny
sumber
5
$ watchCollection hanya menonton dangkal. Jadi seperti yang saya pahami, mengubah properti item array (seperti dalam pertanyaan) tidak akan memicu acara.
Tarnschaf
3
Ini harus berupa komentar, bukan jawaban.
Blazemonger
18

Berikut ini adalah perbandingan dari 3 cara Anda dapat menonton variabel lingkup dengan contoh:

$ watch () dipicu oleh:

$scope.myArray = [];
$scope.myArray = null;
$scope.myArray = someOtherArray;

$ watchCollection () dipicu oleh semua yang di atas DAN:

$scope.myArray.push({}); // add element
$scope.myArray.splice(0, 1); // remove element
$scope.myArray[0] = {}; // assign index to different value

$ watch (..., true) dipicu oleh SEMUANYA di atas DAN:

$scope.myArray[0].someProperty = "someValue";

HANYA SATU LAGI...

$ tonton () adalah satu-satunya yang memicu ketika array diganti dengan array lain bahkan jika array lain itu memiliki konten yang persis sama.

Misalnya di mana $watch()akan menembak dan $watchCollection()tidak akan:

$scope.myArray = ["Apples", "Bananas", "Orange" ];

var newArray = [];
newArray.push("Apples");
newArray.push("Bananas");
newArray.push("Orange");

$scope.myArray = newArray;

Di bawah ini adalah tautan ke contoh JSFiddle yang menggunakan semua kombinasi arloji dan pesan log keluaran yang berbeda untuk menunjukkan "jam tangan" mana yang dipicu:

http://jsfiddle.net/luisperezphd/2zj9k872/

Luis Perez
sumber
Tepat, terutama untuk mencatat 'SATU HAL YANG LEBIH BANYAK'
Andy Ma
12

$ watchCollection menyelesaikan apa yang ingin Anda lakukan. Di bawah ini adalah contoh yang disalin dari situs web angularjs http://docs.angularjs.org/api/ng/type/$rootScope.Scope Meskipun nyaman, kinerja perlu dipertimbangkan terutama ketika Anda menonton koleksi besar.

  $scope.names = ['igor', 'matias', 'misko', 'james'];
  $scope.dataCount = 4;

  $scope.$watchCollection('names', function(newNames, oldNames) {
     $scope.dataCount = newNames.length;
  });

  expect($scope.dataCount).toEqual(4);
  $scope.$digest();

  //still at 4 ... no changes
  expect($scope.dataCount).toEqual(4);

  $scope.names.pop();
  $scope.$digest();

  //now there's been a change
  expect($scope.dataCount).toEqual(3);
Jin
sumber
14
OP menentukan array objek. Contoh Anda berfungsi dengan array string, tetapi $ watchCollection tidak berfungsi dengan array objek.
KevinL
4

Solusi ini bekerja sangat baik untuk saya, saya melakukan ini dalam arahan:

scope. $ watch (attrs.testWatch, function () {.....}, true);

yang sebenarnya berfungsi dengan sangat baik dan bereaksi untuk semua chnage (menambah, menghapus, atau memodifikasi bidang).

Ini adalah plunker yang berfungsi untuk bermain dengannya.

Sangat Menonton Array di AngularJS

Semoga ini bermanfaat bagi Anda. Jika Anda memiliki pertanyaan, jangan ragu untuk bertanya, saya akan mencoba membantu :)

EPotignano
sumber
4

Dalam kasus saya, saya perlu menonton layanan, yang berisi objek alamat juga ditonton oleh beberapa pengontrol lainnya. Saya terjebak dalam satu lingkaran sampai saya menambahkan parameter 'benar', yang tampaknya menjadi kunci keberhasilan ketika menonton objek.

$scope.$watch(function() {
    return LocationService.getAddress();
}, function(address) {
    //handle address object
}, true);
RevNoah
sumber
1

Mengatur objectEqualityparameter (parameter ketiga) dari $watchfungsi jelas merupakan cara yang benar untuk menonton SEMUA properti array.

$scope.$watch('columns', function(newVal) {
    alert('columns changed');
},true); // <- Right here

Piran menjawab ini dengan cukup baik dan menyebutkan $watchCollectionjuga.

Lebih detail

Alasan saya menjawab pertanyaan yang sudah dijawab adalah karena saya ingin menunjukkan bahwa jawaban wizardwerdna bukan jawaban yang baik dan tidak boleh digunakan.

Masalahnya adalah bahwa pencernaan tidak terjadi dengan segera. Mereka harus menunggu sampai blok kode saat ini selesai sebelum dieksekusi. Dengan demikian, menonton lengtharray sebenarnya dapat melewatkan beberapa perubahan penting yang $watchCollectionakan ditangkap.

Asumsikan konfigurasi ini:

$scope.testArray = [
    {val:1},
    {val:2}
];

$scope.$watch('testArray.length', function(newLength, oldLength) {
    console.log('length changed: ', oldLength, ' -> ', newLength);
});

$scope.$watchCollection('testArray', function(newArray) {
    console.log('testArray changed');
});

Sekilas, sepertinya ini akan menyala pada saat yang sama, seperti dalam kasus ini:

function pushToArray() {
    $scope.testArray.push({val:3});
}
pushToArray();

// Console output
// length changed: 2 -> 3
// testArray changed

Itu cukup berhasil, tetapi pertimbangkan ini:

function spliceArray() {
    // Starting at index 1, remove 1 item, then push {val: 3}.
    $testArray.splice(1, 1, {val: 3});
}
spliceArray();

// Console output
// testArray changed

Perhatikan bahwa panjang yang dihasilkan adalah sama meskipun array memiliki elemen baru dan kehilangan elemen, sehingga jam tangan yang $watchterkait lengthtidak berubah. $watchCollectionmengambilnya, meskipun.

function pushPopArray() {
    $testArray.push({val: 3});
    $testArray.pop();
}
pushPopArray();

// Console output
// testArray change

Hasil yang sama terjadi dengan push dan pop di blok yang sama.

Kesimpulan

Untuk menonton setiap properti dalam array, gunakan a $watchpada array itu sendiri dengan parameter ketiga (objectEquality) disertakan dan disetel ke true. Ya, ini mahal tapi terkadang perlu.

Untuk menonton saat objek masuk / keluar dari array, gunakan a $watchCollection.

JANGAN gunakan $watchpada lengthproperti array. Hampir tidak ada alasan bagus yang bisa saya pikirkan untuk melakukannya.

Patrick John Haskins
sumber
0

$scope.changePass = function(data){
    
    if(data.txtNewConfirmPassword !== data.txtNewPassword){
        $scope.confirmStatus = true;
    }else{
        $scope.confirmStatus = false;
    }
};
  <form class="list" name="myForm">
      <label class="item item-input">        
        <input type="password" placeholder="ใส่รหัสผ่านปัจจุบัน" ng-model="data.txtCurrentPassword" maxlength="5" required>
      </label>
      <label class="item item-input">
        <input type="password" placeholder="ใส่รหัสผ่านใหม่" ng-model="data.txtNewPassword" maxlength="5" ng-minlength="5" name="checknawPassword" ng-change="changePass(data)" required>
      </label>
      <label class="item item-input">
        <input type="password" placeholder="ใส่รหัสผ่านใหม่ให้ตรงกัน" ng-model="data.txtNewConfirmPassword" maxlength="5" ng-minlength="5" name="checkConfirmPassword" ng-change="changePass(data)" required>
      </label>      
       <div class="spacer" style="width: 300px; height: 5px;"></div> 
      <span style="color:red" ng-show="myForm.checknawPassword.$error.minlength || myForm.checkConfirmPassword.$error.minlength">รหัสผ่านต้องมีจำนวน 5 หลัก</span><br>
      <span ng-show="confirmStatus" style="color:red">รหัสผ่านใหม่ไม่ตรงกัน</span>
      <br>
      <button class="button button-positive  button-block" ng-click="saveChangePass(data)" ng-disabled="myForm.$invalid || confirmStatus">เปลี่ยน</button>
    </form>

Fluke Klumame
sumber