第 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 子和 Web P I 控制器之间的交互关系图。

显示 H T M L 数据、视图模型、j 子级和 Web A P I 控制器之间的交互的关系图。 H T M L 数据框标记为视图。 标记为数据绑定的双箭头将 H T M L 数据框链接到视图模型框。 标有服务器中 H T P 请求和 j 子模型的双箭头将视图模型链接到 Web P I 控制器。

首先,我们将定义视图模型。 之后,我们将 HTML 标记绑定到视图模型。

将以下 Razor 部分添加到 管理员.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 中,可以通过向 HTML 元素添加 data-bind 属性来执行此操作。 例如,若要将 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>

运行应用程序,使用管理员帐户登录,然后单击“管理员”链接。 你应该会看到产品列表,并且能够创建、更新或删除产品。