knockout.js:更新绑定?

Posted

技术标签:

【中文标题】knockout.js:更新绑定?【英文标题】:knockout.js: update bindings? 【发布时间】:2012-01-07 02:06:10 【问题描述】:

当我在 ko.applyBindings(); 之后将任何新元素注入 DOM 时;被调用,则淘汰赛将无法识别这些新元素。 我可以理解为什么会发生这种情况 - 它们只是没有被淘汰赛索引。

所以,起初我认为这可以通过再次调用 ko.applyBindings() 来解决,在添加我的新元素之后,但后来我意识到,对于你进行的每个 ko.applyBindings() 调用,相应的事件都会被触发多次。所以在应用五次之后,一个 click: 绑定会被触发五次,所以这不是一个理想的解决方案;)

有没有类似 ko.updateBindings() 之类的东西来告诉淘汰赛,嗯...更新元素绑定?

问候, 克里斯

【问题讨论】:

你能发布一些代码来展示你在做什么吗? 好吧,例如这样的: $('body').append('点我! '); 我不确定这是否足以提供有用的答案。我了解您要做什么,但是通过更全面地了解您的代码(为什么/何时注入新的 DOM 元素),很难用最好的方法来解决这个问题。在查看您当前的解决方案之后,可能有人可以指出一种不必注入新元素的方法,或者为您尝试做的事情提供解决方法。 好像找到了。只需调用: ko.applyBindings(viewModel);再次将所有函数再次绑定到整个 DOM。这就是原因,在重新调用此函数后,每个回调都会被调用两次甚至更多。 applyBindings() 的第二个参数是开始应用的 DOM 节点。这默认为 DOM 根。因此,如果您要“手动”将几个具有数据绑定属性的元素添加到某个 DOM 节点,请使用新元素获取对 DOM 节点的引用并将其作为第二个参数传递! 太棒了!我忘记了 applyBindings() 的第二个参数。 【参考方案1】:

我刚刚偶然发现了一个类似的问题。我尝试向容器中添加新元素并为它们提供 onclick 功能。

一开始尝试了你所做的事情,甚至尝试了ColinErecommended的方法。这对我来说不是一个实用的解决方案,所以我尝试了 SamStephens 方法并想出了这个方法,这对我来说非常有效:

html

<div id="workspace" data-bind="foreach:nodeArr, click:addNode">
<div class="node" data-bind="attr:id:nodeID,style:left:nodeX,top:nodeY,text:nodeID, click:$parent.changeColor"></div>
</div>

javascript

<script>
function ViewModel() 
var self = this;
var id = 0;
self.nodeArr = ko.observableArray();
self.addNode = function (data, event) 
    self.nodeArr.push(
        'nodeID': 'node' + id,
        'nodeX' : (event.offsetX - 25) + 'px',
        'nodeY' : (event.offsetY - 10) + 'px'
    )
    id++;

self.changeColor = function(data, event)
    event.stopPropagation();
    event.target.style.color = 'green';
    event.target.style.backgroundColor = 'white';


ko.applyBindings(new ViewModel());
</script>

你可以在我做的JS Fiddle里玩。

【讨论】:

嗯,对我来说......我很久以前就放弃了 KnockoutJS,因为它对我的目的来说太不灵活了。 @ChristianEngel KO 几乎是不灵活的.. 某些事情需要以 KO 方式完成(我还没有找到需要重新绑定!)才能令人愉快,但 Durandal(使用 KO) -> 支持视图的出色灵活性 - 单独的视图有效地减少了手动尝试部分绑定的需要,因为每个视图都可以引入它自己的模型或共享模型的 [部分]。现在,KO有一些我不喜欢的地方,但这与缺乏灵活性无关!一旦被充分利用,可观察模型非常灵活。 @ChristianEngel 处理注入 DOM 的随机(外部)元素的“hackish 但简单”方法是将它们插入到 flow control 绑定中,例如 if (这篇文章显示了foreach,这可以说是更干净的开始),然后在插入元素后将内容切换为“on” - 新的子树将根据需要进行绑定。 no 额外的手册 applyBindings 和一个 observable 控制流绑定。 withProperties 等附加绑定可用于优化范围内的属性。【参考方案2】:

在不知道自己到底在做什么的情况下,您似乎走错了路。您的视图应该由您的视图模型驱动。因此,您不应该直接添加 DOM 元素,然后需要对其应用敲除绑定。

相反,您应该更新您的视图模型以反映视图中的更改,这会导致您的新元素出现。

例如,对于您的$('body').append('&lt;a href="#" data-bind="click: something"&gt;Click me!&lt;/a&gt;');,与其在按钮应该可见时添加 DOM 元素,不如使用视图模型控制按钮可见性。

所以你的视图模型包括

var viewModel =  clickMeAvailable: ko.observable(false) 

您的 HTML 包括

<a href="#" data-bind="click: something, visible: clickMeAvailable">Click me!</a>

当应用程序状态发生变化,点击我按钮可用时,您只需viewModel.clickMeAvailable(true)

这样做的重点,也是淘汰赛的很大一部分,是将业务逻辑与表示分离。因此,使点击我可用的代码并不关心点击我涉及一个按钮。它所做的只是在点击我可用时更新viewModel.clickMeAvailable

例如,假设单击我是一个保存按钮,当表单被有效填写时应该可用。您可以将保存按钮的可见性与 formValid 可观察的视图模型联系起来。

但随后您决定进行更改,因此在表格有效后,会出现一份法律协议,必须在保存之前获得同意。表单的逻辑不会改变 - 当表单有效时,它仍然设置 formValid。您只需更改formValid 更改时发生的情况。

正如 lassombra 在 cmets 关于此答案的指出的那样,在某些情况下,直接 DOM 操作可能是您的最佳方法 - 例如,在复杂的动态页面中,您只想在需要时对视图的某些部分进行水合。但是你放弃了 Knockout 通过这样做提供的一些关注点分离。如果您正在考虑进行这种权衡,请注意。

【讨论】:

虽然我当然同意所有这些,但我认为有时以一种最终得到“嵌套”视图模型的方式利用 knockoutjs 确实是有意义的。这可能更符合克里斯蒂安正在处理的情况。在这种情况下,当它可用/已完成加载时,您应该有一个专门为嵌套 HTML 设置并绑定到嵌套 HTML(可能是通过 ajax 拉入)的视图模型。 为了记录,我认为这是正确的答案。控件应在声明时绑定,而不是由事件行为驱动。 Knockout 是您需要保持整个方法一致以避免将烦人的错误降至最低的技术之一。 视图本身发生变化的特别大的网站怎么办?是否应该将所有可能的视图打包到默认页面中?肯定不是。当您转换到网站的其他“区域”时,通过 ajax 加载视图当然是一个可以接受的操作(我个人在我正在转换的 Web 应用程序中使用它。原始应用程序是 MVC4,对于不同的个人管理数据有不同的视图我正在跟踪(股票、其他财务、待办事项列表、联系人列表、事件日历)。由于它们都使用相同的母版页,因此将它们转换为 MVVM/knockout 非常简单。 这是一个公平的选择,没有“唯一的方法”。但是,如果您要开始直接 DOM 操作,这应该是一个深思熟虑的决定,因为您知道您正在权衡 Knockout 提供的关注点分离。正如我在回答中所说,“不知道你到底在做什么”。不过,我已经编辑以反映您的评论。【参考方案3】:

每次调用 ko.applyBindings 时,都会检查整个 DOM 是否存在绑定。因此,如果您多次执行此操作,您将获得每个元素的多个绑定。如果您只想绑定一个新的 DOM 元素,可以将此元素作为参数传递给 applyBindings 函数:

ko.applyBindings(viewModelA, document.getElementById("newElement"));

查看这个相关问题:

Can you call ko.applyBindings to bind a partial view?

【讨论】:

是的,这也是我想出的解决方案 :) 遗憾的是,我还没有在 knockoutjs.com 上找到直接的 API 文档,不得不对 ko 对象进行一些逆向工程才能找到它 -。 - 没错,没有 API 文档,但它在更“冗长”的文档中。往下看这个页面:knockoutjs.com/documentation/observables.html 你确定这是答案吗?这不会将 viewModelA 应用于 newElement,就好像 newElement 是 viewModelA 的 DOM 的根一样?我可以看到它在某些时候甚至大多数时候会起作用,但我也可以看到它可能是如何产生意想不到的结果(我认为)。 我只是想在这里发表评论,因为我实际上遇到了一个陷阱。我正在通过 ajax 加载子视图并将它们粘贴到绑定到视图的视图模型中,以将子(部分)视图放置在母版页上的正确位置。应该非常小心它们绑定到哪些元素。我不小心将 applyBindings 调用到持有局部视图的元素上,这显然导致它重新加载局部视图。我必须更改逻辑以选择该元素的子元素,或者换句话说,选择整个局部视图。 "只要节点不共享树的一部分(例如,它们是兄弟姐妹),您就可以在每个节点上安全地调用 applyBindings(事实上,这是使用第二个参数的一个原因)。”

以上是关于knockout.js:更新绑定?的主要内容,如果未能解决你的问题,请参考以下文章

knockout.js - 数据绑定文本默认值

Knockout.js简介

knockout.js 虚拟模板绑定

Knockout.JS如何绑定dom元素绑定

Knockout.js 条件绑定

Knockout.js 中的数据绑定