绑定停止更新

时间:2012-12-04 20:42:50

标签: jquery-mobile knockout.js flot

我一直在尝试创建一个自定义绑定来更新flot图表,它似乎在第一次加载时有效,但是当我浏览它时它会退出。

在这个场景中,我在一个页面上有一个列表视图(这是在jQuery Mobile中),带有很少的图形缩略图,图表旁边是一个滑块,它绑定到同一个视图的属性导致图形点重新计算的模型。当您单击其中一个列表项时,它会移动到显示更大版本图形的另一个页面,并允许您通过键入文本框来更改该值(稍后,您将能够直接单击该文本框图形)。绑定看起来像这样:

    ko.bindingHandlers["plot"] = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var qe = $(element);
            var page = qe.closest("div[data-role='page']");
            page.bind("pageshow", function () {
                ko.bindingHandlers["plot"].update(element, valueAccessor);
            });
        },
        update: function (element, valueAccessor, allBindingsAccessor) {
            var qe = $(element);
            var page = qe.closest("div[data-role='page']");
            var curr = $.mobile.activePage;
            var val = ko.utils.unwrapObservable(valueAccessor());
            var data = val.plotData();
            if(data && page.prop("id") == curr.prop("id")) {
                var marker = val.markerData();
                var opt = val.chartOptions();
                opt.yaxis.show = opt.xaxis.show = !qe.hasClass("graphThumb");
                marker.points.radius = opt.yaxis.show ? 5 : 3.5;
                $.plot(qe, [
                    data, 
                    marker
                ], opt);
            }
        }
    };

init处理程序将其设置为在页面显示上绘制图表,因为在绘制到不可见的div时flot无法正常工作。更新将检查当前显示的页面是否与绑定页面相同,并根据需要重绘图形。

对于列表视图中的图形,它们会立即通过update方法绘制并正常工作。但是,对于最初隐藏的页面,绘制图形的功能会触发,图形会绘制,但更新将不再起作用。然后,更糟糕的是,当您返回初始页面时,绑定到pageshow事件的函数将触发,重绘图形,但现在它们也会退出更新。

视图模型如下所示:

var viewModel = (function () {
    this.current = ko.observable(0);
    this.plotData = ko.computed(function () {
        var points = [];
        // a bunch of calculations that depend on the value of current of this and other viewModels in a collection
        return points;
    }
}

我可以在计算出的plotData中添加一个断点,看看它是否正确更新。只是这些更新不会触发绑定处理程序。

HTML绑定看起来像这样:

<!-- the first, visible page -->
<div data-role="page" id="index">
    <ul data-role="listview" data-bind="foreach: factors">
        <li data-bind="attr: {id: listId}">
            <a data-bind="attr: {href: idLink}">
                <div class="graphThumb" data-bind="plot: $data"></div>
            </a>
        </li>
     </ul>
</div>
<!-- hidden details pages -->
<!-- ko foreach: factors -->
<div data-role="page" data-bind="attr: { id: id }"> 
    <div class="graphPlaceHolder" data-bind="plot: $data"></div>
</div>
<!-- /ko -->

更新:我稍微改变了我的绑定,因为我意识到我可以在update事件处理程序上调用pageshow,这简化了事情,但没有。解决问题。似乎这样做不会让淘汰更新它对绑定的依赖。

更新:另一次更新,将val.plotData()分配给变量无法正常工作,也未将其包含在我的if语句中。但是,我有另一个计算的observable,它依赖于current值和父视图模型的另一个属性,我可以检索并添加到我的if语句中。但是,我的解决方案可能具体通用。简短的故事是,淘汰赛将重新评估每次更新的绑定依赖关系,因此您需要确保它正在评估重要的内容而不管任何条件逻辑,否则它将停止更新。

1 个答案:

答案 0 :(得分:0)

所以我可以结束并将这个问题标记为已回答,我将简要总结一下我的经历。

实现自定义绑定与Knockout中的计算属性相同(根据KO文档),计算属性的一件事是重新评估它们每次执行时依赖的属性。这意味着,如果你有一个条件计算属性(或自定义绑定),那么只有在实际执行的条件的分支中访问的属性才会被敲除监视。所以,例如,如果你有这样的属性:

var myComputedProperty = ko.computed(function() {
    if(this.myBool()) {
        $("#someElement").text(this.foo());
    }
    else {
        $("#someElement").text(this.bar());
    }
});

KO将跟踪myBool的价值并重新计算该属性(如果该属性发生变化),但如果myBool为真,它还会跟踪foo,如果myBool }是false,它也会跟踪bar,但它不会跟踪两者 - 因为它不需要。大部分时间这都很好。

在我的情况下,它失败了,因为我有一个不属于视图模型的条件(因此不可观察),我需要它来跟踪视图模型属性,无论条件是否被评估是真还是假。所以我有一些看起来像这样的东西:

if(page.prop("id") == curr.prop("id")) {
    $("#someElement").text(this.foo());
}

此处的比较是绑定所在页面的id与jQuery Mobile提供的$.mobile.activePage之间的对比(显然不可观察)。如果这些id匹配,那么当foo更改时,knockout将更新绑定。但是,如果他们不这样做,那么淘汰赛将失去对foo的依赖性,即使id稍后匹配,它也会失去依赖关系,并且在{{{{{}时不会重新评估1}}改变。

解决这个问题的方法是确保无论条件如何都要评估需要跟踪的任何属性。所以,这样的事情应该解决一般情况:

foo

至于为什么我需要这个条件是因为flot在尝试将图形绘制到不可见的div时会非常困惑,因此我需要在不是当前页面时跳过绘制图形。 / p>