Knockout.js'de gözlemlenebilir bağlamalar nasıl temizlenir / kaldırılır?


113

Kullanıcının birden çok kez gerçekleştirebileceği bir web sayfasında işlevsellik oluşturuyorum. Kullanıcının eylemi aracılığıyla bir nesne / model oluşturulur ve ko.applyBindings () kullanılarak HTML'ye uygulanır.

Veriye bağlı HTML, jQuery şablonları aracılığıyla oluşturulur.

Çok uzak çok iyi.

İkinci bir nesne / model oluşturarak bu adımı tekrarladığımda ve ko.applyBindings () 'i çağırdığımda iki sorunla karşılaşıyorum:

  1. İşaretleme, önceki nesneyi / modeli ve yeni nesneyi / modeli gösterir.
  2. Nesne / modeldeki özelliklerden biriyle ilgili bir javascript hatası oluşur, ancak yine de biçimlendirmede işlenir.

Bu sorunu aşmak için, ilk geçişten sonra jQuery'nin .empty () yöntemini çağırarak tüm veri bağlama özniteliklerini içeren şablonlu HTML'yi kaldırıyorum, böylece artık DOM'da kalmasın. Kullanıcı ikinci geçiş için işlemi başlattığında, veriye bağlı HTML DOM'a yeniden eklenir.

Ancak dediğim gibi, HTML DOM'a yeniden eklendiğinde ve yeni nesneye / modele yeniden bağlandığında, yine de ilk nesneden / modelden veri içeriyor ve yine de oluşmayan JS hatasını alıyorum ilk geçiş sırasında.

Sonuç olarak, işaretleme DOM'den kaldırılmış olsa bile, Altını Gizleme'nin bu bağlı özellikleri elinde tuttuğu görülmektedir.

Yani aradığım şey, bu bağlı özellikleri Knockout'tan kaldırmanın bir yolu; nakavt artık gözlemlenebilir bir model olmadığını söylemek. Bunu yapmanın bir yolu var mı?

DÜZENLE

Temel süreç, kullanıcının bir dosya yüklemesidir; sunucu daha sonra bir JSON nesnesiyle yanıt verir, veriye bağlı HTML DOM'a eklenir, ardından JSON nesne modeli bu HTML'ye bağlanır.

mn.AccountCreationModel = new AccountViewModel(jsonData.Account);
ko.applyBindings(mn.AccountCreationModel);

Kullanıcı model üzerinde bazı seçimler yaptıktan sonra, aynı nesne sunucuya geri gönderilir, veriye bağlı HTML daha sonra DOM'dan kaldırılır ve ardından aşağıdaki JS'ye sahip olurum

mn.AccountCreationModel = null;

Kullanıcı bunu bir kez daha yapmak istediğinde tüm bu adımlar tekrarlanır.

Korkarım kod bir jsFiddle demosu yapmak için fazla "karmaşık".


Ko.applyBindings'in özellikle aynı içeren dom öğesinde birden çok kez çağrılması önerilmez. İstediğinizi elde etmenin başka bir yolu olabilir. Yine de daha fazla kod sağlamanız gerekecek. Lütfen mümkünse bir jsfiddle ekleyin.
madcapnmckay

initUygulanacak verileri ilettiğiniz bir işlevi neden ifşa etmiyorsunuz ?
KyorCode

Yanıtlar:


169

Bellekte bağlı nesneleri elden çıkarmak için DOM öğenizde nakavt'ın temiz düğüm yöntemini çağırmayı denediniz mi?

var element = $('#elementId')[0]; 
ko.cleanNode(element);

Daha sonra, yeni görünüm modellerinizle sadece bu öğeye nakavt bağlamalarını tekrar uygulamak, görünüm bağlamanızı güncelleyecektir.


33
Bu işe yarıyor - teşekkürler. Yine de bu yöntemle ilgili herhangi bir belge bulamıyorum.
awj

Ben de bu nakavt yardımcı program işleviyle ilgili dokümantasyonu araştırdım. Kaynak kodundan anlayabildiğim kadarıyla, deletedom elemanlarının kendilerindeki belirli anahtarları çağırıyor , görünüşe göre tüm nakavt büyüsünün saklandığı yer burası. Herhangi birinin dokümantasyon konusunda bir kaynağı varsa, çok minnettar olurum.
Patrick M

2
Bulamayacaksın. Ko yardımcı program işlevleriyle ilgili belgeleri yüksek ve düşük aradım, ancak hiçbiri yok. Bu blog yazısı, bulabileceğiniz en yakın şey, ancak yalnızca ko.utils üyelerini kapsıyor: knockmeout.net/2011/04/utility-functions-in-knockoutjs.html
Nick Daniels

1
Aşağıdaki cevabımda gösterildiği gibi etkinlikleri manuel olarak da kaldırmak isteyeceksiniz.
Michael Berkompas

1
@KodeKreachor Aşağıdaki çalışma örneğini zaten göndermiştim. Demek istediğim, veriyi ViewModel içinde serbest bırakırken, bağlamayı sağlam tutmanın daha iyi olabileceğiydi. Bu şekilde asla yeniden bağlamayı kaldırmak zorunda kalmazsınız. Doğrudan DOM'dan bağlantıyı manuel olarak çözmek için belgelenmemiş yöntemler kullanmaktan daha temiz görünüyor. Bunun ötesinde, CleanNode sorunludur çünkü olay işleyicilerin hiçbirini serbest bırakmaz (daha fazla ayrıntı için buradaki yanıta bakın: stackoverflow.com/questions/15063794/… )
Zac

31

Üzerinde çalıştığım bir proje için, ko.unapplyBindingsbir jQuery düğümünü ve boole kaldırmayı kabul eden basit bir işlev yazdım . ko.cleanNodeYöntem bununla ilgilenmediği için önce tüm jQuery olaylarının bağlantısını kaldırır . Bellek sızıntılarını test ettim ve gayet iyi çalışıyor gibi görünüyor.

ko.unapplyBindings = function ($node, remove) {
    // unbind events
    $node.find("*").each(function () {
        $(this).unbind();
    });

    // Remove KO subscriptions and references
    if (remove) {
        ko.removeNode($node[0]);
    } else {
        ko.cleanNode($node[0]);
    }
};

Sadece bir uyarı, daha önce ko.cleanNode()çağrılan bir şeye yeniden bağlanmayı test etmedim ve html'nin tamamı değiştirilmedi.
Michael Berkompas

4
Çözümünüz diğer tüm olay bağlamalarını da çözmez mi? yalnızca ko'nun olay işleyicilerini kaldırma olasılığı var mı?
lordvlad

ko çekirdeğini değiştirmeden
lordvlad

1
Doğru, ancak temel bir değişiklik olmadan söyleyebileceğim kadarıyla bu mümkün değil. Burada gündeme getirdiğim bu soruna bakın: github.com/SteveSanderson/knockout/issues/724
Michael Berkompas

KO fikri, doma çok nadiren dokunmanız gerekmiyor mu? Bu cevap dom boyunca ilerler ve benim kullanım durumumda kesinlikle kullanılamaz olmaktan uzaktır.
Blowsie

12

Knockout'un sunduğu ile bağlamayı kullanmayı deneyebilirsiniz: http://knockoutjs.com/documentation/with-binding.html Buradaki fikir, bağlamaları bir kez uygulamak ve verileriniz her değiştiğinde modelinizi güncellemektir.

Diyelim ki bir üst seviye görünüm modeli storeViewModel, cartViewModel tarafından temsil edilen sepetiniz ve bu sepetteki öğelerin listesi - mesela cartItemsViewModel.

En üst düzey modeli - storeViewModel'i tüm sayfaya bağlarsınız. Ardından, sayfanızın alışveriş sepeti veya alışveriş sepeti ürünlerinden sorumlu olan kısımlarını ayırabilirsiniz.

CartItemsViewModel'in aşağıdaki yapıya sahip olduğunu varsayalım:

var actualCartItemsModel = { CartItems: [
  { ItemName: "FirstItem", Price: 12 }, 
  { ItemName: "SecondItem", Price: 10 }
] }

CartItemsViewModel başlangıçta boş olabilir.

Adımlar şöyle görünecektir:

  1. Html'de bağlamaları tanımlayın. CartItemsViewModel bağlamayı ayırın.

      
        <div data-bind="with: cartItemsViewModel">
          <div data-bind="foreach: CartItems">
            <span data-bind="text: ItemName"></span>
            <span data-bind="text: Price"></span>
          </div>
        </div>
      
    
  2. Mağaza modeli sunucunuzdan gelir (veya başka bir şekilde oluşturulur).

    var storeViewModel = ko.mapping.fromJS(modelFromServer)

  3. Üst düzey görünüm modelinizde boş modeller tanımlayın. Daha sonra bu modelin bir yapısı gerçek verilerle güncellenebilir.

      
        storeViewModel.cartItemsViewModel = ko.observable();
        storeViewModel.cartViewModel = ko.observable();
     
    
  4. Üst düzey görünüm modelini bağlayın.

    ko.applyBindings(storeViewModel);

  5. CartItemsViewModel nesnesi kullanılabilir olduğunda, daha önce tanımlanmış yer tutucuya atayın.

    storeViewModel.cartItemsViewModel(actualCartItemsModel);

Sepet öğelerini temizlemek isterseniz: storeViewModel.cartItemsViewModel(null);

Knockout html ile ilgilenecektir - yani model boş olmadığında görünecek ve div'in içeriği ("bağlama içeren") yok olacaktır.


9

Arama düğmesine her tıkladığında ko.applyBinding'i çağırmam gerekiyor ve filtrelenen veriler sunucudan geri dönüyor ve bu durumda ko.cleanNode kullanmadan benim için çalışmayı takip etmeliyim.

Foreach'i şablonla değiştirirsek, collections / observableArray durumunda iyi çalışması gerektiğini deneyimledim.

Bu senaryoyu faydalı bulabilirsiniz.

<ul data-bind="template: { name: 'template', foreach: Events }"></ul>

<script id="template" type="text/html">
    <li><span data-bind="text: Name"></span></li>
</script>

1
Benzer bir sorunu çözmeye çalışmak için en az 4 saat harcadım ve sadece aamir'in çözümü benim için çalışıyor.
Antonin Jelinek

@AntoninJelinek senaryomda yaşadığım diğer şey, html'yi tamamen kaldırmak ve her şeyi kesinlikle kaldırmak için dinamik bir şekilde tekrar eklemek. örneğin $ .Ajax'da Knockout kod kapsayıcı div <div id = "knockoutContainerDiv"> </div> var. Sunucu yöntemi $ ("# knockoutContainerDiv") her çağırdığında takip ediyorum. children.remove (); / bunun / kaldırma içeriği ( "# knockoutContainerDiv") nakavt kod $ ile dinamik html eklemek için yöntemi çağırın append ( "nakavt bağlama koduyla childElements") ve yine applyBinding çağıran.
aamir sajjad

1
Hey ammir, seninle hemen hemen aynı senaryoya sahibim. Ko.cleanNode (element) kullanmak zorunda olmam dışında bahsettiğiniz her şey harika çalıştı; her yeniden bağlanmadan önce.
Radoslav Minchev

@RadoslavMinchev size daha fazla yardımcı olabileceğimi düşünüyor musunuz, evet ise nasıl, belirli bir soru hakkındaki düşüncelerimi / deneyimlerimi paylaşmaktan mutluluk duyarım.
aamir sajjad

Teşekkürler. @aamirsajjad, benim için işe yarayan şeyin, çalışmasını sağlamak için cleanNode () işlevini çağırmak olduğunu belirtmek istedim.
Radoslav Minchev

6

KO'nun dahili işlevlerini kullanmak ve JQuery'nin örtü olay işleyicisinin kaldırılmasıyla uğraşmak yerine, çok daha iyi bir fikir kullanmak withveya templatebağlamaları kullanmaktır . Bunu yaptığınızda ko, DOM'un bu bölümünü yeniden oluşturur ve böylece otomatik olarak temizlenir. Bu da önerilen yoldur, buraya bakın: https://stackoverflow.com/a/15069509/207661 .


4

Bağlamayı tüm zaman boyunca sürdürmenin ve onunla ilişkili verileri güncellemenin daha iyi olacağını düşünüyorum. Bu sorunla karşılaştım ve .resetAll()verilerimi sakladığım dizideki yöntemi kullanarak çağırmanın bunu yapmanın en etkili yolu olduğunu gördüm.

Temel olarak, ViewModel aracılığıyla işlenecek verileri içeren bazı genel değişkenlerle başlayabilirsiniz:

var myLiveData = ko.observableArray();

myLiveDataNormal bir dizi yapamayacağımı anlamam biraz zaman aldı - ko.oberservableArrayrol önemliydi.

O zaman devam edebilir ve ne istersen yapabilirsin myLiveData. Örneğin, bir $.getJSONarama yapın:

$.getJSON("http://foo.bar/data.json?callback=?", function(data) {
    myLiveData.removeAll();
    /* parse the JSON data however you want, get it into myLiveData, as below */
    myLiveData.push(data[0].foo);
    myLiveData.push(data[4].bar);
});

Bunu yaptıktan sonra, ViewModel'inizi her zamanki gibi kullanarak devam edebilir ve bağlamaları uygulayabilirsiniz:

function MyViewModel() {
    var self = this;
    self.myData = myLiveData;
};
ko.applyBindings(new MyViewModel());

Sonra HTML'de myDatanormalde yaptığınız gibi kullanın .

Bu şekilde, hangi işlevden olursa olsun myLiveData ile hata yapabilirsiniz. Örneğin, birkaç saniyede bir güncellemek istiyorsanız, bu $.getJSONsatırı bir işlevin içine sarın ve çağırın setInterval. myLiveData.removeAll();Satırı içeride tutmayı hatırladığınız sürece hiçbir zaman bağlamayı kaldırmanız gerekmeyecek .

Verileriniz gerçekten çok büyük olmadıkça, kullanıcılar diziyi sıfırlama ve ardından en güncel verileri tekrar ekleme arasındaki süreyi bile fark edemez.


Soruyu gönderdikten sonra, şimdi yaptığım şey bu. Sadece bu Nakavt yöntemlerinden bazıları ya belgelenmemiş ya da ne yaptığını bulmak için gerçekten etrafa bakmanız gerekiyor (işlev adını zaten biliyorsunuz).
awj

Bunu bulmak için de çok dikkatli bakmam gerekiyordu (belgelerde kazılan saat sayısı sonuçta ortaya çıkan kod satırı sayısını aştığında ... vay). Çalıştığına sevindim.
Zac


1

Bunu düşündün mü:

try {
    ko.applyBindings(PersonListViewModel);
}
catch (err) {
    console.log(err.message);
}

Bunu buldum çünkü Knockout'ta bu kodu buldum

    var alreadyBound = ko.utils.domData.get(node, boundElementDomDataKey);
    if (!sourceBindings) {
        if (alreadyBound) {
            throw Error("You cannot apply bindings multiple times to the same element.");
        }
        ko.utils.domData.set(node, boundElementDomDataKey, true);
    }

Yani bana göre bu zaten bağlı olan bir konu değil, hatanın yakalanmaması ve ele alınmaması ...


0

Görünüm modeli birçok div bağlama içeriyorsa, bunu temizlemenin en iyi yolunun ko.applyBindings(new someModelView);kullanmak olduğunu buldum : ko.cleanNode($("body")[0]);Bu ko.applyBindings(new someModelView2);, önceki görünüm modelinin hala bağlı olduğu endişesi olmadan dinamik olarak yeni bir çağrı yapmanızı sağlar .


4
Eklemek istediğim birkaç nokta var: (1) bu, uygulamanıza uygun olabilecek TÜM bağlamaları web sayfanızdan temizleyecektir, ancak sayfanın birden çok bölümüne ayrı ayrı bağlamaların eklendiği birçok uygulama olduğunu düşünüyorum. nedenler. TÜM bağlamaları tek bir süpürme komutuyla temizlemek birçok kullanıcı için yararlı olmayabilir. (2) geri çekilmesi için daha hızlı, daha verimli, doğal JavaScript yöntemi $("body")[0]olup document.body.
awj
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.