【问题标题】:Knockout afterRender, but just once渲染后淘汰赛,但只有一次
【发布时间】:2012-05-01 03:51:18
【问题描述】:

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

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

【问题讨论】:

  • 为什么需要知道它们何时被渲染到 DOM 中?如果您正在应用样式,CSS 应该只匹配规则并在添加项目时应用。

标签: knockout.js


【解决方案1】:

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

【讨论】:

  • 太棒了!这正是我想要的;)
  • 有什么方法可以在template: {foreach: ...} 调用中实现这个功能吗?
  • @JulesCopeland 你可以使用这样的东西:jsfiddle.net/rniemeyer/Ampng/37 绑定获取对数组的依赖。
  • 这不适用于@j-a 提到的淘汰赛 3.4.2。
【解决方案2】:

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

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

【讨论】:

  • 完美运行,为我省去了很多麻烦,谢谢!
【解决方案3】:

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

【讨论】:

  • 这很聪明。没想到!
  • 这对我来说非常有效,感谢您的建议!这减少了我的滚动功能被每条消息调用一次(可能是一千次)到一次。保持简单愚蠢,完美!
【解决方案4】:

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

【讨论】:

    【解决方案5】:

    我不确定接受的答案是否适用于 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.
                });
            }
    }
    

    【讨论】:

    • 只是要指出,如果foreach是使用“as”语法foreach: { data: array, as: 'item' }声明的,那么订阅函数就在数据属性的原型上。所以,应该使用allBindings.get("foreach").data.subscribe
    【解决方案6】:

    我想你也可以:

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

    例子:

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

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

    【讨论】:

    • 收到错误:消息:Multiple bindings (with and template) are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.
    【解决方案7】:

    为了找出 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】:

      使用去抖动器怎么样?

      var afterRender = _.debounce(function(){    
      
          // code that should only fire 50ms after all the calls to it have stopped
      
      }, 50, { leading: false, trailing: true})
      

      【讨论】:

        猜你喜欢
        • 2014-08-14
        • 2016-10-08
        • 1970-01-01
        • 1970-01-01
        • 2019-10-31
        • 2014-05-25
        • 2017-04-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多