Knockout deferUpdates与'如果'冲突?捆绑

时间:2017-04-11 09:09:59

标签: knockout.js

我经常在淘汰赛中使用if绑定来隐藏某些东西,并且我不需要担心if内的空引用错误。在此示例中,如果address()为null,则删除整个块,以避免必须处理每个属性的空检查。如果我使用visible绑定,情况就不是这样了。

<div data-bind="if: address()">
    You live at:
    <p data-bind="text: address().street.toUpperCase()"></p>            
</div>

这是上面最简单的情况 - 是的,我通常会将此模式与<!-- ko -->注释语法一起使用。

实际上导致我出现问题的是当我使用更复杂的computed值并启用ko.options.deferUpdates选项时:

<div data-bind="if: hasAddress()">
    You live at:
    <p data-bind="text: address().street.toUpperCase()"></p>            
</div>

这个computed可观察对象的最简单实现可能是这样的:

this.hasAddress = ko.computed(function () { return _this.address() != null; }); 

这一切都很有效,直到我执行以下操作:

  

1)在创建observable之前设置ko.options.deferUpdates = true

     

2)address()将从null开始,一切都很好

     

3)将address()设为{ street: '123 My Street' }。一切都运转正常。

     

4)将address()重置为null。我得到一个空错误,因为address().street是    null :-(

     

这是一个解释问题的小提琴:https://jsfiddle.net/g5gvfb7x/2/

不幸的是,由于微任务运行的顺序,它试图在text绑定之前重新计算if绑定,因此您仍然会得到一个通常不会出现的空错误。发生了。

我对此有点害怕,因为我经常使用这种模式: - (

2 个答案:

答案 0 :(得分:3)

当使用deferUpdates时,Knockout在内部使用dirty事件来通知所有计算的可观察对象的依赖关系发生变化并安排更新,这些更新以深度优先顺序发生。出现此问题的原因是绑定忽略dirty事件并等待change事件,这将以广度优先顺序发生。

必须在Knockout中进行修复才能使绑定响应dirty事件。这已经签入了即将发布的版本(3.5.0):https://github.com/knockout/knockout/issues/2226

答案 1 :(得分:0)

好消息 - 如果您已启用ko.options.deferUpdates并且甚至没有意识到您的应用已经损坏,则您的用户很可能无法看到错误,因为来自微观的错误任务在ko.onErrorwindow.onerror上引发,UI似乎恢复。但是,如果像我一样,你会记录这些错误,那就不好了。

好消息 - 如果您可以确定在if绑定中使用哪些可观察对象,则可以执行以下操作并关闭deferUpdates并重新打开:

// turn deferUpdates off and back on again
var deferUpdatesState = ko.options.deferUpdates;
ko.options.deferUpdates = false;
this.hasAddress = ko.computed(function () { return _this.address() != null; }); 
ko.options.deferUpdates = deferUpdatesState;

这可以成为辅助函数。如果计算的hasAddress具有更复杂的依赖性,则可能存在更复杂的问题。但对于像这样简单的情况,这很好。

坏消息 - 遗憾的是,您无法执行以下操作:

ko.computed(function () { return _this.address() != null; }).extend({ deferred: false });

这只是因为deferUpdates在内部工作的方式(从查看源代码)。

仍然想知道是否有比这个辅助函数更好的解决方案,或者重写if绑定的方法可能会在绑定中做一些聪明的事情来覆盖这种情况。

编辑:感谢@Tomalak提及with绑定。我不认为这是我正在寻找的完整解决方案,但它肯定可以与任何现有的if绑定(具有复杂规则)一起使用。如果尝试通过现有的应用程序,可能是最安全的解决方案。

<div>
    <div data-bind="if: hasAddress">
        You live at:
        <!-- ko with: address() -->
        <p data-bind="text: street.toUpperCase()"></p>          
        <!-- /ko -->
    </div>

    <div data-bind="if: !hasAddress()">
        Sorry! I don't know where you live!
    </div>

</div>