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. May 2011 00:10 by Frederico B. Emídio | Comments (6) | Permalink

Realizando Autenticação (Authentication) e Autorização (Authorization) em Asp.MVC    

Olá pessoal!

Nos últimos posts falei basicamente sobre Asp.Net MVC, que, na minha opinião, é a melhor forma de desenvolver para Web em Asp.Net. Entenda que com “na minha opinião” eu quero dizer que eu me sinto mais confortável utilizando MVC, mas, de uma forma ou de outra, tudo que é feito em MVC pode ser feito em WebForm ou em qualquer outra tecnologia.

Em todos os posts abordei assuntos básicos, pensando numa linha de o que uma pessoa iniciante precisa saber para conseguir fazer uma página inteira em Asp.Net MVC. Acredito que este é o último post necessário para uma pessoa conseguir fazer um site ou um aplicativo web totalmente em Asp.Net MVC.

Naturalmente, na construção de um site, muitas outras coisas são utilizadas, mas o que foi passado aqui já é o suficiente para fazer um funcional.

Como o título do post fala, falarei da parte de segurança de um site.

Qual é a diferença entre Autenticação e Autorização?

Para falar de segurança, é importante começar por conceitos básicos. Por isso vou explicar a diferença entre as duas palavras mais utilizadas nesse assunto: Autenticação e Autorização .

Autenticação

Basicamente, autenticação é a forma do sistema saber se a pessoa é realmente quem ela diz ser. Por exemplo, para acessar um prédio é normal que o segurança peça um documento oficial com foto, para ver se você é realmente a pessoa que diz ser. Nesse processo, ele está realizando a sua autenticação, autenticando o que você diz ser, com o que o documento oficial informa. Se as duas informações baterem, você está autenticado.

Autorização

Tomando como ponto de partida o exemplo anterior, o fato de você realmente ser quem você diz que é, não te garante acesso ao prédio. Imaginemos que você esteja querendo entrar na sede da Microsoft, o segurança já sabe quem você é, e te pergunta: “Com que você gostaria de falar?”, e você: “Steve Ballmer “, o segurança vai responder para você, depois de fazer uma ligação: “Desculpe, mas o sr. Steve não está disponível”, isso se ele for educado, mas na prática ele está te falando: “Você não tem autorização para falar com o chefão”. Autorização é basicamente isso: Depois de saber quem você é (autenticação), saber se você pode fazer o que está querendo fazer (autorização).

Como isso é feito em Asp.Net MVC

Para quem já programa em WebForm, ai vai a boa notícia: Praticamente nada muda!

Em um dos post introdutórios ao Asp.Net, utilizei a figura abaixo para exemplificar como é organizado o Asp.Net:

A parte de autenticação em Asp.Net MVC não muda muito do WebForm porque toda a estrutura de segurança do Asp.Net está no nível Asp.Net da figura, portanto, todas as tecnologias “finais” da plataforma compartilham a mesma infraestrutura de segurança.

No Asp.Net, existem duas formas de realizar a autenticação/autorização:

Windows Authentication: É a forma de utilizar a autenticação do Windows integrado com o IIS. As credenciais utilizadas são as mesmas credenciais do Active Directory (AD) do servidor Web, que utiliza o protocolo LDAP. Caso o site/sistema esteja sendo acessado de uma intranet, o usuário pode já entrar no site autenticado, de acordo com o usuário logado na máquina do cliente. Caso seja um acesso feito da internet, onde o usuário logado na máquina do cliente não está cadastrado no mesmo AD configurado no servidor, então o browser geralmente exibe uma janela popup solicitando o usuário, senha e domínio no momento em que o usuário acessa o site, para o acesso ser autenticado diretamente do AD do servidor, permitindo assim que apenas usuário autenticados consigam acessar o site.

Forms Authentication: Com Forms Authentication, a responsabilidade de identificar quem está acessando o site fica a cargo do próprio site. Nesse processo você configura uma página responsável por obter as credenciais do acesso (Usuário/Senha). Onde o usuário e senha serão validados fica por responsabilidade do desenvolvedor, pode ser feito em qualquer lugar, como Banco de Dados, arquivos, ou mesmo no AD do servidor.

Nos exemplo vou utilizar Forms Autentication, por ser mais comum de ser utilizado. Além do que eu vou mostrar, existem muitas formas estender ou customizar a arquitetura de segurança do Asp.Net, mas isso é assunto para um outro post, nesse mostraremos apenas como configurar a autenticação.

Configurando Forms Autentication em Asp.Net MVC

Tudo que vou mostrar aqui será na visão MVC, para quem conhece o processo para WebForm verá que o que muda, é que você troca a página do WebForm (aspx) por uma Action ou um Controller, se você deseja que a Action Index seja o foco da autenticação.

Para fazer os exemplos, vou utilizar um novo site padrão do Asp.Net MVC 2, e vou forçar que seja feito o login para acessar a página inicial. O Web.Config para configurar o Forms Autentication fica da seguinte forma:

<authentication mode="Forms">
      <forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>

Nessas linhas, adicionado dentro de system.web, que já estão no projeto padrão, estamos informando no atributo loginUrl que a Action responsável por renderizar a View de Login é a Account/LogOn, isso quer dizer que sempre que for necessário que o usuário esteja logado para acessar uma determinada Action, o browser será direcionado para esta Action. Também está definido quanto tempo em minutos que o login ficará ativo se o usuário não tiver nenhuma ação no site, no atributo timeout.

Mas você vai reparar que mesmo com essas configurações, o site não estará exigindo a autenticação. Isso acontece porque você não está definindo as regras de autorização. Ou seja, se nada está definido para autorização, qualquer usuário está autorizado a fazer tudo, logo não precisa estar autenticado.

Vamos então definir as regras de autorização, veja que nada muda até agora do que você já conhecia no WebForms, exceto as trocas de páginas por Actions.

Também no system.web, vamos falar que apenas usuário reconhecidos, ou seja, autenticados, podem acessar o site. Para isso, adicionamos as seguintes linhas ao config:

<authorization>
     <deny users="?"/>
     <allow users="*"/>
</authorization>

Veja que as tags são bem intuitivas. Dentro da tag autorização, eu informo o que eu quero proibir (deny) e o que eu quero permitir (allow). Por sua vez, dentro dessas duas tags (deny e allow), é comum usarmos dois atributos: users, onde coloco os nomes dos usuário que quero permitir ou proibir, e roles, onde defini os grupos de usuários que quero permitir ou proibir.

Na configuração que coloquei acima, estou proibindo todos os usuários anônimos (?), ou seja, não autenticados, e estou permitindo todos os usuários conhecidos (*), ou seja, autenticados.

Caso não queira ser tão genérico, autorizando todo mundo, você pode usar o nome de um usuário ou grupo, por exemplo:

<authorization>
      <deny users="?"/>
      <allow users="fred" roles="admin"/>
</authorization>

Caso você queira adicionar mais de um grupo ou usuário, é só separar por vírgula:

<authorization>
      <deny users="?"/>
      <allow users="fred" roles="admin,users"/>
</authorization>

De qualquer forma, após inserirmos as linhas de autorização, nenhuma página será acessível sem login e o site será redirecionado automaticamente para a página configurada na seção Authentication do web.config. Porém, se você realizar um teste, verá que ainda não funcionará, pois a tela estará toda torta, como a imagem abaixo:

image

Isso acontece porque a segurança do Asp.Net não é apenas para arquivos HTMLs ou Actions, mas para todo o conteúdo do site, incluindo imagens, scripts, css, pastas e sub-pastas, portanto, quando você proíbe o acesso de um usuário ele também não conseguirá acessar as imagens, scripts, etc.

Para que a exibição seja correta, você tem que criar exceções, falando que alguns arquivos ou pastas podem ser acessados mesmo quando o usuário for anônimo, isso é feito adicionando as seguintes linhas ao config,

<location path="Content">
    <system.web>
      <authorization>
        <allow users="?"/>
      </authorization>
    </system.web>
  </location>
  <location path="Views/Shared">
    <system.web>
      <authorization>
        <allow users="?"/>
      </authorization>
    </system.web>
  </location>
  <location path="Scripts">
    <system.web>
      <authorization>
        <allow users="?"/>
      </authorization>
    </system.web>
</location>

Location são colocadas diretamente abaixo da tag Configuration no Web.Config, e servem para configurações específicas para determinadas pastas, arquivos ou Actions. No nosso caso estamos autorizando que usuários anônimos tenham acesso as pastas Content, Shared e Scripts, e se acessarmos o site agora o layout estaria correto:

image

 

Assim já temos uma autorização funcionando. Como disse anteriormente, como será realizada a autenticação fica a cargo do desenvolvedor, o Asp.Net já fornece algumas possiblidades, que você pode ver no projeto padrão que vem com o VisualStudio.

Definindo autorização por Action

Acredito que é necessário apenas falar agora de como restringir acesso para cada Action. No MVC isso é muito fácil, podemos utilizar o atributo Authorize sobre a Action específica, podendo passar na propriedade User ou Roles o usuário específico ou grupo específico.

Por exemplo, digamos que eu queira que apenas pessoas do grupo Admin possam criar um novo usuário, poderíamos decorar a Action Create da seguinte forma:

   1:  // **************************************
   2:  // URL: /Account/Register
   3:  // **************************************
   4:  [Authorize(Roles="admin")]
   5:  public ActionResult Register()
   6:  {
   7:      ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
   8:      return View();
   9:  }
  10:   
  11:  [HttpPost,Authorize(Roles="admin")]
  12:  public ActionResult Register(RegisterModel model)
  13:  {
  14:      if (ModelState.IsValid)
  15:      {
  16:      // Attempt to register the user
  17:      MembershipCreateStatus createStatus = MembershipService.CreateUser(model.UserName, model.Password, model.Email);
  18:   
  19:      if (createStatus == MembershipCreateStatus.Success)
  20:      {
  21:          FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
  22:          return RedirectToAction("Index", "Home");
  23:      }
  24:      else
  25:      {
  26:          ModelState.AddModelError("", AccountValidation.ErrorCodeToString(createStatus));
  27:      }
  28:      }
  29:   
  30:      // If we got this far, something failed, redisplay form
  31:      ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
  32:      return View(model);
  33:  }

Perceba que adicionei o atributo Authorize as Actions e assim o Asp.Net verificará automaticamente se o usuário logado pertence ao grupo informado.

É importante ressaltar que tudo fica automático porque estamos utilizando o Membership Provider do Asp.Net, que já sabe como fazer para verificar se um usuário faz parte ou não de um grupo. Caso você não utilize o AspNetSqlMembershipProvider, isso não vai ficar tão automático assim, pois você terá que criar um Membership Provider, que também não é nada complicado, e é assunto para um próximo post.

Utilizei o AspNetSqlMembershipProvider porque quando criamos um novo projeto em Asp.Net MVC ele automaticamente utiliza esse provider, é muito fácil de utilizar e eu aconselho a utilização dele em qualquer projeto que não tenham regras muito específicas de segurança..

Bom pessoal, é isso! Acredito que com isso você já consiga fazer um site completo em MVC sem muita dificuldade.

Qualquer dúvida é só postar ai nos comentários, não vou postar um arquivo para download porque é exatamente o projeto padrão, com as alterações mostradas nos exemplos.

Até o próximo!

31. March 2011 07:03 by Frederico B. Emídio | Comments (4) | Permalink

Como utilizar validação com Asp.Net MVC    

Olá pessoal!

Hoje vou falar de uma tarefa importante no desenvolvimento de sites: Validação.

Na construção de site nós nunca podemos confiar nas informações que o usuário informa, devemos saber que qualquer pessoa pode errar uma digitação ou esquecer de digitar uma informação qualquer, e por isso é importante saber utilizar Validação nos formulários que criamos.

No Asp.Net MVC existem várias formas de realizar validações, mas hoje vou falar de apenas três:

  • Adicionando informações à propriedade ModelState
  • Utilizando DataAnnotations
  • Utilizando DataAnnotations com JQuery

O processo de validação no Asp.Net MVC ficou muito fácil, eu diria que ficou muito mais fácil que no Asp.Net WebForm, vamos então começar pelo básico:

Validando suas Views com ModelState

A validação no Asp.Net MVC sempre é feita através do estado do modelo (ModelState) recebido na Action de um Controller, o que muda é como esse estado é alterado, pois pode feito manualmente, como faremos agora, ou automaticamente, através de ActionFilter customizado ou automático.

A forma básica é você adicionar manualmente informações de erro no modelo ao ModelState, invocando o método AddModelError(chave,mensagemDeErro).

Vamos aos exemplos! Para começar vou criar meu modelo, bem simples, como em todos os meus outros exemplos: Pessoa

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

Naturalmente, para criar uma pessoa no banco de dados, algumas informações devem ser obrigatórias e outras devem seguir algum formato específico, para todos os nossos exemplos usaremos as mesmas validações:

  • O nome é obrigatório.
  • O e-mail é obrigatório.
  • O telefone é obrigatório.
  • O e-mail deve seguir o formatado texto@texto.texto
  • A idade é deve ser maior que 18.
  • O telefone deve seguir o formato ####-####

Vou criar duas Actions de nome Edicao, uma para renderizar a View inicial e outra para persistir os dados, essa segunda terá a responsabilidade de fazer a validação do Modelo, e adicionar os erros ao ModelState. Caso não tenha erro, apenas redirecionará o usuário para a página inicial (persistir em uma base de dados não vem ao caso).

Abaixo segue o código das Actions:

   1:  public ActionResult Edicao()
   2:  {
   3:      return View();
   4:  }
   5:   
   6:  [AcceptVerbs(HttpVerbs.Post)]
   7:  public ActionResult Edicao(Pessoa pessoa)
   8:  {
   9:      //Valida os dados no lado do servidor.
  10:      if(string.IsNullOrEmpty(pessoa.Nome))
  11:      ModelState.AddModelError("Nome","Nome é obrigatório");
  12:      
  13:      if (string.IsNullOrEmpty(pessoa.Email))
  14:      ModelState.AddModelError("Email", "Email é obrigatório");
  15:      else{
  16:      if (!Regex.Match(pessoa.Email,@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$").Success)
  17:      ModelState.AddModelError("Email", "E-mail inválido.");
  18:      }
  19:   
  20:      if (pessoa.Idade <= 18)
  21:      ModelState.AddModelError("Idade", "Idade deve ser maior que 18.");
  22:   
  23:      if (string.IsNullOrEmpty(pessoa.Telefone))
  24:      ModelState.AddModelError("Telefone", "Telefone é obrigatório.");
  25:      else
  26:      {
  27:      if (!Regex.Match(pessoa.Telefone, @"^\d{4}[-]{1}\d{4}$").Success)
  28:          ModelState.AddModelError("Telefone", "Preencha um telefone no formato ####-####");
  29:   
  30:      }
  31:      //Verifica se algum erro acima falhou.
  32:      if (!ModelState.IsValid)
  33:      {
  34:      return View();
  35:      }
  36:      else
  37:      {
  38:      //Se estive certo só redireciono para a página inicial.
  39:      return Redirect("/");
  40:      }   
  41:  }

O código é bem intuitivo, caso tenha adicionado alguma mensagem de erro, a propriedade IsValid do ModelState retornará false, e o modelo estará inválido, assim eu retorno minha mesma View, que exibirá as mensagens de erros. Minha View fica da seguinte forma:

   1:  <%using(Html.BeginForm()){ %>
   2:  <p>
   3:  <%=Html.LabelFor(Model=>Model.Nome)%>
   4:  <%=Html.TextBoxFor(Model=>Model.Nome)%>
   5:  <%=Html.ValidationMessage("Nome")%>
   6:  </p>
   7:  <p>
   8:  <%=Html.LabelFor(Model=>Model.Email)%>
   9:  <%=Html.TextBoxFor(Model=>Model.Email)%>
  10:  <%=Html.ValidationMessage("Email")%>
  11:  </p>
  12:  <p>
  13:  <%=Html.LabelFor(Model=>Model.Telefone)%>
  14:  <%=Html.TextBoxFor(Model=>Model.Telefone)%>
  15:  <%=Html.ValidationMessage("Telefone")%>
  16:  </p>
  17:  <p>
  18:  <%=Html.LabelFor(Model=>Model.Idade)%>
  19:  <%=Html.TextBoxFor(Model=>Model.Idade)%>
  20:  <%=Html.ValidationMessage("Idade")%>
  21:  </p>
  22:  <p>
  23:  <input type="submit" value="Salvar"/>
  24:  </p>
  25:  <%} %>

Veja que a única novidade da minha View a utilização do HTMLHelper ValidationMessage, passando o nome da chave que eu valido no servidor. Ao executar minha tela, caso passe informações inválidas, terei o seguinte resultado:

image

Ou assim:

image

E a sua validação está pronta para ser usada! Eu poderia utilizar um ValidationSummary, apenas utilizando o helper para isso, da seguinte forma: <%=Html.ValidationSummary()%>.

Vamos à nossa segunda forma de validar nosso modelo.

Validando suas Views com DataAnnotarions

DataAnnotations é uma forma de você adicionar validações ao seu modelo através de atributos, incorporada ao .Net 4. O interessante dessa estratégia é que não está ligada ao Asp.Net MVC, portanto, você pode utilizá-la tanto em Asp.Net, como em qualquer outra tecnologia .Net, como WCF. Isso é muito importante, pois você não precisa repetir validação em diversas partes do código, técnica conhecida como DRY (Don’t Repeat Yourself).

Como a validação é centralizada no modelo, nosso modelo sofrerá uma pequena alteração, assim como nossa Action, pois tiraremos a validação da Action e passaremos para o modelo. Nosso modelo ficará assim:

   1:  public class Pessoa
   2:  {
   3:      [Required(ErrorMessage = "O Nome é obrigatório.")]
   4:      public string Nome { get; set; }
   5:   
   6:      [Required(ErrorMessage = "O E-mail é obrigatório.")]
   7:      [RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "E-mail inválido.")]
   8:      public string Email { get; set; }
   9:   
  10:      [Required(ErrorMessage = "O Telefone é obrigatório.")]
  11:      [RegularExpression(@"^\d{4}[-]{1}\d{4}$", ErrorMessage = "Telefone inválido. Informe ####-####.")]
  12:      public string Telefone { get; set; }
  13:   
  14:      [Range(18, Int16.MaxValue, ErrorMessage = "A idade deve ser maior que 18 anos.")]
  15:      public int Idade { get; set; }
  16:  }

Veja que existem vários tipos de atributos de Validação, e para todos eu posso informar uma mensagem de erro personalizada, através da propriedade ErrorMessage, além de cada um ter propriedades específicas de acordo com o tipo.

Como toda a lógica de validação passou para o modelo, nossa Action ficará bem mais simples:

   1:  [AcceptVerbs(HttpVerbs.Post)]
   2:  public ActionResult Edicao(Pessoa pessoa)
   3:  {
   4:      //O modelo já chega validado
   5:      if (!ModelState.IsValid)
   6:      {
   7:      return View();
   8:      }
   9:      else
  10:      {
  11:      //Se estive certo só redireciono para a página inicial.
  12:      return Redirect("/");
  13:      }
  14:  }

Veja como ficou simples a Action agora, e o resultado nas Views é o mesmo, não mudei nada no meu HTML, apenas adicionei um Summary, para ver que o resultado é o mesmo:

image

E mais uma forma de validação já está funcionando e pronta para publicar!

Mas espera aí! Existe um problema nessas duas formas de validação: Ela são realizadas no servidor. Ou seja, o usuário precisa ser submetido ao servidor para realizar a validação, o que pode prejudicar muito a experiência do usuário, e para isso termos a terceira forma de validação de hoje:

Utilizando DataAnnotations com JQuery para validação no Cliente

O mais interessante da utilização do JQuery com DataAnnotation é o impacto no código do servidor: Nada! Isso mesmo, não tem impacto nenhum no servidor para você adicionar validação no Asp.Net MVC no cliente. Vou fazer a seguinte alteração no HTML:

 

   1:  <% Html.EnableClientValidation();%>
   2:  <%using (Html.BeginForm())
   3:  { %>
   4:  <p>
   5:  <%=Html.LabelFor(Model=>Model.Nome)%>
   6:  <%=Html.TextBoxFor(Model=>Model.Nome)%>
   7:  <%=Html.ValidationMessageFor(Model => Model.Nome)%>
   8:  </p>
   9:  <p>
  10:  <%=Html.LabelFor(Model=>Model.Email)%>
  11:  <%=Html.TextBoxFor(Model=>Model.Email)%>
  12:  <%=Html.ValidationMessageFor(Model => Model.Email)%>
  13:  </p>
  14:  <p>
  15:  <%=Html.LabelFor(Model=>Model.Telefone)%>
  16:  <%=Html.TextBoxFor(Model=>Model.Telefone)%>
  17:  <%=Html.ValidationMessageFor(Model => Model.Telefone)%>
  18:  </p>
  19:  <p>
  20:  <%=Html.LabelFor(Model=>Model.Idade)%>
  21:  <%=Html.TextBoxFor(Model=>Model.Idade)%>
  22:  <%=Html.ValidationMessageFor(Model => Model.Idade)%>
  23:  </p>
  24:  <p>
  25:  <input type="submit" value="Salvar" />
  26:  </p>
  27:  <%} %>

As alterações na view foram pequenas, adicionei o Helper EnableClientValidation e mudei ValidationMessage por ValidationMessageFor, fora isso, foi necessário adicionar referência a alguns arquivos JS (JavaScript), você pode adicionar tanto no seu arquivo de Layout  (Master) ou na própria View. Os arquivos são os seguintes:

 

   1:  <script src="<%=Url.Content("~/Scripts/jquery-1.4.1.min.js")%>" type="text/javascript"></script>
   2:  <script src="<%=Url.Content("~/Scripts/jquery.validate.js")%>" type="text/javascript"></script>
   3:  <script src="<%=Url.Content("~/Scripts/MicrosoftMvcJQueryValidation.js")%>" type="text/javascript"></script>
 

Os arquivos que adicionei são os arquivo do próprio JQuery (a versão desatualizada que vem com o Visual Studio 2010, que você pode mudar para a atual), o plug-in Validate do Jquery e um arquivo fornecido pelo time do Asp.Net para fazer o meio campo entre a validação do Asp.Net com o JQuery de forma totalmente transparente. Esse arquivo pode ser baixado com o Asp.Net MVC Futures, clicando aqui.

O Resultado é o mesmo:

image

E assim sua validação já estará funcionando no cliente. O mais legal dessa abordagem é que você realizará a validação tanto no Cliente como no Servidor, sem repetir uma linha de código (DRY).

No servidor que eu digo não é como no WebForm, onde a validação de um Validator Control também funciona tanto no servidor como no cliente, porque naquele caso, é apenas no servidor Asp.Net, visto que a validação está na página. Nesse caso, a validação está no Modelo, logo, o mesmo código pode ser utilizado em uma classe de negócio, WCF, WWF, etc; sem replicação de código.

Existe uma validação nativa no lado do Cliente no MVC, mas preferi mostrar com o Jquery, porque é possível integrar outros plug-ins com a validação, que podem gerar efeitos bem interessantes à sua página.

Bom, acredito que seja o suficiente para você adicionar validação ao sua página, de forma bem clara sem ter que se esforçar muito.

Abaixo você pode baixar uma solução contendo três projetos, um com cada tipo de validação, para você ver que não existe grandes diferenças entra elas.

Abraços e até o próximo pessoal.

ValidatorsMVC.zip (1,00 mb)

27. February 2011 06:06 by Frederico B. Emídio | Comments (0) | Permalink

Reaproveitando View em Asp.Net MVC    

Olá pessoal!

Hoje vou fazer um post bem pequeno, mas que é uma resposta a uma pergunta deve surgir na cabeça de todo mundo que começa em MVC. Já tive essa dúvida no passado, já ouvi essa pergunta em WebCasts e me fizeram ela semana passada: Para cada Action eu devo ter uma View, mesmo que o formulário seja igual?

Mesmo no meu post de CRUD eu fiz assim, criei uma View para a Action Create e outra para a Action Edit, mas a idéia era realmente possibilitar a evolução do projeto, como fiz aqui e aqui, e agora nesse atual vou continuar aprimorando aquele projeto.

Nesse post vou mostrar que isso não é necessário, e em uma aplicação de verdade deve ser evitado, afinal programar repetidas vezes as mesmas regras de validação sempre é um risco para a manutenção.

Vamos ao código!

Primeiramente, excluí as Action Create, e sua respectiva View, para ser utilizada apenas a Action e View Edit. Vou inicialmente mostrar como ficou o código da View. Como agora não necessariamente ao carregar a View eu terei os dados para preencher os campos, devo fazer um preenchimento condicional, da seguinte forma:

<form name="formCadastro" action="Edit" method="post">
<input type="hidden" id="hdfCodigo" name="id" value="<%=ViewData.Model.Codigo??"" %>" />
<h2>Alterar Contato</h2>
<p>
<span>Nome</span><input type="text" value="<%=ViewData.Model.Nome??"" %>" name="txtNome"
    id="txtNome" /></p>
<p>
<span>E-mail</span><input type="text" value="<%=ViewData.Model.Email??""%>" name="txtEmail"
    id="txtEmail" /></p>
<p>
<span>Telefone</span><input type="text" id="txtTelefone" value="<%=ViewData.Model.Telefone??"" %>"
    name="txtTelefone" /></p>
<p>
<input type="submit" value="Salvar" /></p>
</form>

Perceba que estou utilizando o operador ??, que verificar se o valor à esquerda é nulo e se for, utiliza o da direita. Na View, toda a alteração necessária é essa.

Nas minhas Actions fiz as seguintes mudanças:

   1:  public ActionResult Edit(int? id)
   2:  {
   3:      if (!id.HasValue) return View(new Pessoa());
   4:   
   5:      using (var bd = new bdEntities())
   6:      {
   7:      var pessoa = (from p in bd.Pessoa
   8:                where p.Codigo == id
   9:                select p).First();
  10:   
  11:      return View(pessoa);
  12:      }
  13:  }
  14:   
  15:  [HttpPost]
  16:  public ActionResult Edit(int id, FormCollection collection)
  17:  {
  18:      try
  19:      {
  20:      using (var bd = new bdEntities())
  21:      {
  22:          if (id ==0)
  23:          {
  24:          var pessoa = new Pessoa();
  25:          pessoa.Email = collection["txtEmail"];
  26:          pessoa.Nome = collection["txtNome"];
  27:          pessoa.Telefone = collection["txtTelefone"];
  28:          bd.AddToPessoa(pessoa);
  29:          bd.SaveChanges();
  30:          }
  31:          else
  32:          {
  33:          var pessoa = (from p in bd.Pessoa
  34:                    where p.Codigo == id
  35:                    select p).First();
  36:   
  37:          pessoa.Email = collection["txtEmail"];
  38:          pessoa.Nome = collection["txtNome"];
  39:          pessoa.Telefone = collection["txtTelefone"];
  40:          bd.SaveChanges();
  41:          }
  42:      }
  43:   
  44:      return RedirectToAction("Index");
  45:      }
  46:      catch
  47:      {
  48:      return View();
  49:      }
  50:  }

Na linha 1, mudei o tipo do parâmetro para Nullable<Int>, isso porque quando eu for criar um novo registro, não vou ter o ID ainda. Na linha 3 eu verifico se o ID está preenchido, se não estiver, considero que é um novo registro e retorna a View com a instância de Pessoa sem as informações preenchidas. Caso tenha valor preenchido, continuo o método como era antes, para ter o comportamento de alteração.

No segundo método, verifico se o ID é um ID novo (valor 0) ou se é uma alteração, e realizo o procedimento adequado, com isso eu utilizo a mesma view tanto para as ações de Criar como Editar, e dessa forma, não replico código de interface (View).

Em alguns casos, em vez de eu criar a lógica na Action dessa forma, eu poderia criar duas Actions com nomes distintos (Create e Edit) e na Create, utilizar o método RedirectToAction, passando como parâmetro o nome do método Edit, são apenas abordagens diferentes, mas que teriam o mesmo resultado. Naturalmente, o método RedirectToAction pode ser utilizados em outros contextos, como eu estou fazendo na linha 44, para redirecionar o navegador para a Action Index, e exibir a listagem atualizada.

Bom, como eu disse o post seria curto, e era isso que tinha para falar.

Clique no link abaixo para baixar o projeto atualizado, inclusive corrigindo um bug do arquivo anterior Smiley de boca aberta.

MvcCrud.zip (641,09 kb)

Até o próximo!



  
     
  
25. January 2011 11:23 by Frederico B. Emídio | Comments (0) | Permalink

Tarefas Básicas com Asp.Net MVC Pt. III–Grid com plugin JQuery JQGrid    

 

Olá Pessoal!

Depois de um longo tempo sem escrever, devido a um projeto atrasado, vou finalizar a série sobre tarefas básicas com Asp.Net MVC.

Você viu neste e neste post como criar um cadastro em MVC. Porém, eu criei um grid muito simples, apenas usando as simples tags TABLE do HTML. Quando um iniciante em MVC vê isso, ele já se pergunta: “Mas e todas aquelas facilidades do GridView? Vou ter que criar na mão?”. A resposta é não. Existem muitas grids por ai que funcionam muito bem com Asp.Net MVC, e hoje vou mostrar como utilizar a que eu mais gosto, inclusive em WebForms (exatamente, eu não uso GridView a muito tempo).

A Grid que vou apresentar é um plugin Jquery muito bem feito, chamado JQGrid. Esse plugin tem constantes atualizações, com melhorias e correções, além de ter muitas referências na Web. O próprio site do plugin tem uma parte Wiki com todo o conteúdo necessário para você fazer tudo o que for necessário em um sistema, desde a simples grid com paginação até a SubGrid, Agrupamento, Edição inline, edição em popup,  entre muitas outras coisa, tudo em poucas linhas de códigos. As funcionalidades são separadas em módulos, e você pode baixá-los separadamente. Dê uma olhada aqui que você verá quantas possibilidades existem nesta grid. Além disso, o layout da grid é baseado nos temas do JQuery UI, ou seja, criar temas para grid é muito fácil a partir do site do JQuery UI

O objetivo deste post não é explicar amplamente o plugin, apenas mostrar como pode ser feita a integração com uma Action, a partir daí, muito mais pode se feito.

Então vamos lá!

Instalando o Plugin JQGrid

Para baixar a última versão, você pode acessar aqui. Nessa tela, você poderá selecionar quais módulos da grid deseja baixar, no meu caso eu vou selecionar todos, mas você pode ver com mais atenção quais são realmente necessários para um projeto real. Quanto mais módulos, maior o arquivo, e na internet isso ainda faz uma diferença.

Quando baixamos o Plugin, temos uma pasta src, que você pode estudar para ver como a Grid funciona, mas para adicionar em nosso site, precisamos apenas adicionar o arquivo jquery.jqGrid.min.js, que conterá tudo necessário para a Grid rodar. Além desse arquivo, existe também uma pasta chamada i18n, onde você encontrará arquivos de linguagens, adicione a que você achar melhor, eu vou adicionar apenas o arquivo de Português do Brasil.

Adicionado os arquivos de Scripts, você deve também adicionar os arquivos de layout (CSS). Dois arquivos devem ser adicionados, o CSS da Jquery UI, que pode ser baixado aqui, e o arquivo que complementa o CSS do UI, que vem com o download do JQGrid, chamado ui.jqgrid.css

Meu projeto (o mesmo do artigo anterior) ficará assim:

image

image

Após adicionarmos os arquivos ao nosso projeto, devemos adicionar a referência no HTML, tanto para os JS’s como para os CSS’s. Vou fazer isso na View Index, onde tenho a tabela HTML:

<link rel="Stylesheet" href="../../Content/jquery-ui-1.7.2.custom.css" />
<link rel="Stylesheet" href="../../Content/ui.jqgrid.css" />
<script type="text/javascript" src="../../Scripts/jquery-1.4.1.js"></script>
<script type="text/javascript" src="../../Scripts/i18n/grid.locale-pt-br.js"></script>

Como todo plugin Jquery, a configuração da Grid é feito pelo seu construtor, em cima de alguma tag do HTML selecionado pelo seletor do JQuery. Para criar a Grid, utilizei o seguinte código, em cima da table com id=table:

<script type="text/javascript">
$().ready(function () {
    $("#table").jqGrid({
    multiselect: false,
    datatype: "json",
    url: "Listar",
    colNames: ['Nome', 'Email', 'Telefone'],
    colModel: [
    { name: 'Nome', index: 'Nome', width: 100 },
    { name: 'Email', index: 'Email', width: 300 },
    { name: 'Telefone', index: 'Telefone', width: 80 }
     ],
    height: 220,
    caption: "Resultado",
    pager: "#pager",
    jsonReader: { repeatitems: false, id: "Codigo" },
    rowNum: 10,
    sortname: 'NomeEmpresa',
    sortorder: "asc",
    viewrecords: true
    });
 
});
        
</script>
<table id="table"></table>
<div id="pager"></div>

Vou explicar resumidamente alguns parâmetros, mas se você quiser uma explicação mais detalhada de cada opção, acesse a página do plugin, clicando aqui ou na wiki do site, vamos la:

Parâmetro Descrição
datatype Indica que tipo de dado a grid deve tratar, pode ser JSON, XML, local, entre outros.
url Método no servidor que realizará a pesquisa, no caso a Action, poderia ser uma WebService ou um PageMethods do WebForms. Se o tipo do dado for local, você não precisa desse parâmetro.
colNames Nome (Header) de cada Coluna, se você tiver uma coluna sem nome, só com um checkbox, por exemplo, você deve informar espaços em brancos, a quantidade de itens em ColNames deve ser igual a quantidade de colunas no ColModel
colModel Aqui é onde você define como será cada coluna, as opções são inúmeras, então visite o site para ver mais.
pager Indica em qual controle o plugin criará os controles de paginação. Geralmente é uma div, como no exemplo. Existem muitas opções de pager também, visite o site!
jsonReader Informa a estrutura que os dados devem vir do servidor, e qual é o ID dos dados. A variação também é ampla, estou usando o default.

 

A grid acima ficaria como a imagem abaixo.

image

Usando apenas as configurações padrões, minha grid já é totalmente funcional, com paginação e ordenação por coluna. Como disse, cada opção abre uma grande gama de opções, o site do plugin é bem completo e minha intenção não é replicar o site aqui, apenas apresentar a Grid.

Essas configurações delegam a responsabilidade de paginação e ordenação no servidor, mudando um pouco você poderia ter a ordenação e paginação controlada pela própria Grid, porém, é lógico que é muito mais rápido (no sentido de desempenho em tempo de execução) fazer isso tudo no servidor, além de poupar muito a banda de internet transferindo apenas os dados que serão exibidos para o cliente.

Minha action Listar ficou assim (Implementei apenas a paginação para demonstração, o código naturalmente não é um código de produção, afinal, a idéia é apresentar a grid):

[Obs: Código alterado para correção da lógica]

   1:  {
   2:      using (var bd = new bdEntities())
   3:      {
   4:      var query = from pessoas in bd.Pessoa 
   5:              select pessoas;
   6:      var ini = (page - 1) * 10;
   7:      var fim = query.Count()> 10? 10: query.Count();
   8:      if (ini > 0)
   9:      {
  10:          fim = 10;
  11:          fim = fim - 10 >= 0 ? query.Count() % 10 : fim;
  12:      }
  13:   
  14:      var totalPags = query.Count() / 10;
  15:      if (query.Count() % 10 > 0)
  16:      {
  17:          totalPags++;
  18:      }
  19:      var jsonData = new
  20:      {
  21:          total = totalPags, // número de páginas retornadas no reader
  22:          page = page,
  23:          records = query.Count(), // número total de registros na base.
  24:          rows = query.ToList().GetRange(ini,fim)
  25:      };
  26:      return Json(jsonData, JsonRequestBehavior.AllowGet);
  27:      }
  28:  }

Algumas considerações ao código:

Minha Action retorna um JsonResult, afinal é um objeto Json que minha grid espera. O método Json fica responsável por converter meu objeto jsonData em Json. Apesar de eu estar criando a variável jsonData como um objeto dinâmico, eu poderia definir essa classe como uma classe concreta, e utilizar os parâmetros por toda a aplicação, é assim que costumo fazer.

As propriedades do jsonData seguem um padrão nomes de acordo com o definido no jsonReader (parâmetro da Grid), se você mudar alguma coisa no padrão do jsonReader terá que mudar também nas propriedades de retorno, mais uma vez: todas essas inúmeras possibilidades estão no site do plugin.

Os parâmetros da Action também devem seguir essa assinatura, para que o Asp.Net consiga traduzir os parâmetros vindo do JavaScript diretamente para os parâmetros. Você pode mudar  essas regras com um pouco mais de programação, que, alias, eu acho uma boa idéia se você estiver pensando em adotar a Grid como padrão de lista para seus sistemas, quanto menos parâmetros, mais rápido para desenvolver!

A lógica de paginação não faz parte da solução, é claro.Alegre

Conclusão

A idéia desse post foi apresentar o plugin JQGrid, e mostrar que o mundo sem server controls não é tão difícil assim, eu já uso essa grid a muito tempo em WebForms, porque é uma solução muito mais poderosa e versátil que o GridView. Acredite em mim, cada nova possibilidade que você descobre nesse plugin, você fica mais fascinado e confiante.

O exemplo que mostrei, não mostrou nenhuma possibilidade do plugin, mas se você quiser ter uma noção da capacidade dele, clique aqui e veja com seus próprios olhos.

Para baixar o código do exemplo, clique link abaixo

Baixar Arquivo

20. January 2011 23:55 by Frederico B. Emídio | Comments (3) | Permalink

Tarefas Básicas com Asp.Net. Parte II – HTML Helpers.    

Olá pessoal, nessa semana vo fazer um post bem simples, No post anterior, criei um cadastro básico. Nele, quis deixar claro que queria fazer tudo na mão, criando todo o HTML, apenas para tentar deixar mais claro como funciona a integração entre View e Controller.

Neste post, vou mostra como podemos diminuir a tarefa, muitas vezes enfadonha, de ficar digitando HTML constantemente, através da utilização de HTML Helpers

O que é um HTML Helper

HTML Helper são métodos estáticos que retornam simples strings. Lembre que no Asp.Net MVC não tem server controls como no WebForm, portanto, não espere que um HTML Helper te retorne um “controle” HTML onde você pode definir propriedades após a criação do mesmo via C#. Como disse, ele só retorna strings, em geral que represente uma tag HTML. Você pode criar também qualquer tipo de HTML Helper, mas isso é assunto para outro post, neste vamos apenas falar da utilização.

Por padrão, o Asp.Net MVC já vêm com uma série de Helper pré definidos, alguns deles estão na listagem abaixo:

  • Html.BeginForm()
  • Html.EndForm()
  • Html.TextBox()
  • Html.Hidden()
  • Html.ActionLink()
  • Html.CheckBox()
  • Html.DropDownList()
  • Html.ListBox()
  • Html.Password()
  • Html.RadioButton()
  • Html.TextArea()

A lista acima não tem todos os Helper, mas os mais comuns para criar um formulário de cadastro, por exemplo.

A utilização dos Helpers te abstrai das regras da criação dos formulários. Por exemplo, no exemplo do post anterior, o formulário de criação era o seguinte:

   <form name="formCadastro" action="Create" method="post">
    <h2>
        Criar novo Contato</h2>
    <p>
        <span>Nome</span><input type="text" name = "txtNome" id="txtNome" /></p>
    <p>
        <span>E-mail</span><input type="text" name = "txtEmail"  id="txtEmail" /></p>
    <p>
        <span>Telefone</span><input type="text" id="txtTelefone" name = "txtTelefone"  /></p>
    <p>
        <input type="submit" value="Salvar" /></p>
    </form>

Utilizando os Helpers, ele ficaria da seguintes forma:

    
<%using(Html.BeginForm()){ %>
 <h2>
        Criar novo Contato</h2>
    <p<
        <span>Nome</span><%:Html.TextBox("txtNome")%></p>
    <p>
        <span>E-mail</span><%:Html.TextBox("txtEmail")%></p>
    <p>
        <span>Telefone</span><%:Html.TextBox("txtTelefone")%></p>
    <p>
        <input type="submit" value="Salvar" /></p>
    <%} %>

Perceba que nitidamente tem menos HTML criado, e menos conhecimento de infra estrutura Web é necessário, pois eu não precisei definir o tipo do método do formulário, não precisei definir a Action, o Helper Html.BeginForm presumiu todo o necessário para criar o formulário.

É interessante notar também que em vezes de invocar o BeginForm e o EndForm, utilizei uma diretiva using como muitas vezes é utilizado no C# para definir o início e fim de uma instância de um objeto. Dessa forma, o HTML Helper sabe onde deve colocar o a tag final do formulário, sem a necessidade de utilizar o helper EndForm.

E para criar os textboxes, informei apenas um parâmetro e ele já presumiu o id e o name do controle. Se você fizer essas alterações no exemplo do post anterior, verá que a página funcionará do mesmo jeito, sem necessitar de nenhuma alteração no Controller.

Perceba também que, com exceção do BeginForm, os demais helper precisam ser inseridos dentro de uma tag que imprime texto do Asp.Net. No caso, utilizei as tags Asp.Net <%:Html.TextBox("txtNome") %>, poderia também <%= Html.TextBox("txtNome")%>, ou até mesmo um <%Response.Write(Html.TextBox("txtNome"));%>, lembre-se sempre, eu preciso colocar as tags de print ou utilizar o Response.Write, porque o helper retorna apenas uma string.

Outro exemplo de utilização muito útil de um Html Helper, seria para criar links, sem a necessidade de ter que informar o nome do Controller e da Action, como de costume. Por exemplo, o link abaixo:

<a href="Cadastro/Create">Novo</a>

Poderia ser criado utilizando um Helper da seguinte forma:

<%:Html.ActionLink("Nome","Create") %>

Perceba que não preciso assim conhecer quais são os atributos necessários de um link, apenas definir um texto para o link e saber qual Action invocar no meu Controller atual.

Bom, você viu como pode ser utilizado os Helpers HTML do MVC, é bom conhecer toda a lista existente de Helpers, para podermos maximizar o desempenho no desenvolvimento de sites com MVC, e, é claro, é importante saber que é possível, e simples, criar novos Helpers, para criar estruturas mais complexas, ou mesmo criar conjuntos de Tags que você utiliza sempre, como uma tela de acesso, ou um controle JQuery de tabs. Isso falaremos em um próximo post.

É isso, como comentei, o post hoje seria bem simples.

Até o próximo!

12. December 2010 20:03 by Frederico B. Emídio | Comments (1) | Permalink

Primeiros passos no Asp.Net MVC    

Olá pessoal, depois de uma pequena série preparando o terreno para começar a falar de Asp.Net MVC, finalmente chegou o momento de falar dessa forma de desenvolver Web.

Vou tentar fazer vários pequenos posts sobre este assunto, e hoje será um bem introdutório, tentando falar um pouco da estrutura do MVC, para que quem nunca criou um novo projeto desse tipo possa ir se habituando. Falarei a principio falarei de Asp.Net MVC 2, porém, conforme for se tornando conveniente, falarei das novas funcionalidades do Asp.Net MVC 3, que já está com a segunda Release Candidate publicada.

Então vamos lá!

Criando um Projeto MVC

Da mesma forma que você cria um projeto WebForm, para criar um MVC você deve ir até o Visual Studio, e seguir os procedimentos padrões, apenas escolhendo o tipo certo de site (Clique nas imagens para ampliar):

image

Quando criamos um projeto Asp.Net MVC, percebemos que uma estrutura bem maior do que a de um WebForm é criada, com diversas pastas e subpastas, como mostrado na imagem abaixo:

image

Vamos explicar um pouco sobre isso. Toda aplicação Web tende a ficar extensa, com inúmeros arquivos. No caso do WebForm, você pode organizar isso da forma que achar melhor, criando vários níveis de pastas e sub-pastas. No MVC, isso é um pouco diferente. Com MVC a Microsoft decidiu trabalhar seguindo convenções, e isso é levado a sério ao desenvolver sites MVC. No caso das pastas, a MS convencionou que deveria existir uma pasta com cada tipo de arquivo, que são: Modelos, Visões e Controladores (Model, View, Controllers – MVC). Existem outras pastas, mas o conteúdo delas é também comum ao WebForms, como Scripts e Content.

Lembre-se: Convenção é um termo que você ouvirá muito no mundo MVC.

É possível mudar essa convenção? Sim, mas nem sempre é interessante, na realidade isso será tema de algum próximo post.

Mas de forma geral, os Controllers você pode colocar em qualquer lugar no projeto que ele será encontrado, os Models (os dados da aplicação) também podem está em qualquer lugar, principalmente em outro projeto, como é comum, apenas as Views que, normalmente, você não trocar de lugar, as view sempre estão na seguinte estrutura de pasta: View/{NomeDoController}/{AçãoDoControle}. O que fugir muito dessa estrutura não será encontrado pela engine de View do MVC. E ai está o ponto, se você realmente quiser mudar essa estrutura, você terá que customizar a ViewEngine do Asp.Net MVC (assunto para outro post).

Ainda existe outra pequena forma de mudar a estrutura de pastas, também tema para um post, que a nova funcionalidade, adicionada a partir do MVC 2, que são as Areas, que resumidamente é uma forma de você agrupar “por funcionalidade” a estrutura de pastas Controllers, Models e Views, ou seja, replicando para cada grupo de Controladores, toda a estrutura padrão de pastas.

Agora que vimos um pouco cada pasta da estrutura do MVC, vamos entender no que consiste cada item do padrão Model View Controller na implementação do Asp.Net MVC.

Controller

O Controller no Asp.Net MVC é representado por uma classe que herda da classe System.Web.Mvc.Controller, é o Controller que é a base do endereço que digitamos na URL, e ele que sabe como deve ser a View que deve ser retornada. Um Controller pode retornar várias Views, de acordo com a Ação (Action) definida.

Action são os métodos do Controller que executam alguma ação e retornam uma View específica, utilizando um Modelo (Model) ou não.

Por exemplo, quando criamos um novo projeto, o Visual Studio cria dois Controllers: Home e Account. O Controller Home tem duas Actions, como mostrado abaixo:

public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }

Cada Action tem uma View relacionada na pasta View/Home, como pode ser visualizado no print abaixo:

image

Não precisa ser sempre assim. Lembre que uma View pode ser qualquer coisa que retorne para um usuário, portanto, pode ser texto simples, arquivo, JSON, comando para o browser redirecionar, enfim, qualquer coisa coisa que pode ser retornada para o Browser. Para deixar isso claro, vamos criar um novo Controller, da seguinte forma:

Clique na pasta Controller –> Add-> Controller e dê o nome de PrimeiroController, conforme prints abaixo:

image

image

Perceba que temos outra Convenção ao criar um controller: Todos devem ter o sufixo Controller, como os atributos devem ter o sufixo Attribute.

Quando criamos um Controller é criado uma Action padrão chamada Index, que é invocado toda vez que não definimos qual ação chamar, então vamos alterar a implementação padrão do método/action Index, para ficar como a seguinte:

public class PrimeiroController : Controller
    {
        //
        // GET: /Primeiro/

        public string Index()
        {
            return "Primeira página!";
        }

    }

Dando F5, vamos digitar /Primeiro após o nome do seu site no endereço e receberemos o retorno de nossa Action Index no Controller Primeiro da seguinte forma (Perceba que não precisamos digitar o sufixo Controller):

image

Neste casso, o controller está retornando todo o conteúdo HTML, que é a nossa View, vamos criar mais uma Action para ver que podemos chamar mais de uma Action dentro de um Controller e ter Views diferentes. Abaixo segue o código da Action, e o endereço http://localhost:50413/Primeiro/BemVindo digitado no endereço IE. Perceba que você pode colocar qualquer código HTML no retorno da View, e neste caso precisamos também colocar o nome da Action, porque agora não estamos chamando a Action padrão:

public string BemVindo()
{
	return "<h1>Minha Página!</h1><p>Olá, seja bem-vindo!</p>"

}

image

Mas calma! Você não precisa ficar digitando HTML em C# dessa forma, porque seria bem cansativo. Para isso você utiliza um ActionResult como retorno. Vamos mudar o tipo do retorno das nossas Actions de string  para ActionResult, e adicionar Views HTMLs para elas, dessa forma ficará mais fácil de controlarmos a geração do HTML. Podemos adicionar uma View clicando em cima do Método da Action e depois em Add View. Vamos fazer isso para os dois métodos. Aparecerá  a seguinte janela:

image

Essa é a janela que você define as informações da View. Vamos conhecê-la com o passar do tempo. Por hoje, precisamos ver apenas que estamos dando o nome da View, que é o mesmo nome da Action, e também estamos dizendo que ela utilizará uma MasterPage (Site.Master). Quanto a MasterPages, o conceito é o mesmo que no WebForm.

Depois de adicionarmos as duas Views, nosso diretório de View ficará assim:

image

Quando trabalhamos com Action do tipo ViewResult, podemos passar dados do controller para view de duas formas: Através do dicionário ViewData, que você pode pode definir qualquer informação, e lê-la a partir da View, ou através de Model, que é a representação dos dados, como você já sabe.

A princípio não existe nada de mais no HTML da View, porém, se você vem do WebForm, deve saber que NÃO EXISTEM NENHUM SERVER CONTROL no MVC. Portanto, você deverá conhecer HTML, se você veio de PHP ou Asp 3, verá que é bem parecido com o que você trabalhava, a principio pelo menos.

Vamos primeiro codificar o Index, tanto Action como View:

Action:

 public ActionResult Index()
        {
            ViewData["Texto"] = "Primeira Página";
            return View();
        }

View:

<asp:content id="Content2" runat="server" contentplaceholderid="MainContent">

    <h2><%=ViewData["Texto"] %></h2>

</asp:content>

Resultado:

image

Agora o comportamento com um Model, e vendo como ser acessado a partir da View, agora com a Action BemVindo. Nesse caso, estou utilizando como modelo um objeto dinâmico,, apenas como exemplo, mas poderia ser qualquer estrutura de dados:

Action:

public ActionResult BemVindo()
        {
            dynamic model = new ExpandoObject();
            model.Texto = "Seja bem-vindo!";
            return View(model);
        }

View:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2><%=Model.Texto %></h2>

</asp:Content>

Resultado:

image

Perceba que agora  agente não utilizou o View data, mas um objeto chamado Model, que terá as propriedades do seu modelo de acordo com o definido na Action.

No nosso caso, o IntelliSense do Model não funcionou porque o nosso objeto era dinâmico e o tipo da View era dinâmico, como podemos ver na primeira linha da View:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

No próximo post, veremos como fazer Views fortemente tipadas e como isso pode nos ajudar no desenvolvimento.

Bom, acredito que para um primeiro post, apenas introdutório, o que foi passado já é suficiente para você ir fuçando e descobrindo mais coisas do Asp.Net MVC.

Qualquer dúvida estou a disposição.

Até o próximo!

16. November 2010 07:18 by Frederico B. Emídio | Comments (1) | 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

<<  February 2021  >>
MoTuWeThFrSaSu
25262728293031
1234567
891011121314
15161718192021
22232425262728
1234567

Visualizar posts em um Calendário
Sigua @fredemidio

MCP Asp.NET