0

我有一个带有两个视图的简单应用程序,一个“应用程序”模型和两个视图模型。我有兴趣研究如何取消绑定 jQuery 事件。

应用程序视图模型具有以下属性:

var self = this;    
self.viewModel = ko.observable(null);

最重要的 HTML 片段位于主模板中,即:

        <!-- ko if: viewModel -->
    <div data-bind="template:{name:viewModel().template, data:viewModel(), afterRender: viewModel().viewDidRender}"></div>
    <!-- /ko -->

在应用模型中,初始化后,视图模型被加载。每个视图模型都有一个简单的模板属性,它是一个引用要呈现的 html 模板的字符串。模板声明中的数据项将 knockoutjs 上下文设置为当前视图模型。afterRender 绑定确保调用 ViewModel 的 viewDidRender 方法(使用容器 html 元素作为参数)。

这意味着我可以执行以下操作:

self.viewDidRender = function (parentElement) {
    self.containerElement = parentElement;

    $("html").on("click", function (event) {
        var elements = $(event.target).parents();
        for (var i = 0; i < elements.length; i++)
            if (elements[i] == self.containerElement[0] || elements[i] == self.containerElement) {
                alert("the target exists within the parent element");
                return;
            }
        self.open(false);
    });
};

有一个要求是我们拦截html元素的点击事件。如果事件发生在目标位于子视图的父元素内的位置,则忽略该事件,否则将 'open' 设置为 false(这实际上是您在 iOS 中使用 PopOvers 看到的轻度关闭机制的一部分)

我发现当我在两个主视图之间切换时(每次渲染时都会调用此示例视图的 viewDidRender),上面的正文单击处理程序被绑定了多次。

我认为这是 JavaScript 内存泄漏。应该去哪里清理这些绑定?我可以调用 $("html").off(...) 等...但是在哪里?模板绑定的 beforeRemove 事件不会为纯模板项调用......仅适用于 foreach 绑定。

可以在所有子视图模型中创建另一个方法,例如“viewWillHide”,因此每次将新视图推送到应用模型的 ViewModel 属性时,我都可以尝试在视图模型上调用 viewWillHide 方法。这会奏效。但是,该应用程序比我在这里演示的要复杂。实际上存在视图模型和嵌套模板的层次结构。

手动操作是一种选择;模板绑定的 afterRender 没有对立面,这似乎很奇怪。

那你会怎么做?手动方法?即使它会导致整个视图模型层次结构发生级联变化,以便将 viewWillHide 方法从顶部(应用程序模型)向下传播到较小的视图模型。

谢谢

4

1 回答 1

0

尝试使用以下代码取消绑定您的 jQuery 事件

ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
    //Do cleanup
});

编辑:演示如何包装绑定的小小提琴

http://jsfiddle.net/d6cJG/

问题是绑定到模板绑定的 div 上的 addDisposeCallback 不会触发回调,因为该元素永远不会改变,它里面的元素会在模板改变时改变。

这里的回调将被称为http://jsfiddle.net/d6cJG/1/

于 2013-02-12T19:17:11.513 回答