是否可以暂停属性的初始 Knockout 评估,直到用户更改关联的可观察对象?

Is it possible to pause initial Knockout evaluation of an attribute, until a user changes the associated observable?

我正在开发一项网站功能,允许用户使用 knockout.js 对嵌入的 YouTube 视频进行实时更改(如大小和 URL)。该页面最初加载一个带有硬编码属性默认值和数据绑定的 iframe,但它尚未绑定

<iframe data-bind="attr: {src: videoUrl() }, style: {width: width() + 'px', height: height() + 'px', }" style="width: 420px; height: 315px;" src="//www.youtube.com/embed/OvxlHa6yjqM" frameborder="0" allowfullscreen></iframe>

绑定会在一些用户交互后应用到 iframe - 比方说单击 'Show Options' 按钮。

$('some_button').click(function() { ko.applyBindings(viewModel) });

视图模型包含视频选项。

var viewModel = {
    width: ko.observable(420),
    height: ko.observable(315),
    videoId: ko.observable('OvxlHa6yjqM')
}

iframe src 是一个计算的可观察对象。

viewModel.videoUrl = this.ko.computed( function () {
    return 'http://www.youtube.com/embed/' + this.videoId();
}, viewModel );

一旦 ko.applyBindings() 被执行 knockout.js 评估所有绑定的可观察对象并在 HTML 中输出它们,即使它们与硬编码的相同。宽度和高度等属性会立即重新呈现,但控制 src 属性的 videoUrl 计算可观察对象会导致 iframe 重新呈现并闪烁一秒钟,即使它的 src 没有变化。如果用户输入新的 videoId,iframe 将闪烁并重新呈现新视频,这是完全正常的。但是 有没有办法 prevent/pause 对 videoUrl 进行初始评估,直到它实际上被更改 为其他东西,例如通过输入一个新的 videoId

JS Fiddle example

我试过 deferEvaluation 计算选项,但它对我不起作用(可能它的用途不同)。我尝试了一些条件绑定处理程序,但似乎无法找出正确的逻辑。

更新

Knockout.js 未加载到初始 iframe 加载的文档中。它加载在恰好是一个单独的 iframe 的选项容器中。这意味着 knockout 仅在用户调出选项(单击按钮)后加载并应用绑定到父文档中的 iframe。

视图模型因此分别应用于选项列表和 iframe 容器。

最简单的方法是在 DOM 加载时立即调用 applyBindings,并且只有 show/hide 单击按钮时的选项。

首先,删除显式 src 属性并让 Knockout 在绑定时设置属性:

<iframe data-bind="attr: {src: videoUrl() }, style: {width: width() + 'px', height: height() + 'px', }" style="width: 420px; height: 315px;" frameborder="0" allowfullscreen></iframe>

然后将 applyBinding 调用移到点击处理程序之外:

$('bind').addEvent('click', function () {
    this.hide();
    $('options').show();
});

ko.applyBindings(viewModel);

Fiddle

我找不到一个神奇的解决方案,但毕竟我的问题很具体。我想出了一些解决这个问题的方法。

方法 1:在第一次更改后应用绑定

由于 ViewModel 分别应用于选项列表和 iframe,因此最简单的解决方案是首先仅将其应用于选项列表,然后仅在更改任何选项后才将其应用于 iframe。这样 iframe 不会在进入编辑模式时闪烁,但会在任何第一次更改时闪烁 - 即使这是对大小的更改。

方法 2:防止初始 iframe src 绑定

我在这里找到了一个很好的技巧:How to stop knockout.js bindings evaluating on child elements

在 iframe 的父元素上使用 stopBindings: true(阅读上面的讨论)绑定,我能够正常应用绑定并且仅忽略 iframe 本身。现在我可以通过 viewModel.videoId.subscribe( function () {}) 检测到视频源(视频 ID)的变化,并通过 ko.computedContext.isInitial() 检查这是否是初始变化。我可以将 stopBindings 设置为 false。缺点是,现在为了监视对 src 的更改,我需要使用 ko.cleanNode(element) 重新启动绑定,然后再次 ko.applyBindings。但这有效,现在视频只有在更改 src 后才会闪烁。

方法 3:两个 ViewModel

我什至还没有开始探索这个解决方案,但它基本上涉及到 videoId 有一个单独的视图模型,它将在进行更改后应用 - 就像上面的解决方案一样。这将防止重新绑定,但我认为这是相当昂贵的。