Trabalhando com Classes no JavaScript - Parte II (Herança)    

Olá pessoal!

Falando um pouco mais do mesmo assunto abordado no post anterior, vou tratar de mais um tema que aproxima o JavaScript de Orientação a Objetos. Veja bem, aproxima, mas ainda não o torna em uma linguagem Orientada a Objetos.

Hoje vou mostrar como podemos implementar Herança ou extensão de objeto no JavaScript. Vamos lá!

Tipo de Herança em JavaScript

No JavaScript padrão conseguimos estender classes, ou seja, herdar características de uma classe em uma outra classe de três formas:

  • Através do método apply
  • Através do método call
  • Através da propriedade prototype

Muitas bibliotecas JavaScript espalhadas pela internet criam novas formas de estender objetos, como no Jquery o método $.extend ou a própria forma de criar plugins. Como cada uma implementa de uma forma diferente, não irei mostrar esses tipos, apenas o nativos do JavaScript, listados acima, sendo estes compatíveis com qualquer browser com suporte a JavaScript.

Os métodos Apply e Call no JavaScript

Os métodos Apply e Call eu expliquei no meu último post. Eles são métodos internos do JavaScript, disponíveis em classes e métodos, Visite o post para entender melhor.

Como dito no último post, a função dele é alterar o contexto do this no JavaScript, mas dependendo da forma que você utilizá-lo, você poderá “ampliar” o contexto do this para o contexto da classe sobre a qual está se executando o apply ou o call, ou seja, realizar a herança. Veja o código abaixo:

   1: var w = function (s) { document.write(s); }
   2: var br = function () { w("<br/>") };
   3: var Aninal = function (nome) {
   4:     //Atributo
   5:     this.nome = null;
   6:     //Métodos
   7:     this.comer = function (comida) {
   8:         w(this.nome + " come " + comida);
   9:     }
  10:     //Código do meu construtor
  11:     this.nome = nome;
  12: }

Animal é minha classe base, e nela tento falar que qualquer animal (especificado pelo parâmetro nome) pode comer a comida passada por parâmetro do método comer(). Como eu faria para especializar essa classe, implementando a mesma função para homem? Vou mostrar utilizando o método apply como fazer isso:

   1: var Homem = function () {
   2:     //Herdo da classe Animal passando
   3:     //o nome para o construtor da classe base
   4:     Aninal.apply(this, ["Homem"]);
   5:     //Método exclusivo da classe Homem
   6:     this.falar = function () {
   7:         w("Olá");
   8:     }
   9: }
  10: var homem = new Homem();
  11: //O Resultado vai ser:
  12: //Homem come Arroz e Feijão
  13: homem.comer("Arroz e Feijão");

Veja que desta forma a herança já foi implementada. A classe Homem herda de Animal e utiliza o método comer sem alteração.

Eu poderia fazer as duas classes mais inteligentes. Digamos eu queira fazer o método Comer um pouco mais esperto, fazendo ele validar se o tipo de comido pode ser comido. Digamos que para Homem possa comer “Arroz e Feijão” e para Cachorro possa comer apenas “Ração”. Como você implementaria isso? Um switch poderia ser utilizado, mas teria que ter um case para cada tipo de animal, e se fosse descoberto um novo tipo de animal, você teria que alterar o switch. Isso é bem ruim! É para esse tipo de problema que utilizamos classes, abstrações e espacializações. Vamos implementar o problema acima e ver como fica com herança.

   1: var Aninal = function (nome) {
   2:     //Atributo
   3:     this.nome = null;
   4:     //Métodos
   5:     this.comer = function (comida) {
   6:         //O método podeComer será implementado apenas nas classes filhas
   7:         if (this.podeComer(comida)) {
   8:             w(this.nome + " come " + comida);
   9:         }
  10:         else {
  11:             w(this.nome + " NÃO come " + comida);
  12:         }
  13:     }
  14:     //Código do meu construtor
  15:     this.nome = nome;
  16: }
  17:  
  18:  
  19: var Homem = function () {
  20:     Aninal.apply(this, ["Homem"]);
  21:  
  22:     this.podeComer = function (comida) {
  23:         return comida.toLowerCase()=="arroz e feijão";
  24:     }
  25: }
  26:  
  27: var Cachorro= function () {
  28:     Aninal.apply(this, ["Cachorro"]);
  29:     
  30:     this.podeComer = function(comida){
  31:         return comida.toLowerCase()=="ração";
  32:     }
  33: }
  34:  
  35: var homem = new Homem();
  36: var cachorro= new Cachorro();
  37:  
  38: homem.comer("Arroz e Feijão");br();//pode
  39: homem.comer("pedra");br();//não pode
  40: cachorro.comer("Ração");br();//pode
  41: cachorro.comer("Arroz");br();//não pode

Execute o código e veja que a validação está funcionando, e está sendo delegada para a classe filha. A classe animal está como uma classe abstrata no C# e o método podeComer como um método abstrato.

Você pode perguntar: “Mas e se a classe filha não implementar esse método? Vai dar pau em tempo de execução. Sim! O que nós podemos fazer é uma validação para ver se o método existe, e se não existir, não executar a validação, ou mesmo jogar um exceção mais legivel. Veja:

   1: this.comer = function (comida) {
   2:     //O método podeComer será implementado apenas nas classes filhas
   3:     if (!this.podeComer || this.podeComer(comida)) {
   4:         w(this.nome + " come " + comida);
   5:     }
   6:     else {
   7:         w(this.nome + " NÃO come " + comida);
   8:     }
   9: }

Ou:

   1: this.comer = function (comida) {
   2:     //O método podeComer será implementado apenas nas classes filhas
   3:     if(!this.podeComer) throw "Defina o método podeComer.";
   4:     if (this.podeComer(comida)) {
   5:         w(this.nome + " come " + comida);
   6:     }
   7:     else {
   8:         w(this.nome + " NÃO come " + comida);
   9:     }
  10: }

A primeira solução eu acho mais interessante. No JavaScript, você pode verificar se um método existe, caso ele não exista, o retorno (underfined) é tratado como false em um IF. Tudo que eu fiz com o apply no exemplo, poderia ter feito com o call.

Implementando herança com Prototype

A propriedade prototype está presente em todos os objetos no JavaScript. Ela tem um comportamento totalmente diferente de qualquer outra. Com ela, você consegue estender classes e objetos concretos. É interessante que você consegue inclusive estender classes do próprio JavaScript. Para fazer uma analogia, ela funciona como os Extension Methods do C#.

Por exemplo, para definir uma variável do tipo Date, e definir uma data, eu posso fazer assim sempre que necessário:

   1: var data = new Date();
   2:     data.setDate(21);
   3:     data.setMonth(4);
   4:     data.setYear(2011);

Fazer isso todas as vezes não é legal, então eu posso estender a classe Date para ficar mais simples, posso fazer assim:

   1: Date.prototype.definir= function (data) {
   2:     var partes = data.split("/", 3);
   3:     this.setDate(parseFloat(partes[0]));
   4:     this.setMonth(parseFloat(partes[1]) - 1);
   5:     this.setFullYear(parseFloat(partes[2]));
   6: }
   7:  
   8: var data = new Date();
   9: data.definir("21/05/2011");

Veja que eu estou estendendo a classe Date, e toda variável do tipo Date agora terá o método definir. Se eu utilizasse o prototype diretamente em cima da variável, essa extensão teria efeito apenas na variável em questão, e não em todas as variáveis.

Essa é a utilização mais comum do prototype, mas nesse caso estou estendendo uma classe, e não herdando dela, e como eu faço isso? Vamos utilizar o mesmo exemplo dos Animais, e você verá como é feita a herança com prototype. Minha classe Animal é a mesma, vou mudar apenas a Homem e Cachorro, e ficará claro como utilizar o prototype.

   1: var Homem = function () {
   2:     this.podeComer = function (comida) {
   3:         return comida.toLowerCase() == "Arroz e Feijão";
   4:     }
   5: }
   6: //Informo de quem Homem herda
   7: Homem.prototype = new Aninal("Homem");
   8:  
   9: var Cachorro= function () {            
  10:     this.podeComer = function(comida){
  11:         return comida.toLowerCase()=="ração";
  12:     }
  13: }
  14: //Informo de quem Cachorro herda
  15: Cachorro.prototype = new Aninal("Cachorro");
  16:  
  17: //A utilização é a mesma
  18: var homem = new Homem();
  19: var cachorro= new Cachorro();
  20:  
  21: homem.comer("Arroz e Feijão");br();//pode
  22: homem.comer("pedra");br();//não pode
  23: cachorro.comer("Ração");br();//pode
  24: cachorro.comer("Arroz");br();//não pode

Veja que tirei o apply e a herança continuou a funcionar. Com o prototype eu apenas atribuo uma instância da classe que devo herdar e já está tudo funcionado. Simples e rápido.

Bom, por hoje é isso. Acredito que com isso você já seja capaz de entender e implementar herança em JavaScript. Caso tenha dúvidas, é só deixar um comentário.

Abraços e até o próximo.

21. maio 2011 20:45 by Frederico B. Emídio | Comments (0) | Permalink

Entendendo o ‘this’ no JavaScript    

JavaScript é uma linguagem realmente interessante, e todo mundo que queira ser desenvolvedor Web deve ter essa linguagem como pré-requisito. Mas ela tem características um tanto quanto diferentes de uma linguagem como o C#. Não entender essas características faz com que pessoas tenham enorme dificuldade para entender como usar bibliotecas JavaScript, como JQuery ou KnockoutJS. Ou faz as pessoas fazerem milhares de linhas de script para fazer uma página web um pouco mais dinâmica.

Hoje então vou falar de mais uma característica do JavaScript: o operador this.

Como em qualquer linguagem que tenha esse operador, no JavaScript o this faz referência ao contexto atual da execução. Ou seja, caso você esteja desenvolvendo uma classe, o this fará referência à classe, ou ao objeto criado a partir dessa classe.

Mas no JavaScript, o this também tem uma característica totalmente diferente. No JS a gente consegue alterar o contexto do this, ou seja, é possível a gente mudar o que o JavaScript entende como contexto atual.

Por exemplo, se você já utilizou JQuery, já reparou como o this sempre é algo diferente? Veja o exemplo abaixo:

   1: //Vai sair Window
   2: alert(this);
   3:  
   4: var dados = [{ Nome: "Nome 1", Idade: "25" },
   5:             { Nome: "Nome 2", Idade: "23"}];
   6:  
   7:  
   8: $().ready(function() {
   9:     $("#link").click(function() {
  10:         //Vai sair o ID da tag A e não do Window
  11:         alert(this.id);
  12:     });
  13:  
  14:     $(dados).each(function() {
  15:         //Nesse contexto o this vai ser cada item do array.
  16:         alert("Nome: " + this.Nome + " - Idade: " + this.Idade);
  17:  
  18:     });
  19: });

Veja que para cada situação, o Alert vai exibir uma informação, mesmo que eu não esteja trabalhando em contextos diferentes, não estou utilizando classes, por exemplo.

O que está acontecendo no caso acima é que o JQuery está alterando o contexto this, para ficar mais fácil de trabalhar. Você já deve ter percebido que se eu quiser fazer um for manual no meu array, o this não vai ser cada item do array. Ou seja, o código abaixo vai exibir duas vezes o alert Window:

   1: for (var i = 0; i &lt; dados.length; i++) {
   2:    alert(this);
   3: }

Então como eu posso alterar o meu contexto para conseguir simular o que o JQuery faz com o método $.each e demais métodos? Isso é feito através dos métodos apply ou call.

Conhecendo os métodos Apply e Call no JavaScript

Apply e Call são métodos internos do JavaScript disponíveis para qualquer método ou classe criada por você. A utilização deles é exatamente igual, mudando apenas a forma que é definido o segundo parâmetro. O apply recebe como segundo parâmetro um array de argumentos, enquanto o call recebe uma lista de parâmetros (como o ParamArray do C#).

Com esses métodos você consegue chamar métodos de uma forma diferentes, como o Invoke na classe MethodInfo do .Net. Esse método tem a capacidade de você definir qual é o contexto do this dentro do método que está sendo invocado.

A utilização de ambos se dá da seguinte forma:

   1: AlgumMetodo.call([contexto do this], param1,param2,param3);
   2: AlgumMetodo.apply([contexto do this], [param1,param2,param3]);

Perceba que a única diferença é o segundo parâmetro, que no call é uma lista de parâmetros e no apply é um único parâmetro do tipo array contendo os parâmetros no array.

Então como eu simulo o método JQuery $.each, por exemplo?

   1: var dados = [{ Nome: "Nome 1", Idade: "25" },
   2:             { Nome: "Nome 2", Idade: "23"}];
   3:  
   4:  
   5:  
   6: function exibirItens() {
   7:     alert("Nome: " + this.Nome + " - Idade: " + this.Idade);
   8: }
   9:  
  10: for (var i = 0; i < dados.length; i++) {
  11:     exibirItens.apply(dados[i]);
  12: }   

Se você testar o código acima, verá que no meu método exibirItens o this passa a ser cada item do meu array dados. Isso porque estou invocando meu método através do método apply, e estou passando no meu primeiro parâmetro o item atual do array. Não estou passando nada no segundo parâmetro porque meu método não espera parâmetros. Se eu fosse utilizar o call nada mudaria (apenas o aply pelo call, claro).

Agora que vimos como é possivel alterar o contexto do this, podemos criar métodos que recebam “delegates” (para falar na língua do .Net) como parâmetros e fazer alguns truques.

Por exemplo, baseado no código acima, vamos criar um método chamado paraCada que vai ter o mesmo comportamento do método $.each do JQuery.

   1: var dados = [{ Nome: "Nome 1", Idade: "25" },
   2:              { Nome: "Nome 2", Idade: "23"}];
   3:  
   4:  
   5: //Coleção é meu array com os dados que quero receber
   6: //Acao é o meu método que quero executar para cada item
   7: function paraCada(colecao,acao) {
   8:    for (var i = 0; i < colecao.length; i++)
   9:        acao.apply(colecao[i]);
  10: }
  11: //Aqui eu faço uso do meu método.
  12: paraCada(dados, function() {
  13:    alert("Nome: " + this.Nome + " - Idade: " + this.Idade);
  14: });

E está pronto! Um método como o $.each, Pense nas possibilidades, são muitas.

Bom, era isso! Acredito que para qualquer pessoa que quer ficar bom em JavaScript, esse conceito é muito importante.

Abraços e até o próximo!

20. maio 2011 23:43 by Frederico B. Emídio | Comments (0) | Permalink

Trabalhando com Classes no JavaScript    

Hoje vou abordar um tema que vejo que não é muito abordado em blogs de linha portuguesa e que vem se tornando cada vez mais necessário  conforme a necessidade de criar ricas interfaces na web se torna mais comum.

Antes de abordar o tema propriamente dito, acho importante dizer o que não será tratado nesse post. Caso você, caro leitor, veja a necessidade de abordar os assuntos que citarei a seguir, é só comentar.

Não vou mostrar nesse post uma introdução básica sobre JavaScript, ou seja, presumo que você já conheça a sintaxe e características básicas da linguagem, como o fato de ser uma linguagem dinâmica; não vou abordar JSON, AJAX, JQuery e outras técnicas comuns do JavaScript; não mostrarei bibliotecas que mudam a forma de criar classes, utilizarei apenas o JavaScript padrão e não vou explicar nomenclatura JavaScript, que é diferente de C#.

Dado o breve aviso, também devo informar que JavaScript não é uma linguagem 100% Orientada à Objetos (OO). Apesar de ser possível criar classes, falta algumas capacidades que tornariam possível um título de uma linguagem 100% Orientada a Objetos.

Então vamos lá!

Minha primeira Classe JavaScript

Classe, na maioria das linguagens de programação, é uma estrutura que descreve estados e comportamentos de um determinado objeto. Cada linguagem tem a sua forma de descrever essa estrutura, em geral com a palavra chave class. No JavaScript essa palavra chave não é utilizada (ainda), e nós utilizamos uma função para criar uma Classe.

Vamos ao primeiro código, a definição básica de uma Classe:

   1: var MinhaClasse= function() { 
   2:     }

Bom, até ai não temos uma classe, pois no JavaScript essa também é uma forma de declarar uma função simples. Para MinhaClasse se tornar uma classe, devemos adicionar atributos (estados) e métodos (comportamento). No JavaScript, assim como no Java, não existe o conceito de Propriedades, como no C# ou VB.Net. O encapsulamento é feito através de métodos Getters e Setters.

A classe MinhaClasse terá um atributo e um método público e um atributo e um método privado, apenas para demonstrar a diferença de visibilidade entre os membros da classe.

   1: var MinhaClasse = function() {
   2:   var propriedadePrivada = "Privado";
   3:   var metodoPrivado = function() {
   4:       w(propriedadePrivada);
   5:   }
   6:  
   7:   this.propriedadePublica = "Público";
   8:   this.metodoPublico = function() {
   9:       w(this.propriedadePublica);
  10:   }
  11: }

Como o código mostra, para fazer uma propriedade ou método privado no JavaScript você declara o método ou o atributo com a palavra chave var. Para tornar um atributo ou método público é utilizado a palavra chave this.

Agora sim MinhaClasse virou uma classe, e utilização é bem simples:

   1: var c = new MinhaClasse();

Se você utilizar o Intelisense do Visual Studio, verá que apenas os membros públicos (marcados com this) aparecerão.

image

É importante notar que o comportamento entre os membros de uma classe no JavaScript é diferente. Os métodos públicos conseguem enxergar métodos e atributos privados, porém, métodos privados não conseguem acessar métodos e atributos públicos. Portanto, o código abaixo NÃO funcionará:

   1: var metodoPrivado = function() {
   2:     w(propriedadePrivada);
   3:     br();
   4:     this.metodoPublico();
   5: }

Por outro lado, o método a seguir funcionará:

   1: this.metodoPublico = function() {
   2:    w(this.propriedadePublica);
   3:    br();
   4:    metodoPrivado();
   5: }

Se por acaso você quisesse criar classes internas, seria o mesmo procedimento de criar uma classe normal, porém seria dentro de uma outra classe já criada. A utilização é a mesma.

Agora que vimos como faz uma classe básica no JavaScript, vamos ao próximo item.

Construtores de Classe em JS

É normal em uma classe você fornecer dados para ela ser iniciada (transformada em objeto) e isso é feito através de um método que chamamos de Construtor. No JavaScript não existe um método especial para fazer isso. Mas perceba que uma classe nada mais é que um função elaborada, e quando chamamos o new de uma classe, automaticamente chamamos essa função. Portanto, para passar parâmetros á uma classe no momento da criação/instanciação, precisamos apenas definir parâmetros à função que define a classe. Veja:

   1: var MinhaClasse = function(nome) {
   2:    var propriedadePrivada = "Privado";
   3:    
   4:    //Outros membros...
   5:  
   6:    //Esse código será executado como Construtor
   7:    if (nome)
   8:        propriedadePrivada = nome;
   9:  
  10: }
  11:  
  12: //Utilizando 
  13: var c = new MinhaClasse("Fred");
  14: c.metodoPublico();

Como automaticamente todo parâmetro é opcional no JavaScript, se você não passar o parâmetro nome, ele ficará como undefined.

É importante que o código do “construtor” esteja sempre no final da Classe. Isso por que o JavaScript é uma linguagem interpretada, seu processamento é feito linha a linha, e se por acaso um código chamar um método ou variável que ainda não foi processado pelo Browser, retornará erro, informando que o método ou atributo não existe. Essa regra se aplica em qualquer realidade do JavaScript, por isso é importante sabermos qual será a ordem do processamento do Browser, para sabermos organizar nosso código.

Métodos Estáticos em JavaScript

É comum precisarmos de métodos estáticos em classes. No JavaScript isso é bem simples:

   1: var MinhaClasse = function() {
   2:   var propriedadePrivada = "Privado";
   3:   var metodoPrivado = function() {
   4:       w(propriedadePrivada);
   5:   }
   6:  
   7:   this.propriedadePublica = "Público";
   8:   this.metodoPublico = function() {
   9:       w(this.propriedadePublica);
  10:       br();
  11:       metodoPrivado();
  12:   }
  13: }
  14: //Definindo método estático
  15: MinhaClasse.metodoEstatico = function() {
  16:   w("Método Estático");
  17: }

Veja que estou apenas adicionando o método fora do “corpo” da classe.Isso torna o método estático. É quase como criar uma classe, com a diferença que você não define membros ao método, caso contrário ele transformaria em uma “sub-classe” (que também pode ser uma opção). A utilização do método estático seria assim:

   1: MinhaClasse.metodoEstatico();

Naturalmente, métodos estáticos só acessam métodos e atributos estáticos, sempre necessitando colocar o nome do classe (como acima). Caso queira acessar um método privado ou público, terá que criar uma instância da Classe.

Namespace

É comum, quando criamos muitas classes, querermos organiza-las em grupos (namespace em .Net e packages em Java). Em JavaScript isso também é possível, e de uma forma bem simples:

   1: //Criação do Namespace
   2: var classes = {}
   3:  
   4: //Criação da classe no Namespace
   5: classes.MinhaClasse = function() {
   6:     //Corpo da classe…

A única diferença para utilizar essa classe agora, é que no momento de criar a instância, devemos informar o Namespace:

   1: var c = new classes.MinhaClasse();
   2:     c.metodoPublico();

Conclusão e Dicas

E é isso! Muito simples criar classes em JavaScript, é muito importante, porque ajuda o seu código a ficar muito mais organizados.

Você pode ter reparado que utilizei dois métodos durante a classe que não são nativos e não mostrei antes, o w() e br(). Isso são apenas atalhos que criei para digitar menos e os códigos não ficarem tão carregados, mas eles são simples linhas como as abaixo:

   1: var w = function (a) { document.write(a) };
   2: var br = function () { w("<br/>") }

Perceba que em JavaScript podemos utilizar variáveis que fazem referências à métodos (como Delegates em .Net). Sabendo disso, ai vai uma dica ao criar classes:

Crie todos os métodos como privado (var), assim você não terá problemas com métodos da sua classe não enxergando métodos da própria classe. Feito isso, deixe público apenas os métodos que interessar, criando uma variável pública (this) com o mesmo nome, referenciando o método privado.

Como exemplo, vou reescrever a classe utilizada acima, reorganizando o código  para mostrar como fica. Abaixo o código completo para os exemplos:

   1: //Atalhos
   2: var w = function (a) { document.write(a) };
   3: var br = function () { w("<br/>") }
   4:  
   5: //Criação do Namespace
   6: var classes = {}
   7:  
   8: //Criação da classe no Namespace. Com construtor recebendo
   9: //parâmetro Nome
  10: classes.MinhaClasse = function(nome) {
  11: //Corpo da classe
  12:    
  13:    //Atributos públicos
  14:    var propriedadePrivada = "Privado";
  15:    var propriedadePublica = "Público";
  16:  
  17:    //Métodos privados
  18:    var metodoPrivado = function() {
  19:        w(propriedadePrivada);
  20:    }
  21:    
  22:    var metodoPublico = function() {
  23:        w(this.propriedadePublica);
  24:        br();
  25:        metodoPrivado();
  26:    }
  27:    
  28:    //Torno public dois membros
  29:    this.propriedadePublica = propriedadePublica;
  30:    this.metodoPublico = metodoPublico;
  31:    
  32:    //Esse código será executado como Construtor
  33:    if (nome)
  34:        propriedadePrivada = nome;
  35: }
  36:  
  37: //Definindo método estático
  38: classes.MinhaClasse.metodoEstatico = function() {
  39:    w("Método Estático");
  40: }
  41:  
  42: //Utilizo minha classe. Alterando o valor da propriedade
  43: //privada pelo construtor.
  44: var c = new classes.MinhaClasse("Fred");
  45: c.metodoPublico();
  46: c.propriedadePublica = "Publico Alterado";
  47: br();
  48: c.metodoPublico();
  49: br();
  50:  
  51: //Utilizo método estático
  52: classes.MinhaClasse.metodoEstatico();

Acredito que com esses exemplos, você conseguirá facilmente trabalhar com classes JavaScript em suas páginas, deixando seu código muito mais limpo e reaproveitável.

Abraços e até o próximo post!

13. maio 2011 00:00 by Frederico B. Emídio | Comments (3) | Permalink

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

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