Partie 5 : Création d’une interface utilisateur dynamique avec Knockout.js

par Rick Anderson

Télécharger le projet terminé

Création d’une interface utilisateur dynamique avec Knockout.js

Dans cette section, nous allons utiliser Knockout.js pour ajouter des fonctionnalités à la vue Administration.

Knockout.js est une bibliothèque Javascript qui facilite la liaison de contrôles HTML à des données. Knockout.js utilise le modèle Model-View-ViewModel (MVVM).

  • Le modèle est la représentation côté serveur des données dans le domaine métier (dans notre cas, produits et commandes).
  • La vue est la couche de présentation (HTML).
  • Le view-model est un objet Javascript qui contient les données du modèle. Le modèle d’affichage est une abstraction de code de l’interface utilisateur. Il n’a aucune connaissance de la représentation HTML. Au lieu de cela, il représente des fonctionnalités abstraites de la vue, telles que « une liste d’éléments ».

La vue est liée aux données du modèle d’affichage. Mises à jour au modèle d’affichage sont automatiquement répercutées dans la vue. Le modèle d’affichage obtient également les événements de la vue, tels que les clics de bouton, et effectue des opérations sur le modèle, telles que la création d’une commande.

Diagramme d’interaction entre les données HTML, le modèle de vue, j son et le contrôleur API Web.

Diagramme montrant l’interaction entre les données HTML, le view-model, j son et le contrôleur WEB API. La zone de données HT L est étiquetée vue. Une double flèche intitulée liaison de données lie la zone de données HT L à la zone de modèle d’affichage. Une double flèche intitulée demandes H TPC et modèle j son du serveur lie le modèle d’affichage au contrôleur API web.

Tout d’abord, nous allons définir le modèle d’affichage. Après cela, nous lierons le balisage HTML au modèle d’affichage.

Ajoutez la section Razor suivante à Administration.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>
}

Vous pouvez ajouter cette section n’importe où dans le fichier. Lorsque la vue est affichée, la section apparaît en bas de la page HTML, juste avant la balise /body> fermante<.

Tout le script de cette page passe à l’intérieur de la balise de script indiquée par le commentaire :

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

Tout d’abord, définissez une classe view-model :

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

ko.observableArray est un type spécial d’objet dans Knockout, appelé observable. À partir de la documentationKnockout.js : un observable est un « objet JavaScript qui peut informer les abonnés de modifications ». Lorsque le contenu d’une modification observable, la vue est automatiquement mise à jour pour correspondre.

Pour remplir le products tableau, effectuez une requête AJAX à l’API web. Rappelez-vous que nous avons stocké l’URI de base de l’API dans le conteneur d’affichage (voir la partie 4 du didacticiel).

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

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

Ensuite, ajoutez des fonctions au modèle d’affichage pour créer, mettre à jour et supprimer des produits. Ces fonctions envoient des appels AJAX à l’API web et utilisent les résultats pour mettre à jour le modèle d’affichage.

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

Maintenant, la partie la plus importante : lorsque le DOM est chargé, appelez la fonction ko.applyBindings et transmettez une nouvelle instance du ProductsViewModel:

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

La méthode ko.applyBindings active Knockout et relie le modèle d’affichage à la vue.

Maintenant que nous avons un modèle d’affichage, nous pouvons créer les liaisons. Dans Knockout.js, vous devez ajouter data-bind des attributs aux éléments HTML. Par exemple, pour lier une liste HTML à un tableau, utilisez la foreach liaison :

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

La foreach liaison itère dans le tableau et crée des éléments enfants pour chaque objet du tableau. Les liaisons sur les éléments enfants peuvent faire référence à des propriétés sur les objets de tableau.

Ajoutez les liaisons suivantes à la liste « 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’élément <li> se produit dans l’étendue de la liaison foreach . Cela signifie que Knockout affiche l’élément une fois pour chaque produit du products tableau. Toutes les liaisons dans l’élément <li> font référence à ce produit instance. Par exemple, $data.Name fait référence à la Name propriété sur le produit.

Pour définir les valeurs des entrées de texte, utilisez la value liaison . Les boutons sont liés à des fonctions sur la vue modèle, à l’aide de la click liaison. Le produit instance est passé en tant que paramètre à chaque fonction. Pour plus d’informations, la documentationKnockout.js contient de bonnes descriptions des différentes liaisons.

Ensuite, ajoutez une liaison pour l’événement d’envoi dans le formulaire Ajouter un produit :

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

Cette liaison appelle la create fonction sur le modèle d’affichage pour créer un produit.

Voici le code complet de la vue Administration :

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

Exécutez l’application, connectez-vous avec le compte Administrateur, puis cliquez sur le lien « Administration ». Vous devez voir la liste des produits et pouvoir créer, mettre à jour ou supprimer des produits.