【发布时间】:2013-01-27 22:40:12
【问题描述】:
我有一个简单的应用程序,它有两个视图,一个“应用程序”模型和两个视图模型。我有兴趣研究如何取消绑定 jQuery 事件。
应用视图模型有这个属性:
var self = this;
self.viewModel = ko.observable(null);
最重要的 HTML sn-ps 位于主模板中,即:
<!-- 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 每次渲染都会被调用),上面的 body click 处理程序被绑定了多次。
我认为这是 JavaScript 内存泄漏。应该去哪里清理这些绑定?我可以调用 $("html").off(...) 等...但是在哪里?模板绑定的 beforeRemove 事件不会为纯模板项调用......仅适用于 foreach 绑定。
我可以在所有子视图模型中创建另一个方法,例如“viewWillHide”,因此每次将新视图推入应用模型的 ViewModel 属性时,我都可以尝试调用视图模型上的 viewWillHide 方法。这会奏效。但是,该应用程序比我在这里演示的要复杂。实际上存在视图模型和嵌套模板的层次结构。
手动操作是一种选择;模板绑定的 afterRender 没有对立面,这似乎很奇怪。
那你会怎么做?手动方法?即使它会导致整个视图模型层次结构发生级联变化,以便将 viewWillHide 方法从顶层(应用程序模型)向下传播到较小的视图模型。
谢谢
【问题讨论】:
-
您没有在模板的 html 中使用敲除点击绑定是否有原因?然后在您的视图模型中进行点击处理?不过听起来是个棘手的问题。
-
是的,因为视图模型是瞬态的。是否拦截点击只是视图和视图模型的关心。如果您无论如何都拦截点击,您不仅必须自己从应用程序模型级别向下传播它,而且还会出现其他问题,因此您将无休止地将专门模型的关注点插入应用程序更通用的架构中。我现在刚刚使用处理模式实现了它,如果没有人有更好的见解,我会在这里发布。