Compartilhar via


Parte 5: Criando uma interface do usuário dinâmica com Knockout.js

por Rick Anderson

Download do projeto concluído

Criando uma interface do usuário dinâmica com Knockout.js

Nesta seção, usaremos Knockout.js para adicionar funcionalidade ao modo de exibição administrador.

Knockout.js é uma biblioteca Javascript que facilita a associação de controles HTML aos dados. Knockout.js usa o padrão MVVM (Model-View-ViewModel).

  • O modelo é a representação do lado do servidor dos dados no domínio de negócios (em nosso caso, produtos e pedidos).
  • A view é a camada de apresentação (HTML).
  • O modelo de exibição é um objeto Javascript que contém os dados do modelo. O modelo de exibição é uma abstração de código da interface do usuário. Ele não tem conhecimento da representação HTML. Em vez disso, ele representa características abstratas da visão, como "uma lista de itens".

A exibição está associada a dados ao modelo de exibição. As atualizações para o modelo de exibição são refletidas automaticamente na exibição. O modelo de visão também obtém eventos da visualização, como cliques de botão, e executa operações no modelo, como a criação de um pedido.

Diagrama de interação entre dados H T M L, o modelo de exibição, JSON, e o controlador Web A P I.

Diagrama que mostra a interação entre os dados H T M L, o view-model, o JSON e o controlador da API Web. A caixa de dados H T M L é rotulada como view. Uma associação de dados rotulada por seta dupla vincula a caixa de dados H T M L à caixa de modelo de exibição. Uma seta dupla representando solicitações HTTP e o modelo JSON do servidor conecta o modelo de exibição ao controlador da API Web.

Primeiro, definiremos o modelo de exibição. Depois disso, associaremos a marcação HTML ao modelo de exibição.

Adicione a seguinte seção 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>
}

Você pode adicionar esta seção em qualquer lugar no arquivo. Quando o modo de exibição é renderizado, a seção aparece na parte inferior da página HTML, logo antes da marca de fechamento </body>.

Todo o script desta página entrará na tag de script indicada pelo comentário.

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

Primeiro, defina uma classe de modelo de exibição:

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

ko.observableArray é um tipo especial de objeto em Knockout, chamado de observável. Na documentação do Knockout.js: observável é um "objeto JavaScript que pode notificar os assinantes sobre as alterações". Quando o conteúdo de um observável é alterado, o modo de exibição é atualizado automaticamente para corresponder.

Para preencher a products matriz, faça uma solicitação AJAX para a API Web. Lembre-se de que armazenamos o URI base para a API no recipiente de exibição (consulte a Parte 4 do tutorial).

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

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

Em seguida, adicione funções ao modelo de exibição para criar, atualizar e excluir produtos. Essas funções enviam chamadas AJAX para a API Web e usam os resultados para atualizar o modelo de exibição.

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

Agora, a parte mais importante: quando o DOM estiver totalmente carregado, chame a função ko.applyBindings e passe uma nova instância do ProductsViewModel:

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

O método ko.applyBindings ativa o Knockout e conecta o modelo de visualização à visualização.

Agora que temos um modelo de exibição, podemos criar as associações. Em Knockout.js, você faz isso adicionando data-bind atributos a elementos HTML. Por exemplo, para vincular uma lista HTML a uma matriz, use a foreach vinculação:

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

A foreach associação itera por meio da matriz e cria elementos filho para cada objeto na matriz. As vinculações nos elementos filho podem referir-se às propriedades de uma matriz de objetos.

Adicione os seguintes vínculos à lista "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>

O <li> elemento ocorre dentro do escopo da vinculação foreach. Isso significa que o Knockout renderizará o elemento uma vez para cada produto na products matriz. Todas as associações dentro do <li> elemento referem-se a essa instância do produto. Por exemplo, $data.Name refere-se à Name propriedade no produto.

Para definir os valores das entradas de texto, use a value associação. Os botões são associados a funções no modo de exibição de modelo, usando a click associação. A instância do produto é passada como um parâmetro para cada função. Para obter mais informações, a documentação doKnockout.js tem boas descrições das várias associações.

Em seguida, adicione uma associação para o evento de envio no formulário Adicionar Produto:

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

Essa associação chama a função create no modelo de exibição para criar um novo produto.

Aqui está o código completo para o modo de exibição administrador:

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

Execute o aplicativo, faça logon com a conta administrador e clique no link "Administrador". Você deve ver a lista de produtos e ser capaz de criar, atualizar ou excluir produtos.