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

Autor: Rick Anderson

Pobieranie ukończonego projektu

Tworzenie dynamicznego interfejsu użytkownika z użyciem biblioteki Knockout.js

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

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 widoku jest obiektem javascript, który przechowuje dane modelu. Model-widok jest abstrakcją 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 z modelem widoku. Aktualizacje do modelu widoku są automatycznie odzwierciedlane w widoku. Model-widok 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, modelem widoku, synem i kontrolerem Web A P I.

Diagram przedstawiający interakcję między danymi H T M L, modelem widoku, synem i kontrolerem Web A P I. Pole danych H T M L jest oznaczone etykietą widok. Podwójna strzałka z etykietą powiązania danych łączy pole danych H T M L z polem modelu widoku. Podwójna strzałka z etykietą H T T P P i model syna j z serwera łączy model widoku z kontrolerem Web A P I.

Najpierw zdefiniujemy model widoku. Następnie utworzymy powiązanie znaczników HTML z modelem widoku.

Dodaj następującą sekcję Razor do pliku Administracja.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 zamykającym <tagiem /body> .

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. Z dokumentacjiKnockout.js: Można zaobserwować to "obiekt JavaScript, który może powiadamiać subskrybentów o zmianach". Gdy zawartość obserwowalnej zmiany, widok jest automatycznie aktualizowany w celu dopasowania.

Aby wypełnić tablicę products , utwórz żądanie AJAX do internetowego interfejsu API. Pamiętaj, że podstawowy identyfikator URI interfejsu API jest przechowywany w torbie widoku (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żniejszą częścią: Po załadowaniu modelu DOM wywołaj funkcję ko.applyBindings i przekaż nowe wystąpienie elementu ProductsViewModel:

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

Metoda ko.applyBindings aktywuje knockout i podłącza model widoku do widoku.

Teraz, gdy mamy już model widoku, możemy utworzyć powiązania. W Knockout.js moż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 tablicę i tworzy elementy podrzędne dla każdego obiektu w tablicy. Powiązania elementów podrzędnych mogą odwoływać się do właściwości obiektów tablicowych.

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 danych 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, dokumentacjaKnockout.js zawiera dobre opisy różnych powiązań.

Następnie dodaj powiązanie zdarzenia przesyłania w formularzu Dodaj produkt:

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

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

Oto kompletny kod widoku Administracja:

@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 "Administracja". Powinna zostać wyświetlona lista produktów i możliwość tworzenia, aktualizowania lub usuwania produktów.