19

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

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

4

8 回答 8

23

您最好的选择是使用自定义绑定。您可以将自定义绑定foreach放在绑定列表之后,data-bind也可以在 a 中执行代码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);
于 2012-04-19T15:41:26.653 回答
9

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

<!--ko foreach: { data: ['1'], afterRender: YourAfterRenderFunction } -->
<!--/ko-->
于 2013-11-12T22:20:04.677 回答
8

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

于 2012-04-19T16:00:13.883 回答
3

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);
于 2015-04-01T10:22:15.180 回答
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.
            });
        }
}
于 2014-03-13T15:53:25.137 回答
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");
}

如果您打算将更多项目添加到列表中,请持有额外的标志。

于 2013-02-27T07:56:17.073 回答
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 回答