Usando Flexigrid com VRaptor

Todo programador sempre passa perrengue para fazer listas de dados e mexer com paginação, e sempre procuramos alguns componentes prontos para facilitar a nossa vida, “mas quem disse que a vida é fácil” hehe. Sempre é difícil achar alguma coisa boa ou pelo menos que tenha documentação decente. No caso da flexigrid não foi muito diferente. Ela é boa porem não tem quase nada de documentação.

Portanto hoje iremos desenvolver uma listagem de dados utilizando um componente JavaScript chamado Flexigrid.

Vamos utilizar o VRaptor.

Se você não conhece o VRaptor de uma olhada nesse post do Washington Botelho.

Antes de iniciar a bagaça vamos saber quais as vantagens de se utilizar o Flexigrid.

  • Funciona com JQuery;
  • Paginação de dados sob demanda;
  • Campo de pesquisa para filtrar os registros da grid;
  • Ordenação dos dados;
  • Aceita dados via JSON ou XML;
  • E tudo isso via Ajax.

Vale alertar que este artigo é voltado para uma aplicação real. Poderemos portanto utilizar alguns conceitos mais avançados. Em outras palavras, assumimos que você já possui conhecimento básico em VRaptor, assim como JPA e Json.

Bom, então vamos por a mão na massa!

Este exemplo será construído a partir do blank project do VRaptor 3.

Primeiro devemos colocar os arquivos do Flexigrid no nosso projeto. Eu particularmente gosto de separar os códigos JS das classes CSS e das imagens. Portanto coloquei os arquivos do flexigrid em pastas diferentes.

  • WebContent
    • css
    • js
    • img

A biblioteca Flexigrid requer a inclusão dos seguintes arquivos (1) js do JQuery; (2) js do flexigrid; e (3) CSS do Flexigrid.

<script type="text/javascript" src="<c:url value='/js/jquery-1.4.2.min.js'/>"></script>
<link type="text/css" rel="stylesheet" href="<c:url value='/css/flexigrid.css'/>" />
<script type="text/javascript" src="<c:url value='/js/flexigrid.js'/>"></script>

Para fazer a Flexigrid funcionar é bem simples. Vamos criar uma table com o id “list” e usar o seletor do JQuery com o método da Flexigrid.

    <script type="text/javascript"> $(document).ready(function() { $("#list").flexigrid ({ url: '<c:url value="/listar" />', dataType: 'json', method: 'GET', colModel: [ {display: 'Nome', name: 'nome', width: 400, sortable: true}, {display: 'E-mail', name: 'email', width: 230}, {display: 'Empresa', name: 'empresa', width: 80, sortable: true} ], searchitems: [ {display: 'Nome', name: 'nome', isdefault: true} ], usepager: true }); }); </script>

    

Agora vamos modificar o IndexController que vem no blank project do VRaptor, adicionando o método que vai suprir a Flexigrid com os dados a serem listados e serializados em JSON.

    @Resource
    public class IndexController {
        private final Result result;
        public IndexController(Result result) {
            this.result = result;
        }
        @Path("/")
        public void index() {
            result.include("variable", "VRaptor!");
        }
        
        @Path("/listar")
        public void listar() {
            FlexiGrid flex = new FlexiGrid();
            flex.setPage(1);
            flex.setTotal(2);
            Row rows[] = new Row[2];
            rows[0] = new Row(1, new String[] {"Makoto", "makoto@makoto.blog.br", "Giran"});
            rows[1] = new Row(1, new String[] {"João", "joao@qualquercoisa.com", "Teste"});
            flex.setRows(rows);
            result.use(Results.json()).withoutRoot().from(flex).include("rows").serialize();
        }
    }
    

Reparem que eu utilizei uma classe chamada “FlexiGrid” e uma chamada “Row”, essas classes eu criei para facilitar o trabalho, pois seria muito trampo criar uma String no formato JSON toda hora; sendo que o VRaptor já incorpora nele a biblioteca XStream dentro dele para serializar classes java em XML ou JSON.
Dai eu crie as classes FlexiGrid e Row que são exatamente iguais a estrutura que o Flexigrid esta esperando.

    public class FlexiGrid {
        private int total;
        private int page;
        private Row[] rows;
        public int getTotal() {
            return total;
        }
        public void setTotal(int total) {
            this.total = total;
        }
        public int getPage() {
            return page;
        }
        public void setPage(int page)
        {
            this.page = page;
        }
        public Row[] getRows() {
            return rows;
        }
        public void setRows(Row[] rows) {
            this.rows = rows; 
        }
    }
    
    public class Row {
        private long id;
        private String[] cell;
        public Row(long id, String[] cell) {
            this.cell = cell;
            this.id = id;
        }
        public String[] getCell() {
            return cell;
        }
        public void setCell(String[] cell) {
            this.cell = cell;
        }
        public long getId() {
            return id;
        }
        public void setId(long id) {
            this.id = id;
        }
    }
    

Sendo assim o VRaptor vai me retornar o JSON

abaixo:

    {
        "total": 2,
        "page": 1,
        "rows": [
            {
                "id": 1,
                "cell": [
                    "Makoto",
                    "makoto.vix@gmail.com",
                    "Giran"
                ]
            },
            {
                "id": 1,
                "cell": [
                    "João",
                    "joao@qualquercoisa.com",
                    "Teste"
                ]
            }
        ]
    }

    

Bom, é isso aí pessoal! Assim chega ao fim este post; segue abaixo o link com o projeto

funcionando no github.

Projeto vraptor-flexigrid

Tratando erros com Ajax e VRaptor

Esse post foi motivado pela forma não elegante que eu estava tratando as exceções em requisições ajax em minhas aplicações com VRaptor.

Sempre que eu fazia uma requisição ajax e precisava de tratar um erro acabava fazendo de uma forma bem procedural. Utilizava o retorno do callback do ajax e verificava se o retorno era um erro ou o dado esperando. Mas isso está bem longe de ser uma forma elegante de se tratar erros, sem falar que para cada ocasião é necessário verificar se o dado é erro ou não de forma diferente.

Então tive a ideia de não tratar mais o erro no controller e deixar para tratar no callback de erro do ajax, mas ai tive outro problema.

Como ter mais de uma error page? Uma vez que a página de erro customizada para o usuário não seria muito útil para receber no callback de erro. Postei minha dúvida para a lista do VRaptor da Caelum (caelum-vraptor@googlegroups.com) e o Lucas Cavalcante (@lucascs) me deu a idéia de fazer um interceptor com um try…catch e utilizar o accept do Header para saber se a requisição é ajax. Gostei da ideia, implementei e resolvi fazer este post.

A implementação foi bem fácil e tranquila. Primeiro criei um Interceptor que chamei de AjaxExceptionInterceptor.

        @Intercepts
        public class AjaxExceptionInterceptor implements Interceptor {
            
            private HttpServletRequest request;
            private Result result;
            
            public AjaxExceptionInterceptor(HttpServletRequest request, Result result) {
                this.request = request;
                this.result = result;
            }
        
            public boolean accepts(ResourceMethod method) {
                return request.getHeader("accept").contains("application/json"); 
            }
        
            public void intercept(InterceptorStack stack, ResourceMethod method,
                    Object resourceInstance) throws InterceptionException {
                try {
                    stack.next(method, resourceInstance);
                } catch (Exception e) {
                    result.use(Results.http()).body(e.getCause().getMessage());
                }
            }
        
        }
    

Reparem nas implementações dos métodos “accepts” e “intercept” exigidas pelo contrato da interface Interceptor.

Você verá que na implementação do método “accepts” é verificado se é uma chamada ajax pelo conteúdo do accept do Header.

E na implementação do método “intercept” foi colocado um try…catch e caso o controller lance uma excessão, a mesma será capturada e então é enviada uma resposta com a mensagem da exception e não o html padrão do tomcat ou algum outro customizado no web.xml.

O controller é implementado sem tratar as exceções ou lançando as exceções customizadas.

        @Resource
        public class IndexController {
        
            private final Result result;
        
            public IndexController(Result result) {
                this.result = result;
            }
        
            @Path("/")
            public void index() {
            }
            
            @Post
            @Path("/testaCodigo")
            public void testaCodigo(String codigo) {
                if (codigo == null || codigo.isEmpty()) {
                    throw new RuntimeException("Código Inválido");
                } 
                
                result.use(Results.json()).withoutRoot().from("Sucesso!!!!").serialize();
            }
        
        }
    

E por último basta fazer a chamada ajax na página.

        $.ajax({
            url : "<c:url value='/testaCodigo' />",
            type: "post",
            dataType: "json",
            data: "codigo=" + $("#codigo").val(),
            success: function (data) {
                alert(data);
            },
            error: function (jXHR, textStatus, errorThrown) {
                alert(jXHR.responseText);
            }
        });
    

Repare que agora temos um código mais elegante onde não precisamos verificar o retorno do callback de sucesso para saber se é um erro ou o dado esperado. Agora usamos o callback de erro do $.ajax do jQuery para capturar o erro caso ele ocorra e dar o tratamento desejado.

Clique aqui para ver o projeto completo que fiz com esse exemplo funcional no github.

Meu ambiente de trabalho em 7 itens

Meu ambiente de trabalho em 7 itens, alguém começou esse meme e então meu amigo @jeveaux me convocou para participar e falar um pouco sobre meu ambiente de trabalho.

1) Macbook Pro de 13″

Além de muito bonita é uma máquina fantastica. Na minha opinião macbook pro de 13″ é a melhor escolha para quem procura mobilidade e performance. E ainda vem com o OSX, um Sistema Operacional estável, elegante e muito intuitivo. Não poderia deixar de falar do trackpad ANIMAL dele, que sem sombra de dúvidas é o melhor do planeta. Definitivamente cada dia que passa fica mais impossível voltar a usar um PC.

2) Google

Utilizo o tempo todo o santo Google para buscar todo tipo de coisa. Utilizo também o GMail e o Calendar que juntos salvam várias vidas. E tudo isso obviamente integrado sincronizado com o meu IPhone.

3) Git + gitHub

Git é uma ferramenta de controle de versão distribuida extremamente fantastica. Onde cada usuário possui um repositório completo na sua máquina, o que possibilita o maior números de commits. Pois mesmo se a tarefa/módulo/funcionalidade/etc ainda não esteja pronta você pode fazer o seu commit tranquilo que só vai ficar no seu repositório local, dai quando realmente terminar é só dar um push para o repositório “master”. E o serviço do Github é matador. Eu utilizo na Giran, nos meus projetos pessoais e em meus projetos open-sources.

4) Eclipse

Trabalho quase que exclusivamente com Java e na minha humilde opinião o Eclipse é a melhor IDE disparada. Mas é sempre bom saber usar outras ferramentas como o VIM que me ajuda bastante quando faço acesso remoto via SSH.

5) Giran

O ambiente que a Giran oferece é muito show. É uma empresa jovem com uma estrutura hierárquica horizontal, ela nos oferece total liberdade, uma biblioteca recheada de livros, macbooks, aulas de inglês e temos a nossa disposição um Xbox 360 e muitos jogos \o/ ! Sem sombra de dúvidas é um ambiente que me motiva bastante.

Não poderia deixar de citar as pessoas, e a Giran possui um time multi-disciplinar com nerds de todos os tipos. Pessoa essas que me fazem crescer tanto pessoalmente quanto profissionalmente todos os dias. Posso dizer que sou um privilegiado, pois faço o que gosto com pessoas legais e em um ambiente totalmente d+!

6) Scrum + XP

Na Giram usamos a combinação matadora Scrum + XP e adotamos uma postura não covarde, o que vem dando muito certo. Estamos obtendo resultados muito bons e o melhor de tudo clientes satisfeitos.

7) VirtualBox

Infelizmente nem tudo é perfeito e o maldito IE é o “navegador” mais usado. Por isso eu necessito de uma VM só para fazer os testes das aplicações no IE. Escolhi o VirtualBox porque ele é leve, gratuito e open-source.

Para não quebrar a corrente

Indico meus amigos a participar:

  • Rafael Carneiro (@rcarneiro)
  • Carlan Calazans (@carlancalazans)
  • Washington Botelho (@wbotelhos)
  • André Tagliati (@tagliati)
  • Gabriel Benz (@glbenz)

Preenchendo forms com jQuery Form Fill

Quando estamos desenvolvendo sistemas web sempre aparece aquela tela monstro com mais de 20 campos, dai bate aquele desânimo. Cansado de sofrer com isso desenvolvi o jQuery Form Fill Plugin.

O Form Fill recebe um objeto JSON e preenche um form com os valores do objeto, simples assim!

Seguem abaixo as features do plugin:

  • Preenche todos os tipos de elementos do form:
    • text
    • password
    • hidden
    • textarea
    • checkbox
    • radio
    • select
  • Preenche campos que utilizam o jQueryUI Datepicker
    • Data no formato texto. Ex: mm/dd/yyyy ou yyyy-mm-dd
    • Data em milisegundos.
  • Resolve nomes de elementos em estilo de objeto (usuario.nome) por default, mas também funciona sem estilo (nome).

Vamos fazer um exemplo com as opções default um form com os nomes dos elementos no estilo O.O.

        <form id="form1"> 
            <label>Nome:</label> 
            <input type="text" name="user.name" />
            
            <label>E-mail:</label>
            <input type="text" name="user.email" />
            
            <label>Phone:</label>
            <input type="text" name="user.phone.codeArea" size="3" maxlength="3" />
            <input type="text" name="user.phone.number" size="11" maxlength="10" />
            
            <label>Date:</label>
            <input type="text" name="user.date1" id="date1" /> from string (mm/dd/yyyy, mm-dd-yyyy, yyyy-mm-dd or yyyy/mm/dd)
            
            <label>Date 2:</label>
            <input type="text" name="user.date2" id="date2"/> from milliseconds
            
            <label>Gender:</label>
            <select name="user.gender"> 
                <option>--select--</option> 
                <option value="F">Female</option> 
                <option value="M">Male</option> 
            </select> 
            
            <label>Is Administrator?</label><br /> <!--input type="checkbox" name="user.admin" value="Y" -->
            <input type="radio" name="user.admin" value="Y"> Yes
            <input type="radio" name="user.admin" value="N"> No
            
            <label>Rules:</label>
            <input type="checkbox" name="user.rules&#91;0&#93;.id" value="1" /> Admin
            <input type="checkbox" name="user.rules&#91;1&#93;.id" value="2" /> User
            <input type="checkbox" name="user.rules&#91;2&#93;.id" value="3" /> Developer
            <input type="button" id="btnExample1" value="Fill" />
            <input type="reset" value="Reset" />
        </form> 
    

Agora vamos implementar o evento onclick do botão Fill do form1. Vamos criar um objeto JSON e chamar o método fill no seletor do form1 e a mágica será feita.

        $("#btnExample1").click(function() {
            var user = {
                "name" : "Makoto Hashimoto", 
                "email" : "makoto@makoto.blog.br", 
                "phone" : {
                    "codeArea": "+55",
                    "number" : "2755555555"
                },
                "date1": "03/30/1981",
                "date2": "354769200000", // 1981-03-30 in milliseconds
                "gender" : "M",
                "admin" : "Y",
                "rules" : [
                    {
                        "id" : "1", 
                        "name": "Admin"
                    },
                    {
                        "id" : "3", 
                        "name": "Developer"
                    }
                ]
            };
            $("#form1").fill(user);
        });
    

Seguem abaixo os links das páginas do projeto e do projeto no Github.

Página do Projeto – http://makoto.blog.br/formFill

Projeto no Github – http://github.com/makotohashimoto/formFill

Maré de Agilidade em Vitória

Acompanhando o Maré de Agilidade de BH teremos Este grande evento que ocorrerá na faculdade Faesa Campus I no dia 29 de Maio.

Com os patrocínios da Giran, Caelum, GUJ, infoQBR, Highlan, Qualidata e PowerLogic contará com grandes palestrantres:

Para maiores informações acesse: http://www.mare-vix.com/index.php/palestras/