Angularjs'de bir dizi nasıl derin izlenir?


320

Kapsamımda bir dizi nesne var, her nesnenin tüm değerlerini izlemek istiyorum.

Bu benim kodum:

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

Ben değerleri, mesela ben değişikliğini değiştirmek Ama TITLEüzere TITLE2, alert('columns changed')attı asla.

Bir dizi içindeki nesneleri nasıl derinden izleyebilirim?

Canlı bir demo var: http://jsfiddle.net/SYx9b/

Yanıtlar:


529

Sen 3. argüman ayarlayabilirsiniz $watchiçin true:

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

Bkz. Https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch

Angular 1.1.x sürümünden bu yana, koleksiyonun sığ saatini (yalnızca "ilk seviyesi") izlemek için $ watchCollection öğesini de kullanabilirsiniz.

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

Bkz. Https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchKoleksiyon


2
angular.equalsÜçüncü argüman bir boole değeri aldığında neden kullanıyorsunuz ?
JayQuerie.com

Teşekkürler Trevor, belgelerin yanlış anlaşılması. Yukarıdaki kodu güncelledim ve kodumu eşleştirmek için güncelledim.
Piran

36
1.1.x şimdi var$watchCollection
Jonathan Rowny

39
$watchCollectionanladığım gibi, sadece bir dizi veya nesnenin "ilk seviyesi" izleyecektir. Bundan daha derin izlemeniz gerekiyorsa yukarıdaki cevap doğrudur. bennadel.com/blog/…
Blazemonger

1
Büyük cevap ... yapabilseydim sana birden fazla oy
verirdim

50

$ Saatinizde bir nesneye derinlemesine dalış yapmanın performans sonuçları vardır. Bazen (örneğin, değişiklikler yalnızca push ve pop'lar olduğunda), array.length gibi kolayca hesaplanan bir değeri $ izlemek isteyebilirsiniz.


1
Bunun daha fazla oyu olmalı. Derin izleme pahalıdır. OP'nin derin izlemeyi aradığını anlıyorum, ama insanlar buraya dizinin kendisinin değişip değişmediğini bilmek isteyebilirler. Bu durumda uzunluğu izlemek çok daha hızlıdır.
Scott Silvi

42
Bu bir yorum olmalı, cevap değil.
Blazemonger

1
@Blazemonger yorumunda belirtildiği gibi $ watchCollection kullanılarak performans sorunları en aza indirilebilir ( stackoverflow.com/questions/14712089#comment32440226_14713978 ).
Sean the Bean

Düzgün tavsiye. Bu yorumun bir cevap olmadığı konusunda, benim düşünceme göre, bir cevap için kabul kriterlerini yerine getirme önerisini hazırlamak daha iyi olacaktır. Bu yaklaşımla soruya zarif bir çözüm sağlanabileceğini düşünüyorum.
Amy Pellegrini

43

Yalnızca bir diziyi izleyecekseniz, bu kod parçasını kullanabilirsiniz:

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

misal

Ancak bu, çoklu dizilerle çalışmaz:

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

misal

Bu durumu ele almak için genellikle izlemek istediğim çoklu dizileri JSON'a dönüştürürüm:

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

misal

@Jssebastian'ın yorumlarda belirttiği gibi, '$' ile başlayan üyeleri ve diğer olası durumları ele JSON.stringifyalabileceği için tercih angular.toJsonedilebilir.


Ben de bir 3. parametre olduğunu bulmak $watch, aynı yapabilir miyim? Pass true as a third argument to watch an object's properties too.Bakınız: cheatography.com/proloser/cheat-sheets/angularjs
Freewind

@Freewind Birden fazla diziyi izlemeniz gereken bir durum varsa, burada görüldüğü gibi başarısız olur . Ancak evet, bu da işe yarayacak ve angular.toJsontek bir dizide kullanmakla aynı işlevselliği sağlayacaktır .
JayQuerie.com

2
Sadece angular.toJson '$' ile başlayan üyeleri dahil gibi görünmüyor dikkat edin: angular.toJson ({"$ hello": "world"}) sadece "{}". Alternatif olarak JSON.stringify () kullandım
jssebastian

@ jssebastian Teşekkürler - Cevabı bu bilgileri içerecek şekilde güncelledim.
JayQuerie.com

1
hangi mülkün değiştiğini öğrenebilir miyiz?
Ashwin

21

Angular 1.1.x ve üzeri sürümlerde artık $ watch yerine $ watchCollection kullanabileceğinizi belirtmek gerekir . $ WatchCollection sığ saatler oluşturuyor gibi görünse de beklediğiniz gibi nesnelerin dizileriyle çalışmaz. Diziye yapılan eklemeleri ve silmeleri algılayabilir, ancak diziler içindeki nesnelerin özelliklerini algılayamaz.


5
Bununla birlikte, sadece sığ olanları izliyor. Anladığım kadarıyla, dizinin öğelerinin özelliklerini değiştirmek (soruda olduğu gibi) olayı tetiklemez.
Tarnschaf

3
Bu bir yorum olmalı, cevap değil.
Blazemonger

18

Bir kapsam değişkenini izlemenin 3 yolunun örneklerle karşılaştırılması:

$ watch () tarafından tetiklenir:

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

$ watchCollection () yukarıdaki her şey tarafından tetiklenir VE:

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

$ watch (..., true) yukarıdaki HER ŞEY tarafından tetiklenir VE:

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

SADECE BİR ŞEY DAHA...

$ watch () , diğer dizi aynı içeriğe sahip olsa bile bir dizi başka bir dizi ile değiştirildiğinde tetiklenen tek olandır.

Örneğin, nerede $watch()ateş eder ve $watchCollection()olmazdı:

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

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

$scope.myArray = newArray;

Aşağıda, farklı saat kombinasyonlarını kullanan ve hangi "saatlerin" tetiklendiğini belirtmek için günlük iletilerini kullanan örnek bir JSFiddle bağlantısı bulunmaktadır:

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


Kesinlikle, özellikle 'BİR DAHA FAZLA ŞEY' not etmek
Andy Ma

12

$ watchCollection ne yapmak istediğinizi başarır. Aşağıda, http://docs.angularjs.org/api/ng/type/$rootScope.Scope web sitesinden kopyalanan bir örnek bulunmaktadır. Uygun olsa da, özellikle büyük bir koleksiyonu izlerken performansın dikkate alınması gerekir.

  $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);

14
OP bir nesne dizisi belirledi. Örneğiniz bir dizeyle çalışır, ancak $ watchCollection bir nesne dizisiyle çalışmaz.
KevinL

4

Bu çözüm benim için çok iyi çalıştı, bunu bir direktifte yapıyorum:

scope. $ watch (attrs.testWatch, function () {.....}, doğru);

gerçek oldukça iyi çalışır ve tüm chnages için tepki (bir alan eklemek, silmek veya değiştirmek).

İşte onunla oynamak için çalışan bir dalgıç.

AngularJS'de Bir Diziyi Derin İzliyor

Umarım bu sizin için yararlı olabilir. Herhangi bir sorunuz varsa, sormaya çekinmeyin, yardım etmeye çalışacağım :)


4

Benim durumumda, diğer denetleyiciler tarafından da izlenen bir adres nesnesi içeren bir hizmeti izlemem gerekiyordu. Nesneleri izlerken başarının anahtarı gibi görünen 'true' parametresini ekleyene kadar bir döngüde kaldım.

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

1

Fonksiyonun objectEqualityparametresini (üçüncü parametre) ayarlamak, $watchdizinin TÜM özelliklerini izlemek için kesinlikle doğru yoldur.

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

Piran buna yeterince cevap veriyor ve bahsediyor $watchCollection.

Daha fazla detay

Zaten cevaplanmış bir soruyu cevaplamamın nedeni, wizardwerdna'nın cevabının iyi olmadığını ve kullanılmaması gerektiğini belirtmek istiyorum.

Sorun, sindirimlerin hemen gerçekleşmemesi. Yürütmeden önce geçerli kod bloğunun tamamlanmasını beklemek zorundalar. Böylece, lengthbir dizi izlemek aslında $watchCollectionyakalayacak bazı önemli değişiklikleri kaçırabilir .

Bu yapılandırmayı kabul edin:

$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');
});

İlk bakışta, bu durumda olduğu gibi aynı anda ateş ediyor gibi görünebilir:

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

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

Bu yeterince iyi çalışıyor, ancak şunu düşünün:

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

// Console output
// testArray changed

Dizi böylece seyretmek gibi, yeni bir öğe vardır ve bir öğe kaybetti gerçi çıkan uzunluk aynı hatta oldu Bildirimi olduğu $watchkonusu olduğunda, lengthdeğişmedi. $watchCollectionYine de aldı.

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

// Console output
// testArray change

Aynı sonuç, aynı blokta bir it ve patla ile olur.

Sonuç

$watchDizideki her özelliği izlemek için , üçüncü parametrenin (objectEquality) dahil olduğu dizinde bir a kullanın ve true olarak ayarlayın. Evet, bu pahalı ama bazen gerekli.

Nesne diziye girdiğinde / diziden çıktığında izlemek için a tuşunu kullanın $watchCollection.

Bir KULLANMAYIN $watchüzerine lengthdizinin özelliği. Bunu düşünebilmem için neredeyse hiçbir iyi neden yok.


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>

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.