Część 5. Tworzenie dynamicznego interfejsu użytkownika przy użyciu Knockout.js

Autor: Rick Anderson

Pobieranie ukończonego projektu

Tworzenie dynamicznego interfejsu użytkownika za pomocą Knockout.js

W tej sekcji użyjemy Knockout.js, aby dodać funkcje do widoku administratora.

Knockout.js to biblioteka języka JavaScript, która ułatwia powiązanie kontrolek HTML z danymi. Knockout.js używa wzorca Model-View-ViewModel (MVVM).

  • Model jest reprezentacją danych po stronie serwera w domenie biznesowej (w naszym przypadku produkty i zamówienia).
  • Widok jest warstwą prezentacji (HTML).
  • Model widoków to obiekt javascript, który przechowuje dane modelu. Model widoków to abstrakcja kodu interfejsu użytkownika. Nie ma wiedzy na temat reprezentacji HTML. Zamiast tego reprezentuje abstrakcyjne funkcje widoku, takie jak "lista elementów".

Widok jest powiązany z danymi w modelu widoku. Aktualizacje modelu widoku są automatycznie odzwierciedlane w widoku. Model widoku pobiera również zdarzenia z widoku, takie jak kliknięcia przycisków, i wykonuje operacje na modelu, takie jak tworzenie zamówienia.

Diagram interakcji między danymi H T M L, model-widokiem, JSON, a kontrolerem Web A P I.

Diagram przedstawiający interakcję między danymi HTML, modelem widoku, json i kontrolerem Web API. Pole danych H T M L ma etykietę Widok. Powiązanie danych oznaczone etykietą z dwiema strzałkami łączy pole danych H T M L z polem modelu widoku. Podwójna strzałka oznaczona jako żądania HTTP i model JSON z serwera łączy model widoku z kontrolerem Web API.

Najpierw zdefiniujemy model widoku. Następnie powiążemy znaczniki HTML z modelem widoku.

Dodaj następującą sekcję Razor do pliku 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>
}

Tę sekcję można dodać w dowolnym miejscu w pliku. Gdy widok jest renderowany, sekcja zostanie wyświetlona w dolnej części strony HTML bezpośrednio przed zamknięciem </body> tag.

Cały skrypt dla tej strony przejdzie do tagu skryptu wskazanego przez komentarz:

<script type="text/javascript">
  // View-model will go here
  </script>

Najpierw zdefiniuj klasę modelu widoku:

function ProductsViewModel() {
    var self = this;
    self.products = ko.observableArray();
}

ko.observableArray jest specjalnym rodzajem obiektu w Knockout, nazywanym obserwowalnym. W dokumentacji Knockout.js : Obiekt obserwowalny to "obiekt JavaScript, który może powiadamiać subskrybentów o zmianach". Gdy zawartość obiektu obserwowalnego się zmienia, widok zostanie automatycznie zaktualizowany, aby pasować do nowych danych.

Aby wypełnić tablicę products , utwórz żądanie AJAX do internetowego interfejsu API. Przypomnij sobie, że podstawowy identyfikator URI interfejsu API jest przechowywany w ViewBag (zobacz Część 4 samouczka).

function ProductsViewModel() {
    var self = this;
    self.products = ko.observableArray();

    // New code
    var baseUri = '@ViewBag.ApiUrl';
    $.getJSON(baseUri, self.products);
}

Następnie dodaj funkcje do modelu widoku, aby tworzyć, aktualizować i usuwać produkty. Te funkcje przesyłają wywołania AJAX do internetowego interfejsu API i używają wyników do aktualizowania modelu widoku.

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);
}

Teraz najważniejsza część: kiedy DOM jest w pełni załadowany, wywołaj funkcję ko.applyBindings i przekaż nowe wystąpienie ProductsViewModel.

$(document).ready(function () {
    ko.applyBindings(new ProductsViewModel());
})

Metoda ko.applyBindings aktywuje Knockout i łączy model widoku z widokiem.

Teraz, gdy mamy model widoku, możemy utworzyć powiązania. W Knockout.jsmożna to zrobić, dodając data-bind atrybuty do elementów HTML. Aby na przykład powiązać listę HTML z tablicą, użyj foreach powiązania:

<ul id="update-products" data-bind="foreach: products">

foreach Powiązanie iteruje przez tablicę, tworząc elementy podrzędne dla każdego z obiektów w tablicy. Powiązania elementów podrzędnych mogą odwoływać się do właściwości obiektów tablicy.

Dodaj następujące powiązania do listy "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>

Element <li> występuje w zakresie powiązania foreach . Oznacza to, że funkcja Knockout będzie renderować element raz dla każdego produktu w tablicy products . Wszystkie powiązania w elemecie <li> odwołują się do tego wystąpienia produktu. Na przykład $data.Name odwołuje się do Name właściwości produktu.

Aby ustawić wartości wejściowych tekstu, użyj value powiązania. Przyciski są powiązane z funkcjami w widoku modelu przy użyciu click powiązania. Wystąpienie produktu jest przekazywane jako parametr do każdej funkcji. Aby uzyskać więcej informacji, dokumentacja Knockout.js zawiera dobre opisy różnych wiązań.

Następnie przypisz powiązanie dla zdarzenia submit w formularzu Dodaj produkt:

<form id="addProduct" data-bind="submit: create">

To powiązanie wywołuje funkcję create w widoku modelu, aby utworzyć nowy produkt.

Oto pełny kod widoku administratora:

@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>

Uruchom aplikację, zaloguj się przy użyciu konta administratora i kliknij link "Administrator". Powinna zostać wyświetlona lista produktów i możliwość tworzenia, aktualizowania lub usuwania produktów.