AngularJS'de model değişikliklerini izlerken ilk yükü nasıl yoksayabilirim?


184

$ Scope.fieldcontainer özelliğinde derin bir grafik olarak oturur tek bir varlık için editör olarak hizmet veren bir web sayfam var. REST API'mdan ($ resource aracılığıyla) bir yanıt aldıktan sonra, 'fieldcontainer'a bir saat ekliyorum. Bu saati sayfanın / varlığın "kirli" olup olmadığını tespit etmek için kullanıyorum. Şu anda kaydet düğmesi sıçrama yapıyorum ama gerçekten kullanıcı modeli yönlendirene kadar kaydet düğmesini görünmez yapmak istiyorum.

Ne alıyorum saatin tek bir tetikleyici olduğunu düşünüyorum, çünkü olduğunu düşünüyorum .fieldcontainer = ... atama saatimi oluşturduktan hemen sonra gerçekleşir. Sadece ilk yanlış alarmı emmek için bir "dirtyCount" özelliği kullanmayı düşünüyordum ama bu çok acayip hissediyor ... ve bununla başa çıkmak için "Açısal deyimsel" bir yol olması gerektiğini düşündüm - Ben tek değilim kirli bir modeli tespit etmek için bir saat kullanmak.

İşte saatimi ayarladığım kod:

 $scope.fieldcontainer = Message.get({id: $scope.entityId },
            function(message,headers) {
                $scope.$watch('fieldcontainer',
                    function() {
                        console.log("model is dirty.");
                        if ($scope.visibility.saveButton) {
                            $('#saveMessageButtonRow').effect("bounce", { times:5, direction: 'right' }, 300);
                        }
                    }, true);
            });

Ben sadece "if (dirtyCount> 0)" ile "UI kirli" kodumu korumak daha bunu yapmak için daha temiz bir yol olması gerektiğini düşünüyorum devam ...


Ayrıca belirli düğmeler (örneğin varlığın önceki bir sürümünü yükleme) dayalı $ scope.fieldcontainer yeniden yüklemek gerekir. Bunun için saati askıya almam, yeniden yüklemem ve sonra saati sürdürmem gerekir.
Kevin Hoffman

Cevabımı kabul edilen cevap olarak değiştirmeyi düşünür müsünüz? Oraya kadar okumak için zaman ayıranlar bunun doğru çözüm olduğu konusunda hemfikirdirler.
trixtur

@trixtur Aynı OP sorunum var, ancak benim durumumda cevabınız çalışmıyor, çünkü eski değerim yok undefined. Model güncellememin tüm bilgilerle gelmemesi durumunda gerekli olan varsayılan bir değere sahiptir. Bu yüzden bazı değerler değişmez, ancak tetiklenmesi gerekir.
oliholz

@oliholz ​​Yani tanımlanamayanlara karşı kontrol etmek yerine, "typeof" 'i çıkarınız ve old_fieldcontainer'ı varsayılan değerinize göre kontrol ediniz.
trixtur

@KevinHoffman, bu soruyu bulan diğer insanlar için MW'nin cevabını doğru cevapta işaretlemelisiniz. Çok daha iyi bir çözüm. Teşekkürler!
Dev01

Yanıtlar:


118

ilk yüklemeden hemen önce bir bayrak belirleyin,

var initializing = true

ve sonra ilk $ saat patladığında

$scope.$watch('fieldcontainer', function() {
  if (initializing) {
    $timeout(function() { initializing = false; });
  } else {
    // do whatever you were going to do
  }
});

Bayrak, geçerli özet döngüsünün hemen sonunda yırtılacaktır, bu nedenle bir sonraki değişiklik engellenmeyecektir.


17
Evet, bu işe yarar ama @ MW tarafından önerilen eski ve yeni değer yaklaşımını karşılaştırmak. hem daha basit hem de daha Açısal deyimsel. Kabul edilen cevabı güncelleyebilir misiniz?
gerryster

2
Her zaman eskiDeğeri yeni değere eşit olarak ayarlamak İlk yangıntaki değer, özellikle bu bayrağı kesmek zorunda kalmamak için eklendi
Charlie Martin

2
Eski değerin yeni değeri, eski değerin tanımlanmadığı durumla başa çıkmayacağından MW'nin cevabı aslında yanlıştır.
trixtur

1
Yorumcular için: Bu bir tanrı modeli değildir, hiçbir şey 'başlatılmış' değişkeni bir dekoratörün kapsamına koymanızı ve sonra "normal" saatinizi onunla dekore etmenizi engellemez. Her durumda, bu sorunun kapsamı dışındadır.
yeniden yazıldı

1
Önerilen yöntemleri karşılaştırmak için plnkr.co/edit/VrWrHHWwukqnxaEM3J5i adresine bakın ; hem .get () geri çağrısında gözlemcileri ayarlama hem de kapsam başlatma.
yeniden yazıldı

483

Dinleyici ilk kez çağrıldığında, eski değer ve yeni değer aynı olacaktır. Sadece şunu yapın:

$scope.$watch('fieldcontainer', function(newValue, oldValue) {
  if (newValue !== oldValue) {
    // do whatever you were going to do
  }
});

Aslında bu, Açısal dokümanların ele almasını önerdiği yöntem :

Bir izleyici kapsamla kaydedildikten sonra, dinleyiciyi başlatmak için fn dinleyicisi eşzamansız olarak ($ ​​evalAsync aracılığıyla) çağrılır. Nadir durumlarda, bu istenmeyen bir durumdur çünkü watchExpression sonucu değişmediğinde dinleyici çağrılır. Bu senaryoyu dinleyici fn içinde algılamak için newVal ve oldVal değerlerini karşılaştırabilirsiniz. Bu iki değer aynı ise (===) dinleyici başlatma nedeniyle çağrıldı


3
bu şekilde @
rewritten

25
Bu doğru değil. Nokta null, hiçbir değişiklik olmadığında değişikliği göz ardı etmemek için başlangıç ​​yük değerine geçişi yok saymaktır.
yeniden yazıldı

1
Dinleyici işlevi, saat oluşturulduğunda her zaman tetiklenir. Bu ilk çağrıyı görmezden geliyor, ki ben askerin istediğine inanıyorum. Eski değer null olduğunda değişikliği özellikle göz ardı etmek istiyorsa, elbette oldValue ile null değerini karşılaştırabilir ... ama yapmak istediğini sanmıyorum.
MW.

OP özellikle kaynaklarla ilgili izleyicileri soruyor (REST servislerinden). Kaynaklar önce yaratılır, ardından izleyiciler uygulanır ve sonra yanıttan öznitelikler yazılır ve ancak o zaman Angular bir döngü oluşturur. İlk döngüyü "başlatma" bayrağıyla atlamak, bir izleyiciyi yalnızca başlatılan kaynaklara uygulamak için bir yol sağlar, ancak yine de / noktasındaki değişiklikleri izler null.
yeniden yazıldı

7
Bu yanlış, oldValueilk gidişte geçersiz olacak.
Kevin C.

42

Bu sorunun cevaplandığının farkındayım, ancak bir öneri var:

$scope.$watch('fieldcontainer', function (new_fieldcontainer, old_fieldcontainer) {
    if (typeof old_fieldcontainer === 'undefined') return;

    // Other code for handling changed object here.
});

Bayrakları kullanmak işe yarıyor ama biraz kod kokusu var sanmıyorum?


1
Bu yol. Coffeescript'te, (CS'deki return unless oldValue?varoluşçu işleç) kadar basittir .
Kevin C.

1
Kelime kodu kokusu sahne! Ayrıca tanrı nesne lol göz atın. çok iyi.
Matthew Harwood

1
Her ne kadar bu kabul edilen cevap olmasa da, bir API'dan bir nesneyi doldururken kodumu çalıştırdı ve farklı kaydetme durumlarını izlemek istiyorum. Çok hoş.
Lucas Reppe Welander

1
Teşekkürler - bu bana yardımcı oldu
Wand Maker

1
Bu harika, ancak benim durumumda benim API AJAX çağrısı yapmadan önce verileri boş bir dizi olarak başlatmıştı, bu yüzden tür yerine uzunluğunu kontrol edin. Ancak bu cevap en azından en etkili yöntemi kesinlikle göstermektedir.
Ocak 17'de Saborknight

4

Mevcut değerlerin ilk yüklenmesi sırasında eski değer alanı tanımsızdır. Aşağıdaki örnek, ilk yüklemeleri hariç tutmanıza yardımcı olur.

$scope.$watch('fieldcontainer', 
  function(newValue, oldValue) {
    if (newValue && oldValue && newValue != oldValue) {
      // here what to do
    }
  }), true;

Muhtemelen kullanmak istiyorsunuz! == nullve undefinedbu durumda eşleşecek veya ( '1' == 1vb.)
Jamie Pate

genel olarak haklısın. Ancak bu durumda, önceki denetimler nedeniyle newValue ve oldValue bu köşe değerleri olamaz. Aynı alana ait olduğu için her iki değer de aynı türe sahiptir. Teşekkür ederim.
Tahsin Turkoz

3

Sadece yeni val'in durumunu geçerli kılın:

$scope.$watch('fieldcontainer',function(newVal) {
      if(angular.isDefined(newVal)){
          //Do something
      }
});
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.