共用方式為


第 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 來提供動態互動,而不需要重載頁面。

其他資源: