2012-04-19 15 views
18

私は、多くのユーザーモデルを含むシンプルなobservableArrayを持っています。 マークアップには、ユーザーをループして単純なテーブルに出力するforeachループを持つテンプレートがあります。私はさらにカスタムスクロールバーと他のいくつかのjavascriptでテーブルのスタイルを設定します。だから今foreachループが終了し、すべてのモデルがDOMに追加されることを知る必要があります。ノックアウトafterRenderですが、一度だけ

afterRenderコールバックの問題は、何かが追加されるたびに呼び出されることですが、一度だけ起動するコールバックが必要です。ちょうどあなたがいずれかの可能性私の頭の上から

+0

なぜあなたは彼らがDOMにレンダリングされたときに知っておく必要がありますか?スタイルを適用している場合、CSSはルールと一致し、アイテムが追加されると適用されます。 – arb

答えて

21

を推測テストしていない

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

結合。 foreachの後にカスタムバインディングを配置してdata-bindのバインドリストに入れたり、setTimeoutでコードを実行して、コードを実行する前にforeachがコンテンツを生成できるようにすることができます。ここで

は、コードを単一の時間を実行すると、コードにそのあなたのobservableArrayアップデートするたびに実行している示すサンプルです: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

素晴らしい!これはまさに私が探していたものでした;) –

+0

'template:{foreach:...}'コールでこれが動作する方法はありますか? –

+1

@JulesCopelandあなたは次のようなものに行くことができます:http://jsfiddle.net/rniemeyer/Ampng/37/ここで、バインディングは配列への依存を取得します。 –

0

:代わりにafterRenderイベントにフックの

  • 、あなたはプッシュを持った後、単にあなたの関数を呼び出す/アレイ上のアイテムをポップ。
  • observableArrayをobservableにラップすることもできます。そのobservableArray自体には、独自のafterRenderイベントを持つ子アイテムがあります。 foreachループはそのような観察可能な親を参照する必要があります:

例:これはので、ちょうどあなたの最善の策は、カスタムを使用することです...

+1

エラーを取得しています:メッセージ: '複数のバインディング(およびテンプレート)が同じ要素の子孫バインディングを制御しようとしています。これらのバインディングを同じ要素で一緒に使用することはできません。 ' – Airn5475

7

速いですシンプルな方法は、あなたのafterRenderハンドラで、現在のアイテムとあなたのリストの最後のアイテムを比較することです。一致した場合、これはafterRenderが実行された最後の時刻です。

+0

これは賢いです。私はこれを考えなかった! – kamranicus

+0

これは私にとって完璧に機能しました。これは、各メッセージ(おそらく1000回)ごとに一度呼び出されるスクロール機能を削減します。それは単純な、完璧な愚かなままに! –

0

foreachテンプレートがいつレンダリングを完了したかを知るために、「ダミー」バインディングを作成し、現在のレンダリングされたアイテムインデックスをハンドラに渡し、配列長と一致するかどうかを確認できます。

HTML:

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

のViewModel - ハンドラ:

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

あなたがリストに複数の項目を追加する場合は、余分なフラグを持ちます。

8

私はエレガントなチートを思いついた。すぐにあなたのテンプレート/ foreachのブロックの後に、このコードを追加します。

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

完璧に働いて、私に多くの手間を省いた、ありがとう! – Smegger

+2

+1これは本当にすっきりしています!しかしおそらくそれを控えめに使用するのが最良です... – easuter

0

私は、データ・バインディングはもはやあなたがそれらを宣言するために実行されているものとして受け入れ答えはノックアウト-3.xの(に動作しません場合はわかりません)。

別のオプションがありますが、これはちょうど1回だけ点火されます。

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

デバウンサーの使用はいかがですか?

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の回答にはエラーが含まれているため、コメントする代わりに新しい回答を作成することにしました。 withとtemplateを同時に使用することはできません。 だから、テンプレートのdataタグ

HTMLにあなたのモデルを動かす

<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); 
関連する問題