第 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 son 和 Web A P I 控制器之間的互動圖表。 H T M L 資料方塊已標記為檢視。 標記為雙箭號的資料繫結會將 H T M L 資料方塊連結至檢視模型方塊。 標記為 H T T P 要求的雙箭號,以及伺服器 j son 模型會將檢視模型連結至 Web A P I 控制器。
首先,我們將定義檢視模型。 之後,我們會將 HTML 標記繫結至檢視模型。
將下列 Razor 區段新增至 Admin.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 中,您可將 data-bind
屬性新增至 HTML 元素來執行此動作。 例如,若要將 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>
執行應用程式、使用管理員帳戶登入,然後按一下 [管理員] 連結。 您應該會看到產品清單,並且能夠建立、更新或刪除產品。