Utilizando JQGrid com WebForms    

Olá pessoal!

Muitas pessoas têm dificuldades de utilizar plugins JQuery com WebForms. Principalmente porque desenvolvedor WebForm muitas vezes sabe usar apenas ServerControls, desconhecendo muitos conceitos básicos de Web em sí. Não é a toa que fiz uma série de post nesse blog mostrando conceitos de Web com WebForms.

Geralmente, quando uma pessoa vê exemplos de JQuery, eles estão em PHP, e a pessoa acredita que não funcionaria com WebForms, ou mesmo que saiba que seja possível, não sabe como adaptar para WebForm. Obs.: Geralmente esse problema não acontece com quem desenvolve com Asp.Net MVC.

Nos últimos post, mostrei alguns conceitos que podem ajudar, todos relacionados a PageMethods, como esse e esse. Se você não leu nenhum deles, ou está caindo diretamente nesse post, aconselho fortemente a ler pelo menos esse: Invocando PageMethods diretamente com JQuery.

Se você entender bem esse post que citei, nem precisará desse para utilizar o JQGrid ou qualquer outro plugin JQuery.

Então vamos lá!

Nesse post, vou utilizar as funcionalidades básicas do JQGrid, como Listar, Pesquisar e Editar/Adicionar.

O mais básico, e que todo mundo utiliza, é o Listar. Vou fazer o seguinte no meu HTML/JavaScript:

   1: <script type="text/javascript">
   2:     $().ready(function () {
   3:          $.ajaxSetup({
   4:                dataFilter: function (data, type) {
   5:                    if (type == "json" || type == undefined) {
   6:                        var msg = eval('(' + data + ')');
   7:                        if (msg.hasOwnProperty('d'))
   8:                            return JSON.stringify(msg.d);
   9:                        else
  10:                           return data;
  11:                   }
  12:                   else return data;
  13:               }
  14:            });
  15:  
  16:         $("#tbGrid").jqGrid({
  17:             mtype: "post",
  18:             url: '/Default.aspx/Listar',
  19:             datatype: "json",
  20:             ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },
  21:             colNames: ['ID', 'Nome', 'Idade', 'E-mail'],
  22:             colModel: [
  23:                        { name: 'PessoaID', index: 'PessoaID', width: 55 },
  24:                        { name: 'Nome', index: 'Nome', width: 150 },
  25:                        { name: 'Idade', index: 'Idade', width: 100 },
  26:                        { name: 'Email', index: 'Email', width: 150 }
  27:                    ],
  28:             rowNum: 10,
  29:             rowList: [10, 20, 30],
  30:             pager: '#pager',
  31:             sortname: 'PessoaID',
  32:             viewrecords: true,
  33:             sortorder: "desc",
  34:             caption: "JQGrid com WebForms",
  35:             serializeGridData: function (dados) {
  36:                 return JSON.stringify(dados)
  37:             },
  38:             jsonReader: { repeatitems: false, id: "PessoaID" }
  39:  
  40:         });
  41:         $("#tbGrid").jqGrid('navGrid', '#pager', { edit: false, add: false, del: false });
  42:     });
  43: </script>
  44: <table id="tbGrid">
  45: </table>
  46: <div id="pager">
  47: </div>

Até ai não fiz nada de mais, adicionei configurações padrões na Grid. Adicionei quatro colunas que virão do meu PageMethod, que está na página Default.aspx com o nome Listar.

Como será que deve ficar meu PageMethod para aceitar a requisição dessa Grid? Se não sabe, ai vai uma dica: Utilizando o Fiddler ou o DevelopToolBar, você pode ver qual HTTP Request o Grid está fazendo. Verificando quais campos estão lá você saberá que são os mesmos parâmetros que deve ter no seu PageMethod.

No meu caso, o JQGrid mandou os seguintes campos:

{"_search":false,"nd":1314831637255,"rows":10,"page":1,"sidx":"PessoaID","sord":"desc"}

No meu caso, o que importa é :

  • rows – Representa a quantidade de linha por página.
  • page – Indica a página atual.
  • sidx – Indica o campo pelo qual a Grid está ordenada. Sidx significa Search Index
  • sord – Indica a ordem que o campo definido em sidx deve ser ordenado.

Nesse caso, se meu PageMethod tiver esses campos já será o suficiente. Abaixo segue a implementação dele:

   1: [WebMethod]
   2: public static Reader<Pessoa> Listar( string sidx, string sord, int page, int rows)
   3: {
   4:     var modelo = new Modelo();
   5:     var lista = modelo.Pessoas.ToList();
   6:     var reader = new Reader<Pessoa>(lista,page,rows);
   7:     return reader;
   8: }

Como ele faz a busca, muda de solução para solução. No meu caso eu estou realizando a pesquisa em um SQLServer CE com EntityFramework. Minha classe Reader é responsável por realizar uma “paginação” nos registros retornados e retornar os campos de acordo com o JSONReader da JQGrid. Todo o código fonte dele está abaixo, apenas por curiosidade, visto que você não precisa dele para fazer seu JQGrid funcionar, ele apenas ajuda:

   1: public class Reader<T>
   2: {
   3:    /// <summary>
   4:    /// Número de páginas retornadas no registro
   5:    /// </summary>
   6:    public int total { get; set; }
   7:    /// <summary>
   8:    /// Página atual que a Grid exibirá
   9:    /// </summary>
  10:    public int page { get; set; }
  11:    /// <summary>
  12:    /// Número total de registros na base.
  13:    /// </summary>
  14:    public int records { get; set; }
  15:    /// <summary>
  16:    /// Lista com cada registro.
  17:    /// </summary>
  18:    public List<T> rows { get; set; }
  19:  
  20:    /// <summary>
  21:    /// Construtor recebe a lista e faz a paginação virtual.
  22:    /// </summary>
  23:    /// <param name="lista">Lista com os registros</param>
  24:    /// <param name="paginaAtual">Página atual.</param>
  25:    /// <param name="totalPorPagina">Total de registros por páginas</param>
  26:    public Reader(List<T> lista,int paginaAtual, int totalPorPagina)
  27:    {
  28:  
  29:        var totalRegistros = lista.Count();
  30:        page = paginaAtual;
  31:        var ini = (page - 1) * totalPorPagina;
  32:        var fim = totalRegistros > totalPorPagina ? totalPorPagina : totalRegistros;
  33:        
  34:        if (ini > 0)
  35:        {
  36:            fim = totalPorPagina;
  37:            fim = fim - 10 >= 0 ? totalRegistros % totalPorPagina : fim;
  38:        }
  39:        var totalPags = totalRegistros / totalPorPagina;
  40:        if (totalRegistros % totalPorPagina > 0)
  41:        {
  42:            totalPags++;
  43:        }
  44:        total = totalPags;
  45:        page = page;
  46:        records = totalRegistros;
  47:        rows = lista.ToList().GetRange(ini, fim);
  48:    }
  49: }

Com os códigos acima minha Grid já está funcionando:

image

Se, por exemplo, eu mudo a quantidade de registros por página, a Grid vai passar os novos valores nos parâmetros do PageMethod, e minha pesquisa retornará a nova quantidade de registros por página:

{"_search":false,"nd":1314832264231,"rows":"20","page":1,"sidx":"PessoaID","sord":"desc"}

E se eu quiser utilizar os campos de pesquisa do JQGrid? Será que o PageMethod já está preparado? Vamos ver. Vou buscar pelo usuário de nome “Frederico” na lista da Grid, vamos ver o que a JQGrid envia para o servidor utilizar o Fiddler ou o IEDeveloperToolbar, ou qualquer ferramenta de sua vontade. Utilizando a “Lupa” da Grid e selecionando o campo na popup que aparece:

image

O que ele envia é o seguinte:

{"_search":true,"nd":1314832456208,"rows":"20","page":1,"sidx":"PessoaID","sord":"desc", "searchField":"Nome","searchString":"Frederico","searchOper":"eq","filters":""}

Veja que alguns campos foram adicionados e outros estão com novos valores, os campos que teremos que adicionar em nosso PageMethod são:

  • _seach – Indica se está sendo realizando uma pesquisa ou se é apenas a listagem do dados
  • searchField – Indica por qual campo está sendo pesquisado.
  • searchString – Indica qual é o valor pesquisado.
  • searchOper – Indica qual é o operado utilizado para a pesquisa.

Vou alterar meu PageMethod  para ficar da seguinte forma:

   1: [WebMethod]
   2: public static Reader<Pessoa> Listar(GridParams param)
   3: {
   4:     var modelo = new Modelo();
   5:     List<Pessoa> lista;
   6:  
   7:     if (param._search)
   8:     {
   9:         var operador = new Func<string,string,string>((op,valor) =>
  10:         {
  11:              int saida;
  12:              bool isNum = Int32.TryParse(valor, out saida);
  13:             switch(op){
  14:                 case "ne":
  15:                     return isNum? string.Format(" <>{0}", valor): string.Format(" <>\"{0}\"", valor);
  16:                 default:
  17:                     return isNum ? string.Format(" = {0}", valor) : string.Format(" = \"{0}\"", valor);
  18:                 }
  19:         });
  20:  
  21:         var sb = new StringBuilder();
  22:         sb.Append(param.searchField);
  23:         sb.Append(operador(param.searchOper,param.searchString));
  24:         lista = modelo.Pessoas.Where(sb.ToString()).ToList();
  25:     }
  26:     else{
  27:         lista = modelo.Pessoas.ToList();
  28:     }
  29:     
  30:     var reader = new Reader<Pessoa>(lista, param.page, param.rows);
  31:     return reader;
  32: }

Primeira mudança importante é que troquei todos os parâmetros por uma classe. A classe encapsula todos aqueles campos que a JQGrid envia para o servidor, independente do Request. Apesar de ser bem simples, segue o código:

   1: public class GridParams
   2: {
   3:     public string sidx;
   4:     public string sord;
   5:     public int page;
   6:     public int rows;
   7:     public bool _search;
   8:     public string searchField;
   9:     public string searchString;
  10:     public string searchOper;
  11: }

Criei essa classe para receber todos os parâmetros porque o Asp.Net não aceita WebServices/PageMethods com parâmetros opcionais ou com Overloads, então criei um método com apenas um parâmetro, que pode ter todos os campos definidos ou não. Visto que a JQGrid utiliza o mesmo método para listar e pesquisar, essa é a única solução.

O resto é apenas a lógica do método, que você pode mudar com a sua no lugar, a minha é só para exemplo. O segredo está em validar o parâmetro _search, se ele for true quer dizer que o usuário está realizando uma pesquisa, e não apenas carregando a lista. Para simular, criei apenas lógica necessária para o usuário usar um Campo como critério igual ou diferente do valor que for fornecido.

Com isso minha lógica já estará funcionando.

Como mudei o parâmetro do PageMethod, preciso apenas alterar um evento na configuração da Grid, para encapsular o nome do parâmetro:

   1: serializeGridData: function (dados) {
   2:    return JSON.stringify({param: dados})
   3: },

Esse evento é invocado pela Grid logo antes da requisição ser enviada para o servidor, então eu coloco os parâmetros dentro do campo param. E pronto! sua Grid está funcionando perfeitamente com Asp.Net WebForm.

Para finalizar, vamos fazer a Grid conseguir criar e alterar registros

Adicionando  e Alterando registro com JQGrid

Antes de mais nada, devo habilitar algumas configurações no Pager da grid, fazendo que o botão de adicionar e editar apareça:

   1: $("#tbGrid").jqGrid('navGrid', '#pager', { edit: true, add: true, del: false });

E alterar o ColModel para dizer como será a edição de cada campo:

   1: colModel: [
   2:    { name: 'PessoaID', index: 'PessoaID', width: 55, editable: false, editoptions: { readonly: true, size: 10} },
   3:    { name: 'Nome', index: 'Nome', width: 150, editable: true, editoptions: { size: 50} },
   4:    { name: 'Idade', index: 'Idade', width: 100, editable: true, editoptions: { size: 2 }, editrules: {integer:true} },
   5:    { name: 'Email', index: 'Email', width: 150, editable: true, editoptions: { size: 50 }, editrules: { email: true} }
   6: ],

Depois adicionar a opção para definir o caminho do PageMethod responsável por salvar o registro:

   1: editurl: "/Default.aspx/Salvar"

Também devemos alterar algumas configurações da Grid para conseguir tratar o JSON, porque infelizmente o JQuery tem um problema ao converter os dados para JSON automaticamente, então nós mesmos temos que fazer isso. Para isso, vamos alterar algumas informações padrões da Grid:

   1: $.extend($.jgrid.edit,
   2: {
   3:     ajaxEditOptions: { contentType: "application/json;charset=utf-8" },
   4:     serializeEditData: function (data) {
   5:         return JSON.stringify(data);
   6:     }
   7: });

Feito isso, não precisamos mais fazer nada no JavaScript, apenas preparar o PageMethod. que você verá que é autoexplicativo:

   1: [WebMethod]
   2: public static void Salvar(string Nome, int Idade, string Email, string oper, string id)
   3: {
   4:     var modelo = new Modelo();
   5:     if (oper == "add")
   6:     {
   7:         var pessoa = new Pessoa
   8:         {
   9:             Nome = Nome,
  10:             Idade = Idade,
  11:             Email = Email
  12:         };
  13:         modelo.Pessoas.Add(pessoa);
  14:     }
  15:     else
  16:     {
  17:         var pessoa = modelo.Pessoas.Where("PessoaID = " + id).ToList()[0];
  18:         pessoa.Nome = Nome;
  19:         pessoa.Idade = Idade;
  20:         pessoa.Email = Email;
  21:     }
  22:     modelo.SaveChanges();
  23: }

E pronto!

Com isso você já pode testar a alteração e inclusão clicando nos botões que aparecem na barra da Grid:

Alterando registro com JQGrid X WebForms

Criando registro com JQGrid X WebForms

É isso pessoal, com o que foi passado nesse post você já consegue ir muito além. Você viu que tudo é possível com a estrutura que o Asp.Net fornece para o WebForm.

Espero que isso ajude. Muitas pessoas já me perguntaram sobre JQGrid com WebForm, isso deve ajudar muita gente.

Abraços e até o próximo!

 

::Atualização

Estou publicando o exemplo do código para esclarecer as dúvidas de alguns. Qualquer problema, comente ai embaixo.

Abs.

JQGridWebForm.zip (3,04 mb)

2. setembro 2011 00:39 by Frederico B. Emídio | Comments (15) | 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. janeiro 2011 23:55 by Frederico B. Emídio | Comments (3) | Permalink

Sobre mim

Minha Imagem

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

Para saber mais clique aqui.

Páginas

Calendário

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

Visualizar posts em um Calendário
Sigua @fredemidio

MCP Asp.NET