淘汰赛后,但只有一次

时间:2012-04-19 15:20:57

标签: knockout.js

我有一个简单的observableArray,它包含很多用户模型。 在标记中,有一个带有foreach循环的模板,循环用户并将它们输出到一个简单的表中。我还使用自定义滚动条和其他一些javascript来设置表格样式。所以现在我必须知道foreach循环何时完成并且所有模型都被添加到DOM中。

afterRender回调的问题在于每次添加内容时都会调用它,但我需要一种只触发一次的回调。

8 个答案:

答案 0 :(得分:23)

您最好的选择是使用自定义绑定。您可以将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);

答案 1 :(得分:9)

我想出了一个优雅的作弊。在模板/ foreach块之后,立即添加以下代码:

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

答案 2 :(得分:8)

一种快速而简单的方法是,在您的afterRender处理程序中,将当前项目与列表中的最后一项进行比较。如果匹配,则这是afterRender最后一次运行。

答案 3 :(得分:3)

Jaffa的回答中包含错误,所以我决定创建一个新答案而不是评论。无法同时使用with和模板。 因此,只需将模型移至模板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);

答案 4 :(得分:1)

我不确定接受的答案是否适用于knockout-3.x(因为数据绑定不再按照您声明的顺序运行)。

这是另一种选择,它只会发射一次。

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.
            });
        }
}

答案 5 :(得分:0)

在我的头顶,您可以:

  • 只需在推送/弹出阵列上的项目后调用您的函数,而不是挂钩到afterRender事件。
  • 或者可能将observableArray包装在一个observable中,该observable本身具有自己的afterRender事件下面的子项。 foreach循环需要像这样引用父observable:

示例:

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

没有测试过,所以只是猜测...

答案 6 :(得分: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");
}

如果您打算在列表中添加更多项目,请保留额外标记。

答案 7 :(得分:0)

如何使用去抖动器?

var afterRender = _.debounce(function(){    

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

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