2012-04-19 58 views
18

Çok sayıda kullanıcı modeli içeren basit bir gözlemlenebilir dizi var. İşaretlemede, kullanıcıları döngüleyen ve bunları basit bir tabloda çıkaran bir foreach döngüsüne sahip bir şablon vardır. Ek olarak tabloyu özel bir kaydırma çubuğu ve başka bir javascript ile şekillendiriyorum. Yani şimdi foreach döngüsünün ne zaman biteceğini ve tüm modellerin DOM'a eklendiğini bilmeliyim.Knockout afterRender, ancak sadece bir kez

AfterRender geri çağırma ile ilgili sorun, her şey eklendiğinde çağrılmasıdır, ancak yalnızca bir kez tetiklenen bir geri bildirime ihtiyacım var.

+0

Neden onlar DOM'ye oluşturulur zaman bilmek gerekir? Stil uygularsanız, CSS yalnızca kural ile eşleşmeli ve öğeler eklendikçe uygulanmalıdır. – arb

cevap

21

Yapabileceğiniz en iyi şey, bir özel kullanmaktır Test edilmedi bağlayıcı. Özel bağlayıcınızı foreach sonra data-bind bağlama listesinde yerleştirebilir veya kodunuzu yürütülmeden önce içerik oluşturmak için foreach izin vermek için kodunuzu bir setTimeout içinde yürütebilirsiniz. İşte

kodu tek çalışma süresi ve kodu her zaman çalışan gösterir bir örnek olduğunu da observableArray güncellemeler: http://jsfiddle.net/rniemeyer/Ampng/

HTML:

<table data-bind="foreach: items, updateTableOnce: true"> 
    <tr> 
     <td data-bind="text: id"></td> 
     <td data-bind="text: name"></td> 
    </tr> 
</table> 

<hr/> 

<table data-bind="foreach: items, updateTableEachTimeItChanges: true"> 
    <tr> 
     <td data-bind="text: id"></td> 
     <td data-bind="text: name"></td> 
    </tr> 
</table> 

<button data-bind="click: addItem">Add Item</button> 

JS:

var getRandomColor = function() { 
    return 'rgb(' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ')'; 
}; 

ko.bindingHandlers.updateTableOnce = { 
    init: function(element) { 
     $(element).css("color", getRandomColor());    
    }  
}; 

//this binding currently takes advantage of the fact that all bindings in a data-bind will be triggered together, so it can use the "foreach" dependencies 
ko.bindingHandlers.updateTableEachTimeItChanges = { 
    update: function(element) {  
     $(element).css("color", getRandomColor()); 
    }  
}; 


var viewModel = { 
    items: ko.observableArray([ 
     { id: 1, name: "one" }, 
     { id: 1, name: "one" }, 
     { id: 1, name: "one" } 
    ]), 
    addItem: function() { 
     this.items.push({ id: 0, name: "new" }); 
    } 
}; 

ko.applyBindings(viewModel); 
+0

harika! Bu tam olarak aradığım şeydi;) –

+0

Bunu bir şablon içinde çalışmanın herhangi bir yolu var mı: {foreach: ...} 'call? –

+1

@JulesCopeland bu gibi bir şeye sahip olabilirsiniz: http://jsfiddle.net/rniemeyer/Ampng/37/ buradaki bağlama, diziye bağımlılık getirir. –

0

Sadece kapalı kafamın üst yapabildin ya:

  • yerine afterRender olayın içine çengel, basitçe/itme var dizide bir öğe attı sonra işlevini çağırır. Veya gözlemlenebilirArray'ı, kendi alt öğelerinin altından kendi afterRender olayına sahip olan gözlenebilir bir kutuya sarın. foreach döngüsü şöyle ebeveyn gözlemlenebilir başvurmak gerekir:

Örnek:

<div> 
    <div data-bind="with: parentItem(), template: { afterRender: myRenderFunc }" > 
     <div data-bind="foreach: observableArrayItems"> 
      ... 
     </div> 
    </div> 
    </div> 

bu yüzden sadece tahmin ...

+1

Hata alma: İleti: Birden çok bağlama (birlikte ve şablonla) aynı öğenin alt dizelerini denetlemeye çalışıyor. Bu bağları aynı öğe üzerinde birlikte kullanamazsınız. ' – Airn5475

7

Hızlı ve basit bir şekilde, AfterRender işleyicinizde, geçerli öğeyi listenizdeki son öğeyle karşılaştırın. Eşleşirse, bu, Sonuncu çalıştırıldıktan sonraki son zamandır.

+0

Bu akıllıdır. Bunu düşünmedim! – kamranicus

+0

Bu benim için mükemmel çalıştı, öneri için teşekkürler! Bu, her mesaj için bir kez (muhtemelen bin kere) bir kez çağrılan kaydırma işlevimi kısar. Basit, aptalca tutun! –

0

Foreach şablonunun görüntülemeyi tamamladığını öğrenmek için "kukla" ciltleme yapabilir ve geçerli işlenen öğe dizinini işleyicinize iletebilir ve dizi uzunluğuna uyup uymadığını kontrol edebilirsiniz.

HTML:

<ul data-bind="foreach: list"> 
    <li> 
    \\ <!-- Your foreach template here --> 
    <div data-bind="if: $root.listLoaded($index())"></div> 
    </li> 
</ul> 

ViewModel - Handler: Listeye daha fazla öğe eklemek istiyorsanız

this.listLoaded = function(index){ 
    if(index === list().length - 1) 
     console.log("this is the last item"); 
} 

ekstra bayrak tutun.

8

Zarif bir hile ile geldim.Kabul cevap veri bağlamaları artık sırayla çalıştırılır olarak bunları bildirmek (nakavt-3.x çalışacak olmadığını

<!--ko foreach: { data: ['1'], afterRender: YourAfterRenderFunction } --> 
<!--/ko--> 
+0

Mükemmel çalıştı ve bana bir sürü güçlük kazandı, teşekkürler! – Smegger

+2

+1 Bu gerçekten temiz! ama muhtemelen en iyi şekilde kullanmak için ... – easuter

0

emin değilim: derhal şablon/foreach bloğundan sonra, bu kodu ekleyin).

İşte başka bir seçenek, sadece bir kez ateş edecek.

ko.bindingHandlers.mybinding { 
     init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { 
      var foreach = allBindings().foreach, 
       subscription = foreach.subscribe(function (newValue) { 
       // do something here 
       subscription.dispose(); // dispose it if you want this to happen exactly once and never again. 
      }); 
     } 
} 
0

Nasıl bir debouncer kullanılıyor?

var afterRender = _.debounce(function(){  

    // code that should only fire 50ms after all the calls to it have stopped 

}, 50, { leading: false, trailing: true}) 
2

Jaffa'nın yanıtı bir hata içeriyor, bu yüzden yorum yapmak yerine yeni bir yanıt oluşturmaya karar verdim. Aynı anda hem şablon hem de şablon kullanmak mümkün değildir. Dolayısıyla, sadece şablonun data etiketi

html modelinizi taşımak

<div data-bind="template: {data: myModel, afterRender: onAfterRenderFunc }" > 
    <div data-bind="foreach: observableArrayItems"> 
     ... 
    </div> 
</div> 

JavaScript

var myModel = {  
    observableArrayItems : ko.observableArray(), 

    onAfterRenderFunc: function(){ 
    console.log('onAfterRenderFunc') 
    } 

} 
ko.applyBinding(myModel);