共用方式為


第 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 資料、檢視模型、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>

執行應用程式、使用管理員帳戶登入,然後按一下 [管理員] 連結。 您應該會看到產品清單,並且能夠建立、更新或刪除產品。