19

多くのユーザーモデルを含む単純なobservableArrayがあります。マークアップには、ユーザーをループして単純なテーブルに出力する foreach ループを含むテンプレートがあります。さらに、カスタム スクロールバーとその他の JavaScript を使用してテーブルのスタイルを設定します。したがって、foreach ループがいつ終了し、すべてのモデルが DOM に追加されるかを知る必要があります。

afterRender コールバックの問題は、何かが追加されるたびに呼び出されることですが、一度だけ起動するようなコールバックが必要です。

4

8 に答える 8

23

最善の策は、カスタム バインディングを使用することです。foreachのバインディングのリストの後にカスタム バインディングを配置するか、 でコードを実行して、コードが実行される前にコンテンツを生成できるようにdata-bindすることができます。setTimeoutforeach

コードを 1 回実行し、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);
于 2012-04-19T15:41:26.653 に答える
8

すばやく簡単な方法は、afterRender ハンドラーで、現在の項目をリストの最後の項目と比較することです。一致する場合、これが afterRender が最後に実行されたことになります。

于 2012-04-19T16:00:13.883 に答える
1

受け入れられた回答がノックアウト 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.
            });
        }
}
于 2014-03-13T15:53:25.137 に答える
0

デバウンサーを使ってみてはどうですか?

var afterRender = _.debounce(function(){    

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

}, 50, { leading: false, trailing: true})
于 2014-05-26T14:40:10.433 に答える
0

頭のてっぺんから、次のいずれかを実行できます。

  • afterRender イベントにフックする代わりに、配列の項目をプッシュ/ポップした後に関数を呼び出すだけです。
  • または、observableArray を observable にラップすることもできます。この observable 自体には、独自の afterRender イベントを持つ子項目があります。foreach ループは、次のように親オブザーバブルを参照する必要があります。

例:

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

これはテストしていないので、推測するだけです...

于 2012-04-19T15:29:35.610 に答える
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");
}

リストにさらに項目を追加する場合は、extra フラグを保持します。

于 2013-02-27T07:56:17.073 に答える