MVVM e Asp.Net MVC com KnockoutJS    

Introdução

Hoje vou falar de um tema novo, porém, muito importante para quem desenvolve sistemas Web. Atualmente, os sistemas têm ficado com interfaces cada vez mais ricas e rápidas, o casamento de Asp.Net MVC (ou mesmo WebForms) com Jquery possibilitou trazer muito da experiência de usuário de sistemas Desktop para Web, e quanto mais nós desenvolvedores vemos as possibilidades mais as telas ficam cheias de “efeitos”.

Porém, conforme as telas ficam mais complexas e com mais informações, mais o código para tratar tudo isso fica confuso. É comum em grandes telas de sistemas Web, existirem arquivos JavaScript com algumas centenas de linhas, e para piorar o suporte do Visual Studio para JavaScript, por mais que tenha melhorado muito, continua ainda um pouco restrito.

Se olharmos, muito das linhas de um arquivo JavaScript tem a responsabilidade de ler e escrever em campos HTMLs, muito do arquivo é a utilização do método val() do Jquery com ou sem parâmetros, e por não ter uma bom editor acabamos muitas vezes duplicando trechos de códigos.

Muito do meu esforço nos últimos projetos em que trabalhei era diminuir o esforço para fazer esse mapeamento entre dados (modelo) e tela, tentando mesclando alguns puglins JQuery para resolver o meu problema. Nesse esforço, esbarrei no padrão MVVM, muito utilizado em aplicações Silverlight, e tentei aplicar esse padrão a minha realidade MVC/WebForms X JQuery. Algum tempo tentando “reinventar” a roda, descobri o Knockout, uma bela biblioteca JavaScript projetada para implementar o padrão MVVM em Javascript.

O objetivo deste post é tentar mostrar um pouco das facilidades que essa biblioteca nos dá ao integrar código e interface de maneira simples e limpa. Espero que goste!

O padrão MVVM (Model – View – ViewModel)

Meu conhecimento de MVVM não é amplo, então vou basear essa breve explicação no que você pode encontrar no Wikipedia ou mesmo no site do Knockout.

A definição básica é: “MVVM é um padrão arquitetural criado pela Microsoft como uma espacialização do Design Patter Presentation Model, introduzido originalmente por Martin Fowler”.

A Microsoft introduziu esse padrão com o advento do WPF e Silverlight, então desenvolvedores dessas plataformas já devem estar bem familiarizado com esse padrão.

Basicamente, MVVM é um padrão para criar UI (tela) de forma transparente sofisticada, separando esta tarefa em três partes:

  • Modelo (Model) – Assim como o M do MVC, o modelo representa os dados e operações do domínio da sua aplicação. É totalmente independente de interface e representa o negócio da sua aplicação, que vai muito além de tela. Para conseguir identificar da melhor forma possível o domínio da sua aplicação, que te leva a um bom modelo, recomendo a leitura do livro sobre DDD do Erick Evans, que pode ser algumas páginas online no google.
  • View – Sua tela, com campos, labels e botões, representando o estado da aplicação e permitindo que o usuário interaja com a aplicação, em resumo, a Interface com Usuário – UI (HTML).
  • ViewModel – Uma representação somente de código dos dados e operações de uma UI. Não é a UI de fato, porque não tem nada que lembre botões, campos de texto, etc. Armazena os dados não salvos do usuário e tem conhecimento para conversões de informação. Utilizando Knockout é puro JavaScript.

A técnica de MVVM pode ser aplicada praticamente em qualquer linguagem que tenha interface de usuário rica, com botões, campos de textos, etc. Como comentei, é muito utilizada com Silverlight e WPF, mas pode ser utilizada com WindowsForm, WebForm, VB6, etc, e obviamente não está amarrado ao Knockout.

É importante frisar também que MVVM não substitui MVC, e a View do MVC não tem a mesma função da View do MVVM, visto que no MVC, view pode ser qualquer saída da aplicação, podendo ser relatório, arquivo de texto, prompt de comando, etc; enquanto que no MVVM é sempre uma interface de usuário rica, com botões, campos de textos, combo, etc.

Em geral, em uma aplicação Asp.Net MVC que faz uso de Ajax, a View é um JSON, que muitas vezes é um objeto do próprio Model, ou um objeto ViewModel.

KnockoutJS – MVVM com Javascript

KnockoutJS (KO), como disse anteriormente, é uma biblioteca de MVVM feita em JavaScript puro, ou seja, sem dependências de outras bibliotecas JS para funcionar (na maioria dos casos). Foi criado pelo Steve Sanderson, há pouco tempo atrás e já está ganhando certa notoriedade no mundo Asp.Net, e promete ganhar muito mais (não só no Asp.Net), porque realiza muito bem a tarefa que se dedica a fazer.

Todas as informações necessárias, como documentação, exemplo, releases e download pode ser encontrado no site http://knockoutjs.com/

Os conceitos básicos do KO são:

  • Bindings declarativos;
  • Atualização de UI automática (Observables) e Controle de Dependência;
  • Template (precisa de JQuery);

Neste post, tentarei abordar cada um dele, apenas para se ter uma noção da capacidade dessa biblioteca. Para não me alongar muito, mostrarei apenas um exemplo de cada um. Muitos outros podem ser encontrado no site do KO.

Exemplo 1: Binding declarativo.

Obs.: Para esse exemplo funcionar, é necessário baixar o arquivo do KO, a partir daqui.

O Binding no Knockout é realizado através de atributos HTML5 data-bind, e pode ser utilizado em praticamente todas as TAGs HTMLs. Como o atributo é interpretado pela biblioteca do KO, não importa se o Browser suporta ou não HTML5. Na realidade, o Knockout é suportado por todos os Browsers, inclusive mobile.

Como primeiro exemplo, vou fazer um bem básico. Na minha View Index do Controller Home, vou colocar o seguinte HTML:

   1: <p>Codigo: <span data-bind="text:Codigo"></span></p>
   2: <p><span>Nome:</span><input type="text" data-bind="value:Nome" /></p>
   3: <p><span>Email:</span><input type="text" data-bind="value:Email" /></p>

O código acima é bem simples: Estou dizendo que o atributo text do controle span, por exemplo, será preenchido com o atributo Codigo do meu Modelo. Em outros palavras, seria o mesmo que fazer assim:

   1: <p>Código: <span>1</span></p>

O JavaScript é muito simples para fazer o Knockout funcionar. Vou criar um modelo, que nada mais é que um objeto JSON, e depois invocar o método que efetiva o Bind no formulário. O Código está abaixo:

   1: var usuario = {
   2:     Codigo: 1,
   3:     Nome: "Frederico",
   4:     Email: "blog@fredericoemidio.com"
   5: }
   6:  
   7: ko.applyBindings(usuario);

Com essas poucas linhas de código o Bind já funciona, a única novidade ai é a chamada applyBindings do objeto ko. KO é o objeto de entrada a todas as funções do Knockout. Esse método tem dois overloads, este que estou usando, que a partir de um modelo de dados ele realiza o bind para toda a página onde tiver controller com o atributo data-bind e o outro é com um segundo parâmetro que recebe um container (div por exemplo), informando que o bind só deve ser realizada nos controles dentro do Container.

Com o código acima, o resultado é o seguinte:

 

image

E voilá! Funcionando! Foi um exemplo muito simples, porém, extremamente útil.

Exemplo 2: Implementando atualização automática de UI com Observables

Um dos conceitos centrais do Knockout sãos os Obervables. Observables basicamente é a forma que o Knockout utiliza para que uma atualização feito no JavaScript automaticamente reflita no HTML e vice-versa.

Vou utilizar o mesmo exemplo acima, porém, agora vou simular um botão Salvar, para percebemos como o modelo é automaticamente atualizado conforme o usuário digita nos campos da tela.

Vou alterar o meu modelo para ficar da seguinte forma:

   1: var usuario = {
   2:         Codigo: ko.observable(1),
   3:         Nome: ko.observable("Frederico"),
   4:         Email: ko.observable("blog@fredericoemidio.com"),
   5:  
   6:         Salvar: function() {
   7:         alert("Nome: " + this.Nome() + "\r\nEmail: " + this.Email());
   8:        }
   9:     }

O que eu fiz nesse código: Falei que minhas propriedades são observaveis, com o método ko.observable, veja que também adicionei um método no modelo, para simular um método Salvar.

O legal dessa abordagem é que o código JavaScript fica como um code-behind, e a gente não precisa fazer referência a controles HTMLs, e isso é incrível!

No HTML, para fazer o botão Salvar funcionar, eu também utilizo o atributo data-bind, veja, apenas utilizando o atributo click:

   1: <input type="button" value="Salvar" data-bind="click: Salvar" />

O resultado fica assim:

image

Perceba que alterei o conteúdo e ele refletiu no meus dados, sem eu precisar ficar me preocupando em como ler as informações. Como enviar para o servidor fica a seu critério. Você pode utilizar PageMethods ou métodos AJAX do JQuery.

Exemplo 4 – Mapeamento automático

Mas agora você pode falar: “Eu vou ter que ficar fazendo mapeando das minhas classes no JavaScript? Mas meu cadastro de Pessoa tem 50 campos, isso não vai dar certo!”

Realmente, e por isso o Knockout tem um plugin, que realiza esse mapeamento automaticamente. Para utiliza-lo, você terá que baixa-lo separadamente, neste site.

Para fazer esse exemplo, vou criar uma classe na pasta Models do meu site MVC:

   1: public class Pessoa
   2: {
   3:    public int Codigo { get; set; }
   4:    public string Nome { get; set; }
   5:    public string Email { get; set; }
   6: }

E vou criar uma uma Action no meu Controller para obter um registro de Pessoa do servidor:

   1: public JsonResult ObterPessoa()
   2: {
   3:     return  Json(new Pessoa { Codigo = 1, Nome = "Frederico", Email = "blog@fredericoemidio.com" }, JsonRequestBehavior.AllowGet);
   4: }

E vou alterar meu JavaScript para que ele processe o retorno e realize o bind. Meu HTML não vai mudar nada, apenas o JavaScript: Estou obtendo assincronamente os meus dados com Jquery e invocando o método fromJS do plugin ko.mapping.

   1: $.get("Home/ObterPessoa", function (retorno) {
   2:     var usuario = ko.mapping.fromJS(retorno);
   3:     usuario.Salvar = function () {
   4:         alert("Nome: " + this.Nome() + "\r\nEmail: " + this.Email());
   5:     }
   6:     ko.applyBindings(usuario);
   7: });

Com esse código o resultado é o mesmo do exemplo anterior, com a vantagem que não precisamos realizar o mapping manualmente. Existem outros métodos interessantes nesse plugin que deixarei para outros posts.

Exemplo 4 – Template

O último exemplo do post de hoje será utilizando template do JQuery, mostrando como a mescla de algumas bibliotecas pode fazer o desenvolvimento ficar muito bom. Neste caso, quem fará acesso ao JQuery template será o próprio Knockout, então você não precisa conhecer a fundo o JQuery template, caso queira, acesse o site do JQuery.

Vou criar uma listagem de pessoas e realizar o mesmo processo de binding no JavaScript:

   1: var modelo = { pessoas: [
   2:    { Codigo: 1, Nome: 'Nome 1', Email: 'nome1@email.com' },
   3:    { Codigo: 2, Nome: 'Nome 2', Email: 'nome2@email.com' },
   4:    { Codigo: 3, Nome: 'Nome 3', Email: 'nome3@email.com' }
   5:  
   6: ]
   7: }
   8:  
   9: ko.applyBindings(modelo);

O HTML que muda um pouco, utilizando o padrão do jquery template:

   1: <div data-bind='template: "pessoaTemplate"'>
   2: </div>
   3: <script id='pessoaTemplate' type='text/html'>
   4:     <table>
   5:     <thead>
   6:         <tr>
   7:             <th>
   8:                 Codigo
   9:             </th>
  10:             <th>
  11:                 Nome
  12:             </th>
  13:             <th>
  14:                 Email
  15:             </th>
  16:         </tr>
  17:     </thead>
  18:         <tbody>
  19:             {{each pessoas}}
  20:             <tr>
  21:             <td>${ Codigo }</td>
  22:             <td>${ Nome }</td>
  23:             <td>${ Email }</td>
  24:             </tr>
  25:             {{/each}}
  26:         </tbody>
  27:     </table>
  28:  
  29: </script>

Os templates do JQuery são colocados dentro de blocos Scripts com o tipo ‘text/html’. O resultado será:

image

O template gera uma linha para cada registro. Simples assim.

Para introduzir o Knockout, acredito que esses exemplos são suficientes. Acredito que vale a pena você navegar em http://knockoutjs.com/ e ver todas as possibilidades dessa biblioteca. Tem ajudado muito no meu projeto atual. Faz o seu código JavaScript ficar muito mais claro e simples.

Bom pessoa, por hoje é isso!

Abraços

Projeto de exemplo:

 

Knockout.zip (583,67 kb)

9. maio 2011 00:10 by Frederico B. Emídio | Comments (6) | Permalink

Comments

Márcio Abrantes
Muito bom o poste, está me ajudando muito nos novos conhecimentos com knockout, mas teria um duvida de como poderia retornar um coleção?
18/08/2012 00:37:14 #
Márcio, obrigado pelo Comentário.

Te mandei um e-mail sobre sua dúvida.

Abs,
Fred
20/08/2012 07:36:29 #
Fabio Junio
Prezado Frederico, parabéns pelo post, muito bom mesmo......Poderia me ajudar a gravar os dados no servidor? Tenho um formulário que possui alguns elementos observáveis e outros não. Os dados que não são observáveis, consigo enviar para meu controller sem problema nenhum, mas os observáveis não. Poderia por gentileza me ajudar?
15/02/2013 02:02:39 #
Olá Fábio, tudo bem?

Os dados que são observáveis você não consegue enviar para o servidor porque na realidade eles se tornam métodos no JavaScript, e para o servidor vai apenas os campos.
Para você enviar para o Servidor todos os campos observáveis você deve invocar o método ko.toJSON sobre o seu objeto/modelo ex: var dados = ko.toJSON(MeuModelo) e por fim enviar a variável dados ao servidor. Ela conterá os campos observáveis e os não observáveis.

Abs
Frederico
15/02/2013 03:57:35 #
Henrique Elton
Ola Frederico, primeiramente parabéns pelo artigo ajudou-me a ter uma idéia geral sobre o KO.
Achei interessante a questão do mapeamento automático da ModelView do MVC para ViewModel do Javascript, contudo tenho uma questão, se caso eu queira colocar um metodo na ViewModel do Javascript é isso possível?
22/02/2013 17:20:43 #
Olá Henrique. Obrigado pelo feedback.
Quanto a sua pergunta, sim. Todos os métodos relacionados a interface devem estar no VIewModel do JavasScript.

Você pode ver um exemplo aqui:
www.fredericoemidio.com/.../...ckoutJS-JQuery.aspx

Nesse post mostro os métodos para adicionar e excluir itens. O método está no model.

Abs,
Frederico.
22/02/2013 21:31:53 #
Comments are closed

Sobre mim

Minha Imagem

Meu nome é Frederico Batista Emídio, trabalho com desenvolvimento de sistemas profissionalmente a oito anos, porém, já faço sites pessoais a pelo menos dez anos.

Para saber mais clique aqui.

Páginas

Calendário

<<  novembro 2017  >>
seteququsedo
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

Visualizar posts em um Calendário
Sigua @fredemidio

MCP Asp.NET