第 7 部分:建立主要頁面
作者:Rick Anderson
建立主要頁面
在本節中,您將建立主要應用程式頁面。 此頁面會比 [管理員] 頁面複雜,因此我們會以數個步驟加以處理。 您會接續看到一些更進階的 Knockout.js 技術。 以下是頁面的基本版面配置:
產品、購物車、訂單和訂單詳細資料元素之間的互動圖表。 products 元素會標示為 GET A P I / products,其中包含指向 items 元素的箭號。 items 元素會依標示為 POST A P I / orders 的箭號連接到 orders 元素。 orders 元素會依標示為 GET A P I / orders 的箭號連接到 details 元素。 details 元素標示為 GET A P I / orders / i d。
- 「Products」會保留產品陣列。
- 「購物車」保留具有數量的產品陣列。 按一下 [新增至購物車] 會更新購物車。
- 「訂單」會保留訂單識別碼陣列。
- 「詳細資料」保留訂單詳細資料,這是項目陣列 (有數量的產品)
我們將從在 HTML 中定義一些基本版面配置開始,而不需要資料繫結或指令碼。 開啟 Views/Home/Index.cshtml 檔案,並將所有內容取代為下列:
<div class="content">
<!-- List of products -->
<div class="float-left">
<h1>Products</h1>
<ul id="products">
</ul>
</div>
<!-- Cart -->
<div id="cart" class="float-right">
<h1>Your Cart</h1>
<table class="details ui-widget-content">
</table>
<input type="button" value="Create Order"/>
</div>
</div>
<div id="orders-area" class="content" >
<!-- List of orders -->
<div class="float-left">
<h1>Your Orders</h1>
<ul id="orders">
</ul>
</div>
<!-- Order Details -->
<div id="order-details" class="float-right">
<h2>Order #<span></span></h2>
<table class="details ui-widget-content">
</table>
<p>Total: <span></span></p>
</div>
</div>
接下來,新增指令碼區段並建立空的檢視模型:
@section Scripts {
<script type="text/javascript" src="@Url.Content("~/Scripts/knockout-2.1.0.js")"></script>
<script type="text/javascript">
function AppViewModel() {
var self = this;
self.loggedIn = @(Request.IsAuthenticated ? "true" : "false");
}
$(document).ready(function () {
ko.applyBindings(new AppViewModel());
});
</script>
}
根據稍早所繪製的設計,我們的檢視模型需要可觀察的產品、購物車、訂單和詳細資料。 將以下變數新增到 AppViewModel
物件中:
self.products = ko.observableArray();
self.cart = ko.observableArray();
self.orders = ko.observableArray();
self.details = ko.observable();
使用者可以將產品清單中的項目新增至購物車,並從購物車中移除項目。 為了封裝這些函式,我們將建立另一個代表產品的檢視模型類別。 將下列程式碼新增至 AppViewModel
:
function AppViewModel() {
// ...
// NEW CODE
function ProductViewModel(root, product) {
var self = this;
self.ProductId = product.Id;
self.Name = product.Name;
self.Price = product.Price;
self.Quantity = ko.observable(0);
self.addItemToCart = function () {
var qty = self.Quantity();
if (qty == 0) {
root.cart.push(self);
}
self.Quantity(qty + 1);
};
self.removeAllFromCart = function () {
self.Quantity(0);
root.cart.remove(self);
};
}
}
ProductViewModel
類別包含兩個函式,用來將產品移入和移出購物車:addItemToCart
可將一個單位的產品新增至購物車,而 removeAllFromCart
可移除產品的所有數量。
使用者可以選取現有的訂單並取得訂單詳細資料。 我們將此功能封裝到另一個檢視模型:
function AppViewModel() {
// ...
// NEW CODE
function OrderDetailsViewModel(order) {
var self = this;
self.items = ko.observableArray();
self.Id = order.Id;
self.total = ko.computed(function () {
var sum = 0;
$.each(self.items(), function (index, item) {
sum += item.Price * item.Quantity;
});
return '$' + sum.toFixed(2);
});
$.getJSON("/api/orders/" + order.Id, function (order) {
$.each(order.Details, function (index, item) {
self.items.push(item);
})
});
};
}
OrderDetailsViewModel
會使用訂單初始化,並傳送 AJAX 要求至伺服器來擷取訂單詳細資料。
同時也請注意 OrderDetailsViewModel
上的 total
屬性。 這個屬性是一種特殊的可觀察值,稱為計算的可觀察值。 顧名思義,計算的可觀察值讓您將資料繫結至計算值,在此情況下即為訂單的總成本。
接下來,將這些函式新增到 AppViewModel
:
resetCart
從購物車中移除所有項目。getDetails
可取得訂單的詳細數資料 (藉由將新OrderDetailsViewModel
推入details
清單)。createOrder
會建立新的訂單,並清空購物車。
function AppViewModel() {
// ...
// NEW CODE
self.resetCart = function() {
var items = self.cart.removeAll();
$.each(items, function (index, product) {
product.Quantity(0);
});
}
self.getDetails = function (order) {
self.details(new OrderDetailsViewModel(order));
}
self.createOrder = function () {
var jqxhr = $.ajax({
type: 'POST',
url: "api/orders",
contentType: 'application/json; charset=utf-8',
data: ko.toJSON({ Details: self.cart }),
dataType: "json",
success: function (newOrder) {
self.resetCart();
self.orders.push(newOrder);
},
error: function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
}
});
};
};
最後,對產品和訂單提出 AJAX 要求,以初始化檢視模型:
function AppViewModel() {
// ...
// NEW CODE
// Initialize the view-model.
$.getJSON("/api/products", function (products) {
$.each(products, function (index, product) {
self.products.push(new ProductViewModel(self, product));
})
});
$.getJSON("api/orders", self.orders);
};
好,這有很多程式碼,但我們逐步條列它,所以希望設計的清楚。 現在我們可以將一些 Knockout.js 繫結新增至 HTML。
產品
以下是產品清單的繫結:
<ul id="products" data-bind="foreach: products">
<li>
<div>
<span data-bind="text: Name"></span>
<span class="price" data-bind="text: '$' + Price"></span>
</div>
<div data-bind="if: $parent.loggedIn">
<button data-bind="click: addItemToCart">Add to Order</button>
</div>
</li>
</ul>
這會逐一查看產品陣列,並顯示名稱和價格。 只有當使用者登入時,才會顯示 [新增至訂單] 按鈕。
[新增至訂單] 按鈕會呼叫產品 ProductViewModel
執行個體上的 addItemToCart
。 這展示了 Knockout.js 的良好功能:當檢視模型包含其他檢視模型時,您可以將繫結套用至內部模型。 在此範例中,foreach
內的繫結會套用至每個 ProductViewModel
執行個體。 這種方法比將所有功能放入單一檢視模型更簡潔。
購物車
以下是購物車的繫結:
<div id="cart" class="float-right" data-bind="visible: cart().length > 0">
<h1>Your Cart</h1>
<table class="details ui-widget-content">
<thead>
<tr><td>Item</td><td>Price</td><td>Quantity</td><td></td></tr>
</thead>
<tbody data-bind="foreach: cart">
<tr>
<td><span data-bind="text: $data.Name"></span></td>
<td>$<span data-bind="text: $data.Price"></span></td>
<td class="qty"><span data-bind="text: $data.Quantity()"></span></td>
<td><a href="#" data-bind="click: removeAllFromCart">Remove</a></td>
</tr>
</tbody>
</table>
<input type="button" data-bind="click: createOrder" value="Create Order"/>
這會逐一查看購物車陣列,並顯示名稱、價格和數量。 請注意,[移除] 連結和 [建立訂單] 按鈕會繫結至檢視模型函式。
訂單
以下是訂單清單的繫結:
<h1>Your Orders</h1>
<ul id="orders" data-bind="foreach: orders">
<li class="ui-widget-content">
<a href="#" data-bind="click: $root.getDetails">
Order # <span data-bind="text: $data.Id"></span></a>
</li>
</ul>
這會逐一查看訂單,並顯示訂單識別碼。 連結上的 Click 事件會系結至 getDetails
函式。
訂單詳細資料
以下是訂單詳細資料的繫結:
<div id="order-details" class="float-right" data-bind="if: details()">
<h2>Order #<span data-bind="text: details().Id"></span></h2>
<table class="details ui-widget-content">
<thead>
<tr><td>Item</td><td>Price</td><td>Quantity</td><td>Subtotal</td></tr>
</thead>
<tbody data-bind="foreach: details().items">
<tr>
<td><span data-bind="text: $data.Product"></span></td>
<td><span data-bind="text: $data.Price"></span></td>
<td><span data-bind="text: $data.Quantity"></span></td>
<td>
<span data-bind="text: ($data.Price * $data.Quantity).toFixed(2)"></span>
</td>
</tr>
</tbody>
</table>
<p>Total: <span data-bind="text: details().total"></span></p>
</div>
這會逐一查看訂單中的項目,並顯示產品、價格和數量。 只有當詳細資料陣列包含一或多個項目時,才會顯示周圍的 div。
結論
在本教學課程中,您已建立使用 Entity Framework 與資料庫通訊的應用程式,而 ASP.NET Web API 可在資料層頂端提供公開介面。 我們使用 ASP.NET MVC 4 來轉譯 HTML 頁面,Knockout.js 加上 jQuery 來提供動態互動,而不需要重載頁面。
其他資源: