将现有 DOM 元素绑定到视图模型
Posted
技术标签:
【中文标题】将现有 DOM 元素绑定到视图模型【英文标题】:Bind exsiting DOM elements to view model 【发布时间】:2014-09-24 18:26:19 【问题描述】:我想使用 knockoutjs 显示数组中的数据,我可以使用 foreach 绑定来做到这一点,如下所示:
<ul data-bind="foreach: elements">
<li data-bind="text: data1"></li>
<li data-bind="text: data2"></li>
<li data-bind="text: data3"></li>
</ul>
这可以正常工作,但问题是它只能在页面完全加载后完成,我不喜欢这样,因为我可以在服务器上构建初始列表,这样它加载数据的速度更快。我会从服务器收到类似的东西:
<ul>
<li>Data1 value</li>
<li>Data2 value</li>
<li>Data3 value</li>
</ul>
是否可以将这些列表元素绑定到视图模型数组?
【问题讨论】:
“有可能吗?” - 是的,但有什么区别?它并不是更快,因为您仍然必须加载数据,您只是认为它更快,因为您可能正在加载一堆脚本。如果您坚持这样做,只需使用选择器来抓取元素并获取它们的值并在页面加载后将它们填充到数组中。 来自@PWKad 的明智之言。请避免这种情况,这不是淘汰方式 请不要让您的服务器生成标记... 使用 Knockout 的众多原因之一是将逻辑从服务器转移到客户端。假设您有 1000 多个用户。您的服务器可以使用其 10 个 CPU 轻松处理数据和标记,或者您可以让服务器只处理数据,而您的 1000 个客户端的 CPU 可以处理标记。 当用户在页面上时不会改变的事情 - 在服务器上执行。需要实时更新的内容,无需刷新,您将使用 jQuery 或任何其他框架插入的内容 - 使用敲除和 AJAX 调用您的 API 来执行此操作。 【参考方案1】:这可以使用 jQuery 来解析服务器生成的列表,然后填充 KO observableArray,然后调用 applyBindings,它将服务器列表替换为 KO 绑定列表。
http://jsfiddle.net/dLbc4/1/
更多信息
正如上面的评论者所指出的,这不是纯粹的 KO 做事方式。但是,一些 JS 框架现在会在服务器上预渲染初始内容,然后在页面显示给用户后应用它们的绑定。原因包括:
性能 - 立即显示完整的页面 SEO - 一个完整的 html 文档可供搜索引擎使用 旧版浏览器/移动设备支持 - 这些设备可以使用预构建的 HTML 页面,尽管对它的作用不大执行此操作的框架示例:
http://derbyjs.com/ 与 http://expressjs.com/ 服务器应用程序 https://github.com/rendrjs/rendr 用于 Backbone 应用程序这些允许使用相同代码库的页面的服务器呈现版本;即,相同的逻辑用于在服务器和客户端上创建 HTML。
KO可以做到吗?我不明白为什么不这样做,也许这里有一个有趣的项目可以做到这一点(或者有人已经知道一个项目了吗?)。同一个服务器端数据库可以通过 API 提供静态 HTML 页面和实际数据,以便 KO 稍后呈现。最大的挑战是单一的代码库问题,否则你最终会在服务器上渲染 HTML 的服务器端逻辑(用 php 或 Python 或其他),然后在客户端通过 KO 的 javascript 逻辑渲染 HTML。
这里是一个快速的小提琴,展示了原始问题中 HTML 的概念:
http://jsfiddle.net/dLbc4/1/
在这个概念中,除了由服务器预渲染的实际数据之外,您在服务器 HTML 中添加所有 KO 绑定。为了演示这一点,我添加了一个向列表添加新元素的简单示例。除了 'text: $data' 绑定之外,所有的 KO 语句都在那里。
<ul class="preload" data-bind="foreach: elements">
<li>Data1 value</li>
<li>Data2 value</li>
<li>Data3 value</li>
</ul>
<input type="text" name="new" data-bind="value: new_element">
<button data-bind="click: add_element">add</button>
所以页面是“完整的”——非常适合 SEO、性能等。现在我们需要一些代码来提取数据值并将它们添加到视图模型中。这是我的虚拟机——它处理元素列表和水果列表,只是为了演示一些步骤来使这个通用而不是完全硬编码为只支持一个称为“元素”的结构:
AppViewModel = function()
// 'Elements' list suport
this.elements = ko.observableArray();
this.add_element = function(data, event)
this.elements.push(this.new_element());
this.new_element = ko.observable();
// 'Fruits' list support
this.fruits = ko.observableArray();
this.add_fruit = function(data, event)
this.fruits.push(this.new_fruit());
this.new_fruit = ko.observable();
最后,这是用绑定到 VM 的 KO 渲染内容替换来自服务器的静态 HTML 的代码。您可以检查它是否有效,因为小提琴中的“添加”按钮将分别添加新元素或新水果。代码有点通用,因为相同的代码处理元素列表和水果列表。但是,您可以看到它假定您的列表子元素将始终是一个 li - 所以需要在那里做一些工作!
initKO = function()
// Start with an empty view model
var app_vm = new AppViewModel();
// Handle any list tagged with class="preload"
$(".preload").each(function(i, el)
// Extract the data from the HTML and populate the view model
var array_name = $(el).attr("data-bind").split(":")[1].trim();
$(el).children().each(function(j, child)
app_vm[array_name].push($(child).text());
);
// Empty the prebuilt static HTML
$(el).empty();
// Add a new node for binding
$(el).append($("<li data-bind='text: $data'></li>"));
);
// Call applyBindings to rebuild the HTML via KO
ko.applyBindings(app_vm);
// Do all this after document.ready. The page initially appears with the
// server-provided static HTML. We then replace this HTML with a KO-rendered
// DOM.
$(document).ready(function()
initKO();
);
【讨论】:
这对我有用。性能和旧浏览器支持是我想这样做的主要原因。感谢您提供详细的答案和工作小提琴。【参考方案2】:查看 Erik Schierboom 的解决方案:https://github.com/ErikSchierboom/knockout-pre-rendered。它基本上可以满足您的需求
【讨论】:
以上是关于将现有 DOM 元素绑定到视图模型的主要内容,如果未能解决你的问题,请参考以下文章