Condividi tramite


Parte 5: Creazione di un'interfaccia utente dinamica con Knockout.js

di Rick Anderson

Scaricare il progetto completato

Creazione di un'interfaccia utente dinamica con Knockout.js

In questa sezione si userà Knockout.js per aggiungere funzionalità alla visualizzazione Amministratore.

Knockout.js è una libreria Javascript che semplifica l'associazione di controlli HTML ai dati. Knockout.js usa il modello MVVM (Model-View-ViewModel).

  • Il modello è la rappresentazione lato server dei dati nel dominio aziendale (in questo caso, prodotti e ordini).
  • La visualizzazione è il livello di presentazione (HTML).
  • Il modello di visualizzazione è un oggetto Javascript che contiene i dati del modello. Il modello di visualizzazione è un'astrazione del codice dell'interfaccia utente. Non ha alcuna conoscenza della rappresentazione HTML. Rappresenta invece le caratteristiche astratte della visualizzazione, ad esempio "un elenco di elementi".

La vista è associata a dati al modello di visualizzazione. Gli aggiornamenti al modello di visualizzazione vengono riflessi automaticamente nella visualizzazione. Il modello di visualizzazione ottiene anche gli eventi dalla visualizzazione, ad esempio i clic sui pulsanti ed esegue operazioni sul modello, ad esempio la creazione di un ordine.

Diagramma dell'interazione tra i dati H T M L, il modello di visualizzazione, j son e il controller Web A P I.

Diagramma che mostra l'interazione tra i dati HTML, il modello-vista, JSON e il controller API Web. La casella dati HTML è etichettata come vista. Una doppia freccia etichettata "data binding" collega la casella dati HTML alla casella del modello di vista. Una doppia freccia con etichetta richieste HTTP e modello JSON dal server collega il view-model al controller dell'API Web.

Prima di tutto si definirà il modello di visualizzazione. Successivamente, il markup HTML verrà associato al modello di visualizzazione.

Aggiungere la seguente sezione Razor a 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>
}

È possibile aggiungere questa sezione in qualsiasi punto del file. Quando la visualizzazione viene resa, la sezione appare nella parte inferiore della pagina HTML, proprio prima del tag di chiusura </body>.

Tutto lo script per questa pagina verrà inserito all'interno del tag di script indicato dal commento:

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

Per prima cosa, definire una classe view-model:

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

ko.observableArray è un tipo speciale di oggetto in Knockout, chiamato osservabile. Dalla documentazione diKnockout.js: un oggetto osservabile è un "oggetto JavaScript in grado di notificare ai sottoscrittori le modifiche". Quando il contenuto di una modifica osservabile, la visualizzazione viene aggiornata automaticamente in modo che corrisponda.

Per popolare la products matrice, effettuare una richiesta AJAX all'API Web. Ricorda che l'URI di base per l'API è stato archiviato nel view bag (vedi Parte 4 dell'esercitazione).

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

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

Aggiungere quindi funzioni al modello di visualizzazione per creare, aggiornare ed eliminare prodotti. Queste funzioni inviano chiamate AJAX all'API Web e usano i risultati per aggiornare il modello di visualizzazione.

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

Ora la parte più importante: quando il DOM è completamente caricato, chiamate la funzione ko.applyBindings e passate una nuova istanza di ProductsViewModel:

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

Il metodo ko.applyBindings attiva Knockout e collega il modello di visualizzazione alla visualizzazione.

Ora che è disponibile un modello di visualizzazione, è possibile creare le associazioni. In Knockout.js, puoi farlo aggiungendo data-bind attributi agli elementi HTML. Ad esempio, per associare un elenco HTML a una matrice, usare l'associazione foreach :

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

L'associazione foreach scorre la matrice e crea elementi figlio per ogni oggetto nella matrice. Le associazioni sugli elementi figlio possono fare riferimento alle proprietà sugli oggetti matrice.

Aggiungi i collegamenti seguenti all'elenco "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>

L'elemento <li> si verifica all'interno dell'ambito dell'associazione foreach . Ciò significa che Knockout eseguirà il rendering dell'elemento una volta per ogni prodotto nella products matrice. Tutte le associazioni all'interno dell'elemento <li> fanno riferimento a tale istanza del prodotto. Ad esempio, $data.Name fa riferimento alla Name proprietà del prodotto.

Per impostare i valori degli input di testo, usare l'associazione value . I pulsanti sono associati alle funzioni nella visualizzazione modello, usando l'associazione click . L'istanza del prodotto viene passata come parametro a ogni funzione. Per altre informazioni, la documentazione diKnockout.js contiene descrizioni valide delle varie associazioni.

Aggiungere quindi un'associazione per l'evento di invio nel modulo Aggiungi prodotto:

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

Questa associazione chiama la create funzione nel modello di visualizzazione per creare un nuovo prodotto.

Di seguito è riportato il codice completo per la visualizzazione Admin:

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

Eseguire l'applicazione, accedere con l'account amministratore e fare clic sul collegamento "Amministratore". Verrà visualizzato l'elenco dei prodotti e sarà possibile creare, aggiornare o eliminare prodotti.