自定义击倒绑定意外地发射两次

时间:2014-02-11 13:09:49

标签: javascript knockout.js bindinghandlers

我编写了一个自定义绑定处理程序来将我的viewmodel数据绑定到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 container [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]}]
  update container2 Object {chart = {...},xAxis = {...}}
  seriesUnwrapped container2 [Object {name =“Scenario2”,color =“blue”,data = [2]}]

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

2 个答案:

答案 0 :(得分:3)

我不确定这会对你有什么帮助,因为上面评论中的nemesv答案似乎非常接近你想要达到的目标,即。停止双重更新。

然而,我花了一点时间,所以我会告诉你我想出了什么,并希望它有所帮助。

Fiddle here

我不知道nemesv提到的peek()方法(很棒的提示),所以我根据计算结果等调查了两次更新的原因。

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

所以这让我思考,为什么你需要在那里进行x轴设置。

所以我在你的场景中添加了一个新属性来返回当前的场景名称

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

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

为确保图例/轴正确,我在图表对象中添加了一个名为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:'']
        }   

这是一些重构,但它实现了你的目标;希望它有所帮助。

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

答案 1 :(得分:3)

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

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

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

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