【问题标题】:Custom knockout binding fires twice, unexpectedly自定义淘汰赛绑定意外触发两次
【发布时间】:2014-02-11 13:09:49
【问题描述】:

我编写了一个自定义绑定处理程序来将我的视图模型数据绑定到一个 highcharts 图表。这实际上有两部分,一个绑定 highcharts 所需的初始配置,第二个将系列绑定到图表。

这里是bindingHandler 代码

ko.bindingHandlers.highchart = {
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var value = valueAccessor();
        var valueUnwrapped = ko.unwrap(value);
        console.log ('update',element.id,valueUnwrapped);        
        if(allBindings.get('series')){
            var series = allBindings.get('series');
            var seriesUnwrapped = ko.unwrap(series);
            if(!$.isArray(seriesUnwrapped)){
                seriesUnwrapped = [seriesUnwrapped];
            }
            console.log('seriesUnwrapped',element.id,seriesUnwrapped)
            valueUnwrapped.series = seriesUnwrapped;
        }
        $(element).highcharts(valueUnwrapped);        
    }
}

现在我为此设置了 2 个测试,第一个按预期工作。它绑定了一个具有多个系列的图表,当我添加到绑定到series 的可观察数组时,它只更新一次图表。查看this fiddle 并在单击“添加”按钮时观察控制台。你会得到的输出是

更新容器对象 { chart={...}}
seriesUnwrapped 容器 [Object { name="Scenario0", color="red", data=[9]}, Object { name="Scenario1", color="green", data=[9]}]

表示我们只经历过上述代码一次。

现在检查我的第二个小提琴:http://jsfiddle.net/8j6e5/9/。这略有不同,因为 highcharts 初始配置是 computed 可观察的,系列也是如此。当您单击此按钮上的“添加”按钮时,您会看到绑定执行了两次:

更新 container2 对象 { chart={...}, xAxis={...}, series=[1]}
seriesUnwrapped container2 [Object { name="Scenario2", color="blue", data=[2]}]
更新 container2 对象 { chart={...}, xAxis={...}}
seriesUnwrapped container2 [Object { name="Scenario2", color="blue", data=[2]}]

我猜想在我的 highcharts 绑定处理程序中使用allBindings.get('series') 我设置了对它的依赖,并且当两个绑定都更改时,它执行 highcharts 绑定两次。我的问题是,有没有办法阻止这种情况,或者以任何其他方式编写此功能以防止这种情况发生?

【问题讨论】:

  • 您必须决定何时调用更新函数。因此,当您的绑定属性更改或series 绑定属性更改时。然后使用peek() 而不是ko.unwrapvar seriesUnwrapped = series.peek();var valueUnwrapped = value.peek(); 您当然需要检查以测试valueseries 在调用peek 之前确实是可观察的。
  • @nemesv - 这是一个非常好的开始,仅此一项就解决了双重通话问题。然而,在进行了简单的更改之后,我的第一个小提琴将不再更新(因为它绑定到一个计算系列,但一个静态的 highchart 属性)。你距离一个非常好的答案只有 2/3。
  • 如果你使用值var valueUnwrapped = ko.isObservable(value) ? value.peek() : value; 来解决这两个问题。但是,如果您的绑定是 highchart: yourProp, series: otherProp 并且只有 yourProp 发生变化,而 otherProp 没有变化,那么它就不会正确更新您的图表...

标签: javascript knockout.js bindinghandlers


【解决方案1】:

我不确定这会对您有所帮助,因为上面 cmets 中的 nemesv 答案似乎非常接近您想要实现的目标,即。停止双重更新。

不过,我已经花了一些时间,所以无论如何我都会向您展示我的想法,希望对您有所帮助。

Fiddle here

我不知道 nemesv 提到的 peek() 方法(很好的提示),所以我研究了为什么它会根据计算值等更新两次。

我看到您的 self.breakdownChart 正在访问 currentScenario observable,当我将它作为测试删除时,第二次更新没有发生。

这让我开始思考,为什么你需要在那里设置 x 轴。

所以我向您的场景添加了一个新属性以返回当前场景名称

 self.name='Scenario' + self.number;

然后对于基本场景,将其更改为“基本场景”以确保标题正确显示为该系列。

为确保图例/轴正确,我向图表对象添加了一个名为 baseSeriesName 的新属性

self.breakdownChart = ko.computed(function(){
    return {    
        baseSeriesTitle: baseScenario.name,

并且设置为 baseScenario 的名称。

最后,为了在 BindingHandler 中将所有内容绑定在一起,我在其中更新了 xAxis:

       //set the xAxis titles, only add the second title if different from the base
        valueUnwrapped.xAxis={
            categories: [valueUnwrapped.baseSeriesTitle, valueUnwrapped.baseSeriesTitle!=seriesUnwrapped[0].name ? seriesUnwrapped[0].name:'']
        }   

这有点重构,但它达到了你的目标;希望有帮助。

哦,我还在视图模型中添加了一个chartType observable,并在图表定义中使用它(breakdownChart 计算),以测试如果图表在不同的 observable 上刷新并且它仍然初始化,则不会发生双重更新正确 - 所以小提琴显示 chartType 更新,没有双重更新。

【讨论】:

    【解决方案2】:

    你会得到两个更新,因为 Knockout 会在它们的依赖关系发生变化时立即更新计算的 observables,并且你的绑定有两个依赖关系,每个依赖关系都会依次更新。

    解决此问题的一种方法是使用一种技术来延迟绑定更新。一个简单的方法是使用Deferred Updates plugin,如下所示:http://jsfiddle.net/mbest/8j6e5/15/

    延迟更新使用setTimeout 执行更新,这意味着更新是异步发生的。如果您希望它对于特定更新是同步的,您可以使用ko.tasks.processImmediate

    ko.tasks.processImmediate(function() {
        self.scenarios.push(newScenario);
        self.currentScenario(newScenario);
    });            
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      • 1970-01-01
      • 2013-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多