第 5 部分:使用 Knockout.js 创建动态 UI
作者:Rick Anderson
使用 Knockout.js 创建动态 UI
在本部分中,我们将使用Knockout.js向管理员视图添加功能。
Knockout.js 是一个 Javascript 库,可以轻松地将 HTML 控件绑定到数据。 Knockout.js使用 Model-View-ViewModel (MVVM) 模式。
- 此 模型 是业务域中数据的服务器端表示形式, (在本例中,产品和订单) 。
- 视图是 HTML) (表示层。
- 视图模型是保存模型数据的 Javascript 对象。 视图模型是 UI 的代码抽象。 它不知道 HTML 表示形式。 相反,它表示视图的抽象特征,例如“项列表”。
视图的数据绑定到视图模型。 视图模型的汇报会自动反映在视图中。 视图模型还会从视图中获取事件(如按钮单击),并对该模型执行操作,例如创建订单。
显示 H T M L 数据、视图模型、j 子级和 Web A P I 控制器之间的交互的关系图。 H T M L 数据框标记为视图。 标记为数据绑定的双箭头将 H T M L 数据框链接到视图模型框。 标有服务器中 H T P 请求和 j 子模型的双箭头将视图模型链接到 Web P I 控制器。
首先,我们将定义视图模型。 之后,我们将 HTML 标记绑定到视图模型。
将以下 Razor 部分添加到 管理员.cshtml:
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript" src="@Url.Content("~/Scripts/knockout-2.1.0.js")"></script>
<script type="text/javascript">
// View-model will go here
</script>
}
可以在文件中的任何位置添加此部分。 呈现视图时,部分显示在 HTML 页面底部的结束 </body> 标记之前。
此页面的所有脚本都将位于注释指示的脚本标记内:
<script type="text/javascript">
// View-model will go here
</script>
首先,定义视图模型类:
function ProductsViewModel() {
var self = this;
self.products = ko.observableArray();
}
ko.observableArray 是 Knockout 中的一种特殊对象,称为 可观测对象。 从 Knockout.js文档中:可观测对象是“可通知订阅者更改的 JavaScript 对象”。当可观测到的内容发生更改时,视图会自动更新以匹配。
若要填充 products
数组,请向 Web API 发出 AJAX 请求。 回想一下,我们已将 API 的基 URI 存储在视图包中 (请参阅教程) 的第 4 部分 。
function ProductsViewModel() {
var self = this;
self.products = ko.observableArray();
// New code
var baseUri = '@ViewBag.ApiUrl';
$.getJSON(baseUri, self.products);
}
接下来,将函数添加到视图模型以创建、更新和删除产品。 这些函数将 AJAX 调用提交到 Web API,并使用结果更新视图模型。
function ProductsViewModel() {
var self = this;
self.products = ko.observableArray();
var baseUri = '@ViewBag.ApiUrl';
// New code
self.create = function (formElement) {
// If the form data is valid, post the serialized form data to the web API.
$(formElement).validate();
if ($(formElement).valid()) {
$.post(baseUri, $(formElement).serialize(), null, "json")
.done(function (o) {
// Add the new product to the view-model.
self.products.push(o);
});
}
}
self.update = function (product) {
$.ajax({ type: "PUT", url: baseUri + '/' + product.Id, data: product });
}
self.remove = function (product) {
// First remove from the server, then from the view-model.
$.ajax({ type: "DELETE", url: baseUri + '/' + product.Id })
.done(function () { self.products.remove(product); });
}
$.getJSON(baseUri, self.products);
}
现在最重要的部分:当 DOM 已满加载时,调用 ko.applyBindings 函数并传入 的新实例 ProductsViewModel
:
$(document).ready(function () {
ko.applyBindings(new ProductsViewModel());
})
ko.applyBindings 方法激活 Knockout 并将视图模型连接到视图。
有了视图模型后,可以创建绑定了。 在 Knockout.js 中,可以通过向 HTML 元素添加 data-bind
属性来执行此操作。 例如,若要将 HTML 列表绑定到数组,请使用 foreach
绑定:
<ul id="update-products" data-bind="foreach: products">
绑定 foreach
循环访问数组,并为数组中的每个对象创建子元素。 子元素上的绑定可以引用数组对象上的属性。
将以下绑定添加到“update-products”列表:
<ul id="update-products" data-bind="foreach: products">
<li>
<div>
<div class="item">Product ID</div> <span data-bind="text: $data.Id"></span>
</div>
<div>
<div class="item">Name</div>
<input type="text" data-bind="value: $data.Name"/>
</div>
<div>
<div class="item">Price ($)</div>
<input type="text" data-bind="value: $data.Price"/>
</div>
<div>
<div class="item">Actual Cost ($)</div>
<input type="text" data-bind="value: $data.ActualCost"/>
</div>
<div>
<input type="button" value="Update" data-bind="click: $root.update"/>
<input type="button" value="Delete Item" data-bind="click: $root.remove"/>
</div>
</li>
</ul>
元素 <li>
出现在 foreach 绑定的范围内。 这意味着 Knockout 将为数组中的每个 products
产品呈现元素一次。 元素中的所有 <li>
绑定都引用该产品实例。 例如, $data.Name
引用 Name
产品上的 属性。
若要设置文本输入的值,请使用 value
绑定。 使用 绑定将按钮绑定到模型视图 click
上的函数。 产品实例作为参数传递给每个函数。 有关详细信息, Knockout.js文档 对各种绑定进行了很好的说明。
接下来,在“添加产品”窗体上为 提交 事件添加绑定:
<form id="addProduct" data-bind="submit: create">
此绑定调用 create
视图模型上的 函数以创建新产品。
下面是管理员视图的完整代码:
@model ProductStore.Models.Product
@{
ViewBag.Title = "Admin";
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript" src="@Url.Content("~/Scripts/knockout-2.0.0.js")"></script>
<script type="text/javascript">
function ProductsViewModel() {
var self = this;
self.products = ko.observableArray();
var baseUri = '@ViewBag.ApiUrl';
self.create = function (formElement) {
// If valid, post the serialized form data to the web api
$(formElement).validate();
if ($(formElement).valid()) {
$.post(baseUri, $(formElement).serialize(), null, "json")
.done(function (o) { self.products.push(o); });
}
}
self.update = function (product) {
$.ajax({ type: "PUT", url: baseUri + '/' + product.Id, data: product });
}
self.remove = function (product) {
// First remove from the server, then from the UI
$.ajax({ type: "DELETE", url: baseUri + '/' + product.Id })
.done(function () { self.products.remove(product); });
}
$.getJSON(baseUri, self.products);
}
$(document).ready(function () {
ko.applyBindings(new ProductsViewModel());
})
</script>
}
<h2>Admin</h2>
<div class="content">
<div class="float-left">
<ul id="update-products" data-bind="foreach: products">
<li>
<div>
<div class="item">Product ID</div> <span data-bind="text: $data.Id"></span>
</div>
<div>
<div class="item">Name</div>
<input type="text" data-bind="value: $data.Name"/>
</div>
<div>
<div class="item">Price ($)</div>
<input type="text" data-bind="value: $data.Price"/>
</div>
<div>
<div class="item">Actual Cost ($)</div>
<input type="text" data-bind="value: $data.ActualCost"/>
</div>
<div>
<input type="button" value="Update" data-bind="click: $root.update"/>
<input type="button" value="Delete Item" data-bind="click: $root.remove"/>
</div>
</li>
</ul>
</div>
<div class="float-right">
<h2>Add New Product</h2>
<form id="addProduct" data-bind="submit: create">
@Html.ValidationSummary(true)
<fieldset>
<legend>Contact</legend>
@Html.EditorForModel()
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</form>
</div>
</div>
运行应用程序,使用管理员帐户登录,然后单击“管理员”链接。 你应该会看到产品列表,并且能够创建、更新或删除产品。