0

我编写了一个自定义绑定处理程序来将我的视图模型数据绑定到一个 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它只更新一次图表。当您单击“添加”按钮时,请查看此小提琴并观看控制台。你会得到的输出是

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

表明我们只经历过一次上面的代码。

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

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

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

4

2 回答 2

3

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

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

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

ko.tasks.processImmediate(function() {
    self.scenarios.push(newScenario);
    self.currentScenario(newScenario);
});            
于 2014-02-11T18:34:55.153 回答
3

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

但是,我花了一些时间在上面,所以无论如何我都会向您展示我的想法,并希望它有所帮助。

在这里提琴

我不知道 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 可观察对象,并在图表定义中使用它(breakdownChart 计算),以测试如果图表在不同的可观察对象上刷新并且它仍然正确初始化,则不会发生双重更新 - 所以小提琴显示了 chartType 更新,没有双重更新。

于 2014-02-11T16:36:44.733 回答