原文:【ASP.NET Web API教程】2.3.5 用Knockout.js创建动态UI
注:本文是【ASP.NET Web API系列教程】的一部分,如果您是第一次看本博客文章,请先看前面的内容。
Part 5: Creating a Dynamic UI with Knockout.js
第5部分:用Knockout.js创建动态UI
本文引自:http://www.asp.net/web-api/overview/creating-web-apis/using-web-api-with-entity-framework/using-web-api-with-entity-framework,-part-5
Creating a Dynamic UI with Knockout.js
用Knockout.js创建动态UI
In this section, we'll use Knockout.js to add functionality to the Admin view.
在本小节中,我们将用Knockout.js对Admin视图添加功能。
Knockout.js is a Javascript library that makes it easy to bind HTML controls to data. Knockout.js uses the Model-View-ViewModel (MVVM) pattern.
Knockout.js是一个JavaScript库,它让HTML控件很容易与数据进行绑定。Knockout.js使用的是“模型-视图-视图模型(MVVM)”模式。
- The model is the server-side representation of the data in the business domain (in our case, products and orders).
模型是事务域中数据的服务器端表示(在我们的示例中,是产品和订单)。 - The view is the presentation layer (HTML).
视图是表现层(HTML)。 - The view-model is a Javascript object that holds the model data. The view-model is a code abstraction of the UI. It has no knowledge of the HTML representation. Instead, it represents abstract features of the view, such as "a list of items".
视图模型是保存了模型数据的一个JavaScript对象。视图模型是UI的一种代码抽象。它没有HTML表示方面的知识,但它表现了视图的抽象特性,如“列表项”。
The view is data-bound to the view-model. Updates to the view-model are automatically reflected in the view. The view-model also gets events from the view, such as button clicks, and performs operations on the model, such as creating an order.
视图是与视图模型数据绑定的。对视图模型的更新会自动地在视图得到反映。视图模型也会获取视图的事件,如按钮点击,并在模型上执行操作(见图2-23)。
图2-23. 模型-视图-视图模型之间的关系
First we'll define the view-model. After that, we will bind the HTML markup to the view-model.
首先,我们要定义视图模型。之后,要将HTML标记与视图模型进行绑定。
Add the following Razor section to Admin.cshtml:
对Admin.cshtml添加以下Razor片段:
@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>
}
You can add this section anywhere in the file. When the view is rendered, the section appears at the bottom of the HTML page, right before the closing </body> tag.
可以把这个片断添加到文件的任何地方。当视图被渲染时,这个片段会出现在HTML页面的底部,</body>关闭标签的前面。
All of the script for this page will go inside the script tag indicated by the comment:
用于这个页面的所有脚本都被放在由以下注释所指示的script标签中:
<script type="text/javascript">
// View-model will go here
// 视图模型会放在这儿
</script>
First, define a view-model class:
首先,定义一个视图模型类:
function ProductsViewModel() {
var self = this;
self.products = ko.observableArray();
}
ko.observableArray is a special kind of object in Knockout, called an observable. From the Knockout.js documentation: An observable is a “JavaScript object that can notify subscribers about changes.” When the contents of an observable change, the view is automatically updated to match.
ko.observableArray是Knockout中的一种叫做observable的特殊对象(请将这种observable对象称为可见对象。这种对象往往作为视图模型与视图进行交互,对视图而言,它是透明可见的,故称为可见对象。在以下翻译中,都将这种observable对象称为可见对象 — 译者注)。根据Knockout.js文档的描述:可见对象是一种“能够通知订户数据变化情况的JavaScript对象”。当一个可见对象的内容发生变化时,视图会自动地进行匹配更新。
To populate the products array, make an AJAX request to the web API. Recall that we stored the base URI for the API in the view bag (see Part 4 of the tutorial).
为了填充Products数组,需要形成一个发送到Web API的请求。调回我们在视图包(View Bag)中存储的、用于此API的基URI(见本教程的第4部分)。
function ProductsViewModel() {
var self = this;
self.products = ko.observableArray();
// New code
// 新代码
var baseUri = '@ViewBag.ApiUrl';
$.getJSON(baseUri, self.products);
}
Next, add functions to the view-model to create, update, and delete products. These functions submit AJAX calls to the web API and use the results to update the view-model.
下一步,对视图模型添加创建、更新以及删除产品的函数。这些函数会对Web API递交AJAX调用,并使用(所得到的)结果对视图模型进行更新。
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.
// 如果表单数据有效,把序列化的表单数据递交给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);
}
Now the most important part: When the DOM is fulled loaded, call the ko.applyBindings function and pass in a new instance of the ProductsViewModel:
现在,最重要的部分:当DOM全部装载时,调用ko.applyBindings函数,并在其中传递一个新的ProductsViewModel实例:
$(document).ready(function () {
ko.applyBindings(new ProductsViewModel());
})
The ko.applyBindings method activates Knockout and wires up the view-model to the view.
ko.applyBindings方法会激活Knockout,并将视图模型与视图连接起来。
Now that we have a view-model, we can create the bindings. In Knockout.js, you do this by adding data-bind attributes to HTML elements. For example, to bind an HTML list to an array, use the foreach binding:
现在,我们有了一个视图模型,于是可以创建绑定(这里的绑定含义是将视图模型中的数据项与视图中的各个HTML控件进行绑定 — 译者注)。在Knockout.js中,通过把data-bind标签属性(标签属性是指HTML元素的属性,这样翻译的目的也是与类的属性有所区别 — 译者注)添加到HTML元素的办法来做这件事。例如,要把一个HTML列表绑定到一个数据,使用foreach绑定:
<ul id="update-products" data-bind="foreach: products">
The foreach binding iterates through the array and creates child elements for each object in the array. Bindings on the child elements can refer to properties on the array objects.
foreach绑定会遍历数组,并为数组中的每个对象创建子元素。在子元素上的绑定可以指向数组对象上的属性。
Add the following bindings to the "update-products" list:
对“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>
The <li> element occurs within the scope of the foreach binding. That means Knockout will render the element once for each product in the products array. All of the bindings within the <li> element refer to that product instance. For example, $data.Name refers to the Name property on the product.
<li>元素出现在foreach绑定的范围之内。这意味着Knockout会对products数组中的每个产品渲染一个元素。在<li>元素中的所有绑定指向这个产品实例。例如,$data.Name是指该产品上的Name属性。
To set the values of the text inputs, use the value binding. The buttons are bound to functions on the model-view, using the click binding. The product instance is passed as a parameter to each function. For more information, the Knockout.js documentation has good descriptions of the various bindings.
设置文本输入框的值,要使用value绑定。按钮要绑定到视图模型上的函数,需使用click绑定。产品实例是作为参数传递给每个函数的。对于更多信息,Knockout.js文档对各种绑定有很好的描述。
Next, add a binding for the submit event on the Add Product form:
下一步,为“添加产品”表单上的submit(递交)事件添加一个绑定:
<form id="addProduct" data-bind="submit: create">
This binding calls the create function on the view-model to create a new product.
这个绑定调用视图模型上的create函数,以创建一个新产品。
Here is the complete code for the Admin view:
以下是Admin视图的完整代码:
@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
// 如果有效,把序列化的表单数据递交(post)给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
// 先从服务器删除,然后从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>
Run the application, log in with the Administrator account, and click the "Admin" link. You should see the list of products, and be able to create, update, or delete products.
运行应用程序,用Administrator账号登录,并点击“Admin”链接。应当看到产品列表,并能够创建、更新或删除产品。
看完此文如果觉得有所收获,恳请给个推荐