sábado, 17 de novembro de 2012

JQuery Mobile, Plante Aqui vs Google Maps API

Boa tarde caros "encodes" ...
sábado quente em Londrina pra variar! A um bom tempo sem escrever, prova de certificação bem próxima e o tempo vai ficando um pouco escasso, chega de desculpas baratas "HAHA" e vamos lá (...)
Post de hoje pretendo expor a vocês uma nova API que venho estudando e pude participar de um "Hackathon" com uma galera muito fera, que o intuito era apresentar essa API e suas funcionabilidades. Falando um pouco sobre a API, ela se chama Plante Aqui você pode encontrar mais sobre no Plante Aqui no Facebook ou se preferir no site oficial http://www.planteaqui.org/ lá você vai encontrar sobre a ideia da API e a proposta que a mesma está divulgando que é bastante interessante por sinal.
Aqui vou apresentar algo bastante básico e utilizar o recurso de consultas que a API (Plante Aqui) me fornece.
Após você ter dado uma lida nos links que repassei vamos explicar o nosso programa de exemplo:

1 - Desenvolver uma interface mobile com JQuery Mobile.
2 - Repassar os dados consultados a API(Plante Aqui).
3 - Com o retorno de sucesso da API(Plante Aqui) vamos utilizar a API(Google Maps) para mostrar os resultados em tela.

Considerando que você leu a ideia central do Plante Aqui a nossa interface cliente basicamente vai receber os dados de latitude e longitude informados pelo usuário que seria as coordenadas em que você quer saber se naquele ponto existe um local adequado para se plantar uma arvore se as coordenadas forem encontradas vamos repassar elas a nosso maps marcando o local.
Chega de papo e vamos codar =)
Começamos a implementar a parte visual:
 <!DOCTYPE html>  
 <html>  
 <head>  
      <meta charset="utf-8">  
      <title>Google Maps vs Plante Aqui</title>  
      <link rel="stylesheet" href="css/themes/default/jquery.mobile-1.2.0.css" />  
      <link rel="stylesheet" href="docs/_assets/css/jqm-docs.css" />  
      <script src="js/jquery.js"></script>  
      <script src="docs/_assets/js/jqm-docs.js"></script>  
      <script src="js/jquery.mobile-1.2.0.js"></script>  
 </head>  
 <script type="text/javascript">  
 </script>  
 <body>  
      <div class="ui-body ui-body-a">  
           <div data-role="fieldcontain">  
              <label for="latitude">Latitude:</label>  
              <input type="text" name="latitude" id="latitude" value="" />  
           </div>  
           <div data-role="fieldcontain">  
              <label for="longitude">Longitude:</label>  
              <input type="text" name="longitude" id="longitude" value="" />  
           </div>  
           <button type="button" data-theme="b" id="enviar" name="enviar" onclick="buscarEndereco();">Verificar Disponibilidade  
           </button>  
      </div>  
      <div id="map_canvas" style="width: 100%; height: 500px"></div>  
 </body>  
 </html>  
Como pode ver o código é bastante simples com JQuery Mobile.
O próximo passo seria implementar o codigo que será responsável em "desenhar" o mapa.
Não sei se você teve a oportunidade de trabalhar com alguma integração das API do google porém vai ver que é bastante simples de entender e aplicar nos seus programas as inúmeras funcionabilidades que a google apresenta.
Sobre o google maps em questão você vai encontrar a documentação aqui: https://developers.google.com/maps/documentation/javascript/v2/introduction neste link tem os exemplos de códigos e parametros que você pode utilizar para enriquecer teu programa.
Voltando a nosso exemplo implemente essa função.
   var map = null;  
   var geocoder = null;  
   function initialize() {  
    if (GBrowserIsCompatible()) {  
     map = new GMap2(document.getElementById("map_canvas"));  
     map.setCenter(new GLatLng(37.4419, -122.1419), 1);  
     map.setMapType(G_HYBRID_MAP);  
     map.setUIToDefault();  
     geocoder = new GClientGeocoder();  
    }  
   }  

Se você deu uma lida rápida no link que repassei sobre a API do Google Maps creio que ficou bastante fácil entender o meu código a seguir! "hehe" De modo geral eu instancio a classe GMap2() repassando para o construtor da classe o elemento HTML criado na minha pagina que irá receber o canvas do mapa gerado.
O restante do código se trata de configurações propriamente falando do mapa. O método SetMapType(G_HYBRID_MAP); por exemplo defini o tipo de mapa que será apresentando ao inicializar o programa, no meu caso eu escolhi o "SATÉLITE".
O nosso próximo passo seria adicionar a função a seguir no onload da pagina:
 <body onload="initialize()" onunload="GUnload()">  
...e também não podemos esquecer de adicionar a referencia a API do google:
 <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key="SUA_CHAVE"  
 type="text/javascript"></script>  
Perceba que no parametro "KEY" você deverá colocar a sua KEY gerada pela google no momento que você marcou a utilização de alguma API com o teu usuário google é claro.
Mais informações para obter a sua KEY:
https://developers.google.com/maps/documentation/javascript/tutorial
Fazendo isso você já verá algum resultado (...)

Vamos agora para a nossa próxima implementação que seria a função de busca das coordenadas informadas.
No evento onclick="" do button "Verificar Disponibilidade" vamos chamar a seguinte função:
 function buscarEndereco(button){  
           var iLatitude = $('#latitude').val();  
           var iLongitude = $('#longitude').val();  
           if(iLatitude != '' || iLongitude != ''){  
                $.getJSON('http://www.planteaqui.org/api/position?lat='+iLatitude+'&lon='+iLongitude+'',   
                     function(data) {  
                          // dados retornados do Plante Aqui  
                          point = new GLatLng(data[0].positions.lat,data[0].positions.lon);  
                          marker = new GMarker(point);  
                          map.addOverlay(marker);  
                     }  
                );  
           }else{  
                alert('Informe a Latitude e a Longitude para buscar!');  
           }  
  }  
Antes de explicarmos o nosso código vale ressaltar sobre a API Plante Aqui, pois se você a leu a documentação vai perceber que ela é baseada em REstFull desta forma utilizei a função $.getJSON(url,function() { } ); 
veja que a requisição é assíncrona e no segundo parâmetro você deve criar a sua função que receberá o retorno que no nosso caso é um JSON, após o retorno instancio a classe 
GLatLng(LATITUDE, LONGITUDE); 
repassando as coordenadas encontradas pela API Plante Aqui o retorno da função será guardada em uma VAR que será também repassada a classe GMarker(); que receberá as coordenadas retornando um ponto a ser marcado que por final será adicionado ao objeto principal do mapa. =)
Tranquilo né?
Pensando em ficar funcional para o usuário que estará fazendo a consulta não fico muito legal pois saber a latitude e longitude exata de um local é um pouquinho complicado "HAHA" porém este é o nosso primeiro exemplo utilizando API Plante Aqui, A ideia é evoluir e o próximo post que está praticamente pronto será implementado algumas alterações para corrigir isso. =)
A nível acadêmico alcançamos o nosso objetivo que seria mostrar como consumir uma API em RestFull e também a integração com o maps do google.
Fica o reforço da dica para você dar uma melhor olhada na API Plante Aqui veja que a documentação explica exatamente como "sugerir um local para se plantar" e outros métodos vale dizer também que a API está em constante alterações e novas melhorias vem ai.
Ficamos por aqui, com o agradecimento em especifico ao pessoal do Plante Aqui que abriu a oportunidade e a iniciativa.

Download do projeto completo
Até a próxima. :)

sexta-feira, 26 de outubro de 2012

MVC Helpers vs ExtJS 3.4 Sencha

Eai Caros "Encodes" (...)
Sexta-feira de muita chuva em Londrina é totalmente inspirador para um Post novo hehe, hoje vo tenta mostra a vocês a ideia de utilização de helpers no MVC 3.
Iniciando podemos começar falando um pouco do "RAZOR". Esse é o novo motor de visões de paginas adotado pela microsoft, esse novo "cara" segue o mesmo modelo de arquivos (.ASPX/.ASCX/.MASTER) que já estamos acostumados no WEB FORM.
Para uma mais refinada explicação vou utilizar uma definição feita em um ótimo blog que costumo visitar. (ScottGu's Blog
Compacto, Expressivo, e Fluente: o Razor minimiza o número de caracteres e teclas digitadas necessárias em um arquivo, e permite um fluxo de trabalho de codificação rápido e fluente. Ao contrário das sintaxes de outros modelos, você não precisa interromper sua codificação para explicitamente indicar blocos do servidor dentro do código HTML. O analisador é inteligente o suficiente para inferir isso a partir do seu código. Isto permite uma sintaxe muito compacta e expressiva, que é limpa, rápida e divertida para digitar.
Fácil para Aprender: o Razor é fácil de aprender e te permite ser produtivo rapidamente com um mínimo de conceitos. Você usa todas as suas habilidades existentes de linguagens de programação e HTML.
Não é uma nova linguagem: Nós conscientemente optamos por não criar uma nova linguagem imperativa com o Razor. Em vez disso, queríamos permitir que os desenvolvedores utilizassem seus atuais conhecimentos de linguagens de programação C#/VB (ou outras) com o Razor, entregando uma sintaxe de marcação para modelagem que permite um fluxo de trabalho de construção de código HTML bem legal com a sua linguagem de preferência.
Funciona com qualquer Editor de Texto: o Razor não necessita de uma ferramenta específica e te permite ser produtivo em qualquer editor de texto velho e simples (notepad funciona muito bem).
Tem ótimo suporte à Intellisense: o Razor foi projetado para não necessitar de uma ferramenta específica ou editor de código, mas ainda assim ele terá um excelente suporte para intellisense dentro do Visual Studio.
Unidade Testável: A nova implementação deste motor de visões suportará a capacidade de testar visões de maneira unitária (sem a necessidade de um controlador ou de um servidor web, e pode ser hospedado em qualquer projeto de teste unitário - não há necessidade de um app-domain [domínio de aplicação] especial).
Após entendermos algumas definições do Razor podemos partir para uma outra ideia que seria de criar o nosso próprio ajudante para criação de layouts.
O que vou mostrar a seguir seria basicamente utilizar o recurso das chamadas Razor em uma pagina HTML seguido de alguns parâmetros dinâmicos e gerando através do ExtJs (O que é ExtJs?) É um framework JavaScript de ricos recursos para desenvolvimento de layouts, ficando extremamente conhecido no Brasil, sendo agora de propriedade da SENCHA (http://www.sencha.com/) ele vem sendo atualizado constantemente a comunidade é ativa e nem preciso elogiar também a documentação !
Se você não conhece e tiver algum interesse em estudar mais ...

Docs - Versão 3.4 utilizada neste Post

Docs - Versão 4.1 (ATUAL)

Continuando (...) o que o ExtJs tem haver com o Razor? hehe na verdade como disse no começo vou agregar esse dois recursos para um ganho significativo na produtividade para a criação de uma Grid. Se você tem uma noção mais voltada para arquitetura pode estar pensando "Abstrair o que já está abstraído?" realmente um termo que me perguntei varias vezes até começar a trabalhar em algo do tipo, porém a PRODUTIVIDADE vs PADRONIZAÇÃO falou mais alto. Na verdade isso seria papo para uma longa discução mas ao final do POST seja você capaz de tirar suas próprias conclusões! Chega de "BLA BLA" e para manter o costume sirva-se de um café e vamos codar  ...

Primeiramente, criei um projeto PADRÃO utilizando um template já pronto do VS2010 e também uma pasta "Helpers".


Na pasta Helpers adiciona um classe chamada "HelperGrid.cs" essa classe irá receber e processar as chamadas vinda da pagina feita pelo Razor.
Vamos implementar a classe HelperGrid.cs, resumidamente nela eu implementei uma logica simples, aonde coloquei toda a ideia de parâmetros que uma Grid poderia receber dinamicamente sendo mais simplificado ainda, utilizei da minha imaginação e adicionei alguns métodos aonde enxerguei que a minha Grid poderia ser dinâmica. Segue o código:
 namespace MvcApplication1.Helpers  
 {  
   public static class HelperGrid  
   {  
     public static ExtGrid ExtGrid(this HtmlHelper helper, string id)  
     {  
       return new ExtGrid(helper, id);  
     }  
   }  
   public class ExtGrid  
   {  
     private HtmlHelper helper;  
     private string id;  
     private string url;  
     private string pkTabela;  
     private string tituloGrid;  
     private List<ColunasGrid> listaColunasGrid = new List<ColunasGrid>();  
     private string elementoRoot;  
     private int pageSizeGrid;  
     public ExtGrid(HtmlHelper helper, string id)  
     {  
       this.helper = helper;  
       this.id = id;  
     }  
     /// <summary>  
     ///   
     /// </summary>  
     /// <param name="url"></param>  
     /// <param name="pkTabela"></param>  
     /// <returns></returns>  
     public ExtGrid SetDataStore(string url, string pkTabela)  
     {  
       this.url = url;  
       this.pkTabela = pkTabela;  
       return this;  
     }  
     /// <summary>  
     ///   
     /// </summary>  
     /// <param name="root"></param>  
     /// <returns></returns>  
     public ExtGrid SetElementRoot(string root)  
     {  
       this.elementoRoot = root;  
       return this;  
     }  
     /// <summary>  
     ///   
     /// </summary>  
     /// <param name="pageSize"></param>  
     /// <returns></returns>  
     public ExtGrid SetPageSize(int pageSize = 20)  
     {  
       this.pageSizeGrid = pageSize;  
       return this;  
     }  
     /// <summary>  
     ///   
     /// </summary>  
     /// <param name="Name"></param>  
     /// <param name="Header"></param>  
     /// <param name="Tamanho"></param>  
     /// <param name="Visivel"></param>  
     /// <param name="Xtype"></param>  
     /// <returns></returns>  
     public ExtGrid Columns(string Name, string Header, int Tamanho, bool Visivel = true, string Xtype = null)  
     {  
       ColunasGrid colunasGrid = new ColunasGrid(Name, Header, Tamanho, Visivel, Xtype);  
       this.listaColunasGrid.Add(colunasGrid);  
       return this;  
     }  
     /// <summary>  
     ///   
     /// </summary>  
     /// <param name="titulo"></param>  
     /// <returns></returns>  
     public ExtGrid SetTitulo(string titulo = null)  
     {  
       this.tituloGrid = titulo;  
       return this;  
     }  
     /// <summary>  
     ///   
     /// </summary>  
     public void Render()  
     {  
       GridXml gridXmlAtributos = new GridXml()  
       {  
         url = this.url,  
         pkTabela = this.pkTabela,  
         tituloGrid = this.tituloGrid,  
         elementoRoot = this.elementoRoot,  
         pageSize = this.pageSizeGrid  
       };  
       foreach (ColunasGrid column in this.listaColunasGrid)  
       {  
         GridColumnXml layoutColumnGrid = new GridColumnXml()  
         {  
           name = column.name,  
           header = column.header,  
           tamanho = column.tamanho,  
           visible = column.visivel,  
           xtype = column.xtype  
         };  
         gridXmlAtributos.columns.Add(layoutColumnGrid);  
       }  
       string xml;  
       try  
       {  
         xml = HelperUtil.Serialize<GridXml>(gridXmlAtributos);  
       }  
       catch (Exception ex)  
       {  
         throw new Exception("Erro ao serializar o XML" + ex.Message);  
       }  
       helper.ViewContext.Writer.Write("<div id=\"" + this.id + "\"");  
       helper.ViewContext.Writer.Write(" data-isGrid=\"true\"");  
       helper.ViewContext.Writer.WriteLine(">");  
       helper.ViewContext.Writer.WriteLine("<script type=\"text/xml\" id=\"" + this.id + "grid_xml\">");  
       helper.ViewContext.Writer.WriteLine(xml);  
       helper.ViewContext.Writer.WriteLine("</script>");  
       helper.ViewContext.Writer.WriteLine("</div>");  
     }  
   }  
   public class ColunasGrid  
   {  
     public string name { get; set; }  
     public string header { get; set; }  
     public int tamanho { get; set; }  
     public bool visivel { get; set; }  
     public string xtype { get; set; }  
     public ColunasGrid(string name, string header, int tamanho, bool visivel, string xtype)  
     {  
       this.name = name;  
       this.header = header;  
       this.tamanho = tamanho;  
       this.visivel = visivel;  
       this.xtype = xtype;  
     }  
   }  
   /// <summary>  
   ///   
   /// </summary>  
   [XmlType(TypeName = "gridLayout")]  
   public class GridXml  
   {  
     public string url;  
     public string pkTabela;  
     public string tituloGrid;  
     public string elementoRoot;  
     public int pageSize;  
     public string modeFilter;  
     public int charFiltro;  
     public List<GridColumnXml> columns;  
     public GridXml()  
     {  
       this.columns = new List<GridColumnXml>();  
     }  
     public readonly static XmlSerializer Serializer = new XmlSerializer(typeof(GridColumnXml));  
   }  
   /// <summary>  
   ///   
   /// </summary>  
   [XmlType(TypeName = "column")]  
   public class GridColumnXml  
   {  
     [XmlAttribute]  
     public string name;  
     [XmlAttribute]  
     public string header;  
     [XmlAttribute]  
     public int tamanho;  
     [XmlAttribute]  
     public bool visible;  
     [XmlAttribute]  
     public string xtype;  
   }  
 }  


Vale colocar algumas observações na parte do código:

       helper.ViewContext.Writer.Write("<div id=\"" + this.id + "\"");  
       helper.ViewContext.Writer.Write(" data-isGrid=\"true\"");  
       helper.ViewContext.Writer.WriteLine(">");  
       helper.ViewContext.Writer.WriteLine("<script type=\"text/xml\" id=\"" + this.id + "grid_xml\">");  
       helper.ViewContext.Writer.WriteLine(xml);  
       helper.ViewContext.Writer.WriteLine("</script>");  
       helper.ViewContext.Writer.WriteLine("</div>");  

Esse é o trecho que podemos dizer que é mais importante nessa logica, pois através do ViewContext que é disponibilizado pelo "helper" que seria basicamente todo conteúdo da pagina colocamos um pacote XML serializado que como você pode perceber no restante do código anterior é a nossa hierarquia de classes. Porque um pacote XML contido dentro de um <script> </script> ?  Isso é devido a renderização da pagina pois se escrevermos simplesmente o texto do XML serializado o browser irá tentar "fazer alguma coisa" com a string repassada prejudicando o nosso objetivo que como vamos ver adiante é capturar esse pacote via JavaScript   (...)

AHH, já ia esquecendo implemente uma outra classe chamada "HelperUtil.cs" nela coloquei o codigo responsável por serializar qualquer objeto repassado <T>, segue o codigo:
   public static class HelperUtil  
   {  
     public static string Serialize<T>(T value)  
     {  
       if (value == null)  
       {  
         return null;  
       }  
       XmlSerializer serializer = new XmlSerializer(typeof(T));  
       System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings();  
       settings.Encoding = new System.Text.UnicodeEncoding(false, false);  
       settings.Indent = false;  
       settings.OmitXmlDeclaration = false;  
       using (StringWriter textWriter = new StringWriter())  
       {  
         using (System.Xml.XmlWriter xmlWriter = System.Xml.XmlWriter.Create(textWriter, settings))  
         {  
           serializer.Serialize(xmlWriter, value);  
         }  
         return textWriter.ToString();  
       }  
     }  
   }  

Ao chamar o metodo Render(); do nosso helper o pacote XML será escrito na pagina que foi feita a chamada, nesta VIEW o código é bastante reduzido pois ai que está a "graça" de tudo hehe nela será contida apenas os parâmetros requisitados para compor a nossa Grid e como estamos utilizando MVC a Url.Content do nosso controlador aonde a busca dos dados será feita.


Index.cshtml

 @using MvcApplication1.Helpers  
 @{   
   Html.ExtGrid("DIV_GRID_CONSULTA_PESSOA").SetDataStore(@Url.Content("~/Teste/GetAll"), "idPessoa").  
      SetTitulo("Consultar Pessoas").  
      SetElementRoot("Pessoa").  
      SetPageSize(20).  
      Columns("idPessoa", "Pessoa", 50, true, "Int").  
      Columns("nmPessoa", "Nome", 820, true, "String").  
      Render();  
 }  

ATENÇÃO: não se esqueça de adicionar o @using do seu helper pois é extremamente valido, sem ele você não terá acesso aos métodos disponibilizados no helper.

Teria uma outra maneira de implementar essa referencia diretamente no Web.config mas optei por fazer na VIEW para uma melhor visualização conceitual e também expressar a importância pois sem a referencia você não terá acesso a sua classe ajudante.

Seguindo a nossa chamada que foi feita a partir da nossa VIEW o pacote XML foi gerado como retorno na pagina, agora iremos montar no JavaScript como iria ficar a nossa Grid. Antes de "codarmos" o JS teremos que adicionar em nosso projeto a pasta contendo o Source da estrutura do ExtJs, enfatizando que disse no começo utilizei a versão 3.4, aproveitando também criamos uma outra pasta chamada 
"ext-js-helpers" para colocarmos o nosso JS que será responsável pela criação da Grid.
Agora o escopo do nosso projeto ficaria mais ou menos assim (...)

Vamos implementar o nosso JS ...
 jQuery(document).ready(function () {  
   var grids = jQuery("div[data-isGrid=\'true']");  
   grids.each(function (i, obj) {  
     var grid = new Grid(i, obj);  
   });  
 });  
 function Grid(index, rootDiv) {  
   
   var $this = this;  
   var id = $(rootDiv).attr('id');  
   var id_grid_panel = 'GRID_'+id;  
   // Vamos pegar o nosso XML...  
   var atributosGrid = $($("#" + id + "grid_xml").html());  
   // aqui vamos pegar os atributos do nosso xml  
   var url        = $(atributosGrid).find('url').text();  
   var pkTabela   = $(atributosGrid).find('pkTabela').text();  
   var tituloGrid = $(atributosGrid).find('tituloGrid').text();  
   var root       = $(atributosGrid).find('elementoRoot').text();  
   var pageSize   = parseInt($(atributosGrid).find('pageSize').text());  
   
   var columnsModel = [];  
   var columnsField = [];  
   
   $(atributosGrid).find('columns column').each(function (i, obj) {  
     columnsModel.push({  
       id: $(obj).attr("name"),  
       header: $(obj).attr("header"),  
       dataIndex: $(obj).attr("name"),  
       width: parseInt($(obj).attr("tamanho")),  
       sortable: true  
     });  
     columnsField.push($(obj).attr("name"));  
   });  

   // criamos um JsonStore...  
   var StoreGrid = new Ext.data.JsonStore({  
     url: url,  
     root: root,  
     totalProperty: 'total',  
     idProperty: pkTabela,  
     fields: columnsField  
   });  

   StoreGrid.load({ params: { start: 0, limit: pageSize } }); 
 
   var grid = new Ext.grid.GridPanel({  
     id: id_grid_panel,  
     width: 900,  
     height: 395,  
     title: tituloGrid,  
     store: StoreGrid,  
     viewConfig: { emptyText: 'Nenhum registro encontrado', deferEmptyText: false },  
     stripeRows: true,  
     // grid columns  
     columns: columnsModel,  
     bbar: new Ext.PagingToolbar({  
       pageSize: pageSize,  
       store: StoreGrid,  
       displayInfo: true,  
       displayMsg: 'Exibindo Tópicos {0} - {1} of {2}',  
       emptyMsg: "Nenhum tópico para mostrar"  
     })  
   });  
   grid.render(id);  
 }  

Veja que não tem segredo !
O código é basicamente implementado a partir do ExtJs para gerar a instancia do objeto Ext.grid.GridPanel e as propriedades vindas do pacote XML sendo capturado utilizando a bilbioteca do JQuery e abusando do comando Push() para montar as propriedades de Object esperado pelo ExtJs dinamicamente.
Após a correta implmentação não vamos esquecer de adicionar os scripts a nossa pagina ...



Com isso feito o resultado da nossa Grid seria mais ou menos isso !

Os dados gerados não são dados vindo de uma base "quente" e sim dados aleatórios (de um Model) serializados no método que informamos no  @Url.Content.
Perceba a requisição feita através do firebug no Firefox

Algo bastante importante para chamarmos atenção seria os metodos SetElementRoot() passado na VIEW que corresponde ao elemento PAI do JSON de retorno e também é claro os campos que como pode perceber o objeto que utilizei tinha muito mais propriedades do que mostrei na Grid porém exibi apenas o campo idPessoa e nmPessoa do mesmo, a ideia é exatamente essa deixar intuitivo para o desenvolvedor que string NAME repassada ao Helper corresponde ao nome da propriedade do objeto serializado.

É isso galera, espero que isso tenha sido útil e aqueles que lerem absorvam a ideia que quis repassar dessa junção de Helper e ExtJs. 
OBS: vale ressaltar novamente a quantidade de código na VIEW para a criação do mesmo, deixando seu projeto mais limpo e padronizado.

Abraço.  =))
Assim que tenho visto que não há coisa melhor do que alegrar-se o homem nas suas obras, porque essa é a sua porção; pois quem o fará voltar para ver o que será depois dele?
Eclesiastes 3:22 

quinta-feira, 18 de outubro de 2012

ASP.NET MVC Areas

Eai Caros "Encodes"  (...)
Estava a pensar nesses dias sobre um novo assunto para compartilhar com vocês e veio a mente um problema que tive algum tempo atrás em uma arquitetura web que estou trabalhando e resolvi compartilhar pois fiz varias pesquisas achei bons tutorias sobre o assunto porém a maioria deles em inglês, não é algo muito complicado porém se você nunca necessitou desse tipo de arquitetura e está disposto a implementar algo parecido vai perceber que fica um pouco chato no começo e muito simples e organizado de se trabalhar no passar do tempo do projeto.
Falando do "problema" necessariamente, quando você está trabalhando em um padrão de projeto em dotnet  mais especificamente MVC 3 que é o meu caso, você possui um padrão que o próprio visual studio cria para você ... seria mais ou menos assim:

 (OBS: apenas um parentes como você pode ver eu estou utilizando a versão 2012 do VS e apenas para nível de aprendizado e percepção de mudanças criei um projeto MVC4 mas para o assunto que estamos abordando não vai mudar nada praticamente na implementação)
Voltando (...)

Como pode ver existe o padrão de pastas CONTROLLERS, MODELS, VIEWS e o Global.asax este por sua vez é de extrema importância num projeto MVC pois ele é responsável pela manipulação de eventos e do ciclo de vida da aplicação. Basicamente ele contém uma tabela de rotas que é criada ao iniciar o aplicativo, mais detalhes sobre você pode dar uma lida no site oficial:
http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/asp-net-mvc-routing-overview-cs
até ai estava tudo certo até que comecei a perceber que no projeto em que estou locado iria ter um problema de organização dessas pastas pois não iria ter apenas um conceito de regra dentro da hierarquia do projeto, deixando isso mais claro simulei apenas para nível conceitual um diagrama demonstrando esse possível ambiente:

Seria mais ou menos essa a ideia, como você pode ver tenho um site principal aonde o acesso seria publico sendo mais detalhista seria acessado por qualquer usuário sem nenhuma autenticação, porém os outros módulos seria de acesso especifico apenas para usuários credenciados e também devemos considerar que seria desenvolvido por uma equipe diferente. Foi nessa ideia que partir a pesquisar e verifiquei que o dotnet tem algo chamado de AREA. É dessa forma que ele representa os subdomínios da sua aplicação, com isso você poderia tranquilamente simular esse ambiente que colocamos a seguir tendo nele uma arquitetura de pastas na mesma visão do MVC porém dividido por áreas ficando assim muito mais organizado e limpo o desenvolvimento (...)
Pra você que curti uma vídeo aula:
http://www.asp.net/mvc/videos/mvc-2/how-do-i/aspnet-mvc-2-areas

Chega de "historinha" hehe, para não perder o costume, sirva-se de um belo café e vamos codar, irei mostras as alterações que implementei com base nesse diagrama e também a tela principal com alguns ActionLink fazendo o roteamento para as outras áreas.
Inicialmente esta seria a arquitetura já criada:

 Perceba que o visual studio já criou pra você classes de "Registros" separadamente. Isso quer dizer que quando o Global.asax for registrar a aplicação verá que necessita também de registrar as áreas separadamente (...)
Global.asax:

   // Note: For instructions on enabling IIS6 or IIS7 classic mode,   
   // visit http://go.microsoft.com/?LinkId=9394801  
   public class MvcApplication : System.Web.HttpApplication  
   {  
     public static void RegisterRoutes(RouteCollection routes)  
     {  
       routes.IgnoreRoute("{resource}.axd/{*pathInfo}");  
       routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });  
     }  
     protected void Application_Start()  
     {  
       AreaRegistration.RegisterAllAreas();  
       FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);  
       RouteConfig.RegisterRoutes(RouteTable.Routes);  
     }  
   }  


As classes "PosVendaAreaRegistration.cs", "LojaAreaRegistration.cs", "FinanceiroAreaRegistration.cs" foram criadas automaticamente pelo VS quando criei a AREA.


Apenas na classe "PrincipalAreaRegistration.cs" fiz uma pequena alteração dizendo quem seria a pagina "ROOT" do projeto basicamente seria a pagina que será chamada inicialmente ao abrir a aplicação.

   public class PrincipalAreaRegistration : AreaRegistration  
   {  
     public override string AreaName  
     {  
       get  
       {  
         return "Principal";  
       }  
     }  
     public override void RegisterArea(AreaRegistrationContext context)  
     {  
       context.MapRoute("Root", "", new { controller = "Publico", action = "Index", id = UrlParameter.Optional });  
       context.MapRoute(  
         "Principal_default",  
         "Principal/{controller}/{action}/{id}",  
         new { action = "Index", id = UrlParameter.Optional }  
       );  
     }  
   }  

O trecho de codigo "context.MapRoute()" mapea o root da pagina que por sua vez aponta para o controller "Publico" e a View "Index" do projeto, ao executar a aplicação o resultado seria esse:
Como pode ver eu já implementei os links que irão chamar os outros módulos, perceba que ao clicar em cada um deles o roteamento é feito para a sua respectiva área chamando o "CONTROLLER" indicado no ActionLink, veja o fonte da Index:

 @{  
   Layout = null;  
 }  
 <!DOCTYPE html>  
 <html>  
 <head>  
   <meta name="viewport" content="width=device-width" />  
   <title>Index</title>  
 </head>  
 <body>  
   <div style="text-align:center">  
     <h2>Domínio Público</h2>  
     @Html.ActionLink("Area - Financeira", "Index", "Home", new { area = "Financeiro" }, null)  
     <br />  
     @Html.ActionLink("Area - Loja de Produtos", "Index", "Home", new { area = "Loja" }, null)  
     <br />  
     @Html.ActionLink("Area - Pós Venda", "Index", "Home", new { area = "PosVenda" }, null)  
   </div>  
 </body>  
 </html>  

Sem mistérios !  =))
Uma visão bastante interessante desse tipo de arquitetura seria o compartilhamento de _layouts e informações entres os "SITES" a grosso modo podemos ter uma percepção que temos vários ambientes de site dentro de apenas um, facilitando a manutenção e futuras melhorias no mesmo.
É isso ...
Espero ter ajudado.
Um abraço!

segunda-feira, 15 de outubro de 2012

HTML5 - Web SQL Database

Eai, Caros "Encodes" ... Meu primeiro post =)
Decidi compartilhar com vocês uma tecnologia que pude estudar a algum tempo atrás e implementar alguns pequenos projetos.
Este recurso de "BANCO DE DADOS" no browser é um recurso de persistência diretamente no cliente.
Para aqueles acostumados ao desenvolvimento web de alguns anos atrás pode achar que esse recurso é totalmente impossível da parte conceitual. 
Porém com os ricos recursos que o HTML5 vem trazendo isso é totalmente aceitável até porque se você tem um bom conceito de "desenvolvedor back-end" vai estar acostumado a utilizar SQL e feliz como um "PORCO NA LAMA" hehe então ...
Aqui neste breve exemplo vamos abordar três métodos:

1- openDatabase
2- transaction
3- executeSql

Porém Antes de implementa-los vamos aplicar um layout simples apenas para dar um lado mais "usuário" para o exemplo, vou utilizar uma biblioteca (JQuery Mobile) rica, simples, funcional...enfim vai te poupar de muita coisa na parte gráfica, a parte ruim é que alguém poderá te chamar de "Designer" HAHAHA chega de papo sirva-se de um café e vamos codar :)

Primeiramente não se esqueça de fazer as referencias necessárias para o funcionamento do JQuery Mobile.

 <link rel="stylesheet" href="css/themes/default/jquery.mobile-1.2.0.css" />  
 <script src="js/jquery.js"></script>  
 <script src="js/jquery.mobile-1.2.0.js"></script>  


Feito isso vamos codificar a parte do layout (...)


 <body>  
      <div class="ui-body ui-body-a">  
        <div data-role="fieldcontain">  
                <label for="status">Status:</label>  
                <select name="status" id="status" data-role="slider">  
                     <option value="on">Ativa</option>  
                     <option value="off">Inativa</option>  
                </select>   
           </div>  
           <div data-role="fieldcontain">  
              <label for="name">Nome:</label>  
              <input type="text" name="name" id="name" value="" />  
           </div>  
           <div data-role="fieldcontain">  
                <label for="sexo" class="select">Sexo:</label>  
                <select name="sexo" id="sexo">  
                     <option value="Selecione">Selecione</option>  
                     <option value="Masculino">Masculino</option>  
                     <option value="Feminino">Feminino</option>  
                </select>  
           </div>  
           <button type="button" data-theme="b" id="gravar" name="gravar" onclick="GravarDados()">Gravar</button>  
      </div>  
 </body>  

Desta forma já podemos verificar o resultado inicial do nosso layout.




"bunitão né?" rs, repare que o JQuery Mobile já trata todos os recursos necessários para o funcionamento ideal para um dispositivo móvel.

Continuando, como você pode ver fizemos uma chamada de uma função Javascript "GravarDados()" ao clicar no botão "GRAVAR" porém ela não foi implementada e se por acaso você fizer o teste verá que o resultado será um "Erro" por falta de implementação da mesma, sendo assim vamos codifica-la ...


      var GravarDados = function(){  
           if (!window.openDatabase) {  
                alert('Not Supported -> Please try with a WebKit Browser');  
           }else{  
                try{  
                     var db = openDatabase('bancoWebBrowser_1', '1.0', 'meu primeiro web database', 2 * 1024 * 1024);  
                }catch(e) {  
                     alert('Erro !'+e);  
                }  
           }  
      }  

Como o objetivo do tutorial é mostrar de forma "simplificada" pode notar que adotei um padrão de código simples, nada impede destas implementações ser feita utilizando recursos mais avançados de técnicas de orientação objetos e etc (...)

Algo bastante importante em citar na primeira verificação " if (!window.openDatabase)  { "que é bastante necessária pois o webSql Database não foi implementado por todos os browser.  =/
Os navegadores que dão suporte até o momento são: 
(Safari, SafariMobile and Chrome) and Opera 10.50 (ATOW alpha on Mac) 
As empresas responsáveis por alguns browsers como o Firefox alega que a não implementação seria por falta de padronização do SQL, lembrando que o padrão adotado seria do SQLLite.Devemos lembrar também que as especificações sobre web SQL Database não estão mais sendo mantida pela W3C.

Beware. This specification is no longer in active maintenance and the Web Applications Working Group does not intend to maintain it further.
Se quiser saber mais sobre o assunto verifique na documentação oficial. http://www.w3.org/TR/webdatabase/

Voltando ao que interessa (...)

O código abaixo efetua a criação do nosso banco de dados.
1 - Database Name
2 - Version Number
3 - Text Description
4 - Estimated size of database

- Nada de muito complexo até o momento! =)


  db.transaction(function (tx) {  
     tx.executeSql('CREATE TABLE IF NOT EXISTS pessoa (id INTEGER PRIMARY KEY AUTOINCREMENT, status text, nome text, sexo text)');  
        
        var status = $('#status').val();  
        var nome = $('#name').val();  
        var sexo = $('#sexo').val();  
        
        tx.executeSql('INSERT INTO pessoa (status,nome,sexo) VALUES (?,?,?)', [status,nome,sexo]);  
        
        tx.executeSql('SELECT id,status,nome,sexo FROM pessoa', [], function (tx, results) {  
        
        var len = results.rows.length, i;  
        for (i = 0; i < len; i++) {  
            console.log('Chave: ' +results.rows.item(i).id + ' Nome: '+ results.rows.item(i).nome+ ' Sexo: '+results.rows.item(i).sexo+ ' Status Cadastro: '+ results.rows.item(i).status);  
        }  
     });  

     // Limpa campos ...  
     $('#status').val('');  
     $('#name').val('');  
     $('#sexo').val('');  
  });  

- Note que os comando adotados para a criação da tabela "pessoa" possui uma sintaxe em que qualquer desenvolvedor back-end está acostumado a utilizar no seu dia-dia, após efetuarmos um script de INSERT também padrão pegando os campos da tela com o auxilio do JQuery.


 var nome = $('#name').val();  

...em seguida efetuamos uma consulta para verificarmos todos os dados gravados na nossa base sendo eles mostrado no "Console" do navegador. (Nada impede que você implemente uma interface para a melhor mostragem dos dados.)
 tx.executeSql('SELECT id,status,nome,sexo FROM pessoa', [], function (tx, results) {  
                               var len = results.rows.length, i;  
                                for (i = 0; i < len; i++) {  
                                 console.log('Chave: ' +results.rows.item(i).id + ' Nome: '+ results.rows.item(i).nome+ ' Sexo: '+results.rows.item(i).sexo+ ' Status Cadastro: '+ results.rows.item(i).status);  
                                }  
 });  

É interessante efetuarmos um parentes aqui e enfatizar a importância da utilização de uma ferramenta de DEBUG para implementação de códigos Javascript's pois ele é o seu único Guia como sabemos o Javascript é interpretado pelo motor do browser isso dificulta um pouco a leitura dos erros. Eu costumo utilizar o Chrome que já possui um console nativo porém o Firefox existe o Firebug um plugin excelente de terceiro que inclusive existe para varias outras versões de navegadores.
Site Oficial: http://getfirebug.com/

O resultado da implementação acima seria essa:



O resultado do "SELECT * " no console, ficaria assim (...)




Isso é tudo que você precisa para começar com Web SQL Database.

Você já deve ter percebido mas vale constar a ideia de software que colocamos a seguir é um conceito que vem surgindo novo, principalmente no mundo móvel que seria de um software web porém com funcionabilidades locais não descartando a possibilidade de uma integração com algum webservice ou sincronia desses dados (...) enfim fica a ideia !

Missão dada é missão cumprida ! hehe
LOGO estarei disponibilizando para download o fontes dos exemplos realizados aqui.

Até a próxima  =)

Links Utilizados no Post: