Front End

13 fev, 2007

Paginação e Ordenação com Ajax

Publicidade

Olá amigos. Nosso objetivo neste artigo será estudar AJAX com aplicação de XML, PHP e DOM. Teremos um arquivo para paginação de resultados de consulta de banco de dados. Um modelo que procurei na internet, mas não encontrei.

Mas vamos ao que interessa! Nesse exemplo estou usando sete arquivos. Gosto muito da boa prática de programação por módulo.

Os arquivos são:

Index.php (Onde será mostrada nossa listagem);
Listagem.php (Responsável por fazer a consulta no banco de dados e gerar o arquivo XML de retorno);
Includes/conexão.php (Faz a conexão com o banco de dados);
Includes/paginação.php (Responsável pelo processo de paginação);
Includes/útil.js (Contem algumas utilidades);
Includes/ajax.js (script para o ajax);
Includes/funcoes.js (O mais importante, contêm todas as funcionalidades para o ajax);
Sql.txt (Arquivo extra – Script do banco de dados).

Vou detalhar os arquivos:

index.php

<HTML>
<HEAD>
 <TITLE>Documento PHP</TITLE>
 <SCRIPT type="text/javascript" src='includes/ajax.js'> </SCRIPT>
 <SCRIPT type="text/javascript" src='includes/funcoes.js'> </SCRIPT>
 <SCRIPT type="text/javascript" src='includes/util.js'> </SCRIPT>
</HEAD>
<BODY onLoad="javascript:listar(1)">
<h2>Paginação e Ordenação com Ajax (XML e JavaScript)</h2>
<div id="carregando" style="font:Verdana; color:#FFFFFF; background-color:#DD0000; width:180px;">
  Carregando...
</div>
<br>
  <table border="0">
    <tr>
	  <td>Resgistro por página?</td>
	  <td>
	    Ordernar por?
	  </td>
	</tr>
  <form>
	<tr>
	  <td>	
	    <select id="regPag" onChange="javascript:listar(1);">
	      <option value="1">01 registro  por página</option>
	      <option value="2">02 registros por página</option>
	      <option value="3">03 registros por página</option>
	      <option value="4">04 registros por página</option>
	      <option value="5">05 registros por página</option>
	      <option value="6">06 registros por página</option>
	      <option value="7">07 registros por página</option>
	      <option value="8">08 registros por página</option>
	      <option value="9">09 registros por página</option>
	      <option value="10" selected="selected">10 registros por página</option>
	      <option value="20">20 registros por página</option>
	      <option value="30">30 registros por página</option>
	      <option value="50">50 registros por página</option>
	    </select>
	  </td>
	  <td>
	    <select id="ordenacao" onChange="javascript:listar(1);">
	      <option value="1" selected="selected">ID Ascendente</option>
	      <option value="2">ID Descendente</option>
	      <option value="3">NOME Ascendente</option>
	      <option value="4">NOME Descendente</option>
	    </select>
	  </td>
	</tr>
  </form>
  </table>  
  <br>
  <table id="tabelaPrincipal" border="1" cellpadding="0" cellspacing="0">
    <tr>
      
    </tr>
  </table>
  <table id="tabelaPaginas" border="0" width="200" cellpadding="0" cellspacing="0">
    <tr align="center">
      <td>  <a style="display:none" id="pri" href="">Primeiro</a>   </td>
	  <td>  <a style="display:none" id="ant" href="">Anterior</a>   </td>
	  <td>  <a style="display:none" id="pro" href="">Proximo </a>   </td>
	  <td>  <a style="display:none" id="ult" href="">Ultimo  </a>   </td>
    </tr>
  </table>
</BODY>
</HTML>

Linhas (4 a 6) – Importação dos arquivos necessários para o funcionamento.
Linhas (10 a 12) – Div onde irá aparecer a mensagem “Carregando…”.
Linhas (24 a 38) – Um campo select para a escolha da qtd de registro por página.
Linhas (41 a 46) – Um campo select para a escolha do tipo de ordenação.
Linhas (52 a 56) – Tabela onde irá ser exibido os registros.
Linhas (57 a 64) – Tabela onde irá ser exibido os texto “Primeiro Anterior Proximo e Ultimo”, para a paginação.

listagem.php

<?
   //include do arquivo conexao.php para coneção com o BD
   include("includes/conexao.php");
   //SQL para a pesquisa no BD
   $sql = "select ID, NOME from cidades where ID <= 30";
   
   if($ordenacao == 1){
     $sql = $sql." order by ID";
   }else if ($ordenacao == 2){
     $sql = $sql." order by ID DESC";
   }else if ($ordenacao == 3){
     $sql = $sql." order by NOME";
   }else if ($ordenacao == 4){
     $sql = $sql." order by NOME DESC";
   }
   //Include do arquivo paginacao.php Responsável pelo processamento da paginação
   include("includes/paginacao.php");
   //$limite contem a quantidade de registro por página definido no index.php pelo usuário
   $qtd = mysql_num_rows($limite);
   // Inicio da montagem do XML
   Header("Content-type: text/xml");   
   $xml  = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
   $xml .= "<cidades>\n";               

   while(list($id,$nome) = mysql_fetch_row($limite)) {  
	  $xml .= "<cidade>\n";     
      $xml .= "<id>".$id."</id>\n";                  
      $xml .= "<nome>".$nome."</nome>\n";         
      $xml .= "</cidade>\n";    
   }                
   
   $xml.= "<totalReg>$totalRegistros</totalReg>";
   $xml.= "<pagAtual>$pagAtual</pagAtual>";
   $xml.= "<numPag>$numPaginas</numPag>";
   $xml.= "<ordenacao>$ordenacao</ordenacao>";
   $xml.= "</cidades>\n";
   //Fim da montagem do XML
   
   //Exibição do XML, isso não ficará visível para o usuário
   echo $xml;
?>

Nesse arquivo temos a consulta no banco e a geração do arquivo XML que será preciso para ser exibido no index.php

conexao.php

<?
   $conn = @mysql_connect("localhost", "root", "");

   if (!$conn)
     die("Erro ao conectar com MySQL: " . mysql_error());

   if (!mysql_select_db("ajax", $conn))
     die("Erro ao selecionar o banco: " . mysql_error());

?>

Nesse arquivo não tem nenhum mistério.

paginacao.php

<?
   // Números de Registros por página
	$registrosPorPagina = $regPorPag; 
	
	if(empty($registrosPorPagina)){
	  $registrosPorPagina = 5;
	}

	// Validação para não exibir a página 0
	if (empty($pagina)):    $pagAtual = 1; 	else:    $pagAtual = $pagina; endif;

	// Retorna os "$registrosPorPagina" até o número passado por "$pagina"
	$inicio = $pagAtual - 1; 
	$inicio = $inicio * $registrosPorPagina; 

	// Pesquisa da página atual
	$limite = mysql_query("$sql LIMIT $inicio,$registrosPorPagina"); 

	// Número total de registros da tabela
	$totalRegistros = mysql_num_rows(mysql_query("$sql")); 

	// verifica o número total de páginas 
	$numPaginas = $totalRegistros / $registrosPorPagina;
?>

util.js

//Receberá um elemento e substituirá todo o texto desse elemento pelo texto fornecido
function substituirTexto(elemento, texto) {
  if (elemento != null) {
	//usado para limpar todos os filho existente do elemento
    limparTexto(elemento);
    //<!-- Usamos para criar um novo nó texto e depois o acrescentamos aos nós filhos do elemento
	var newNode = document.createTextNode(texto);
    elemento.appendChild(newNode);
	//-->
  }
}

//Removerá todos os nós filhos do elemento que vc informar
function limparTexto(elemento) {
  if (elemento != null) {
    if (elemento.childNodes) {
      for (var i = 0; i < elemento.childNodes.length; i++) {
        var childNode = elemento.childNodes[i];
       //removerá toidos os nós filhos.
		elemento.removeChild(childNode);
      }
    }
  }
}

ajax.js

 //Inicio da verificação se o navegador suporta AJAX
   function ObjAjax() {
	  try {
		//Usado por algumas versões do IE
		 ajax = new ActiveXObject("Microsoft.XMLHTTP");
      } 
      catch(e) {
         try {
            //Usado pela maioria das versões do IE
			ajax = new ActiveXObject("Msxml2.XMLHTTP");
         }
	     catch(ex) {
            try {
               //Funciona no Safari, FireFox, Mozila, Opera e 
			   //a maioria dos navegadores que não são Microsoft
			   ajax = new XMLHttpRequest();
            }
	        catch(exc) {
               //Navegador sem suporte a AJAX
			   alert("Navegador não suporta AJAX");
               ajax = null;
            }
         }
      }
   }
   //Fim da Verificação

Criar o objeto a ser utilizado o AJAX.

funcoes.js

//Função responsavel por fazer a listagem dos registros.
   function listar(pagina){
     //criar o objAjax
	 ObjAjax();
	 if(ajax) {
	   //Pego a quantidade de resgistro por página definido pelo usuário no index. 
	   var qtdRegPorPag = document.getElementById('regPag').value;
	   //Pego o tipo de ordenação definido pelo usuário no index.
	   var ordenacao = document.getElementById('ordenacao').value;
	   
	   //Definir URL para onde devemos fazer a listagem passando a página que queremos.
	   //Definir Parametros onde devemos passar junto a url a pagina atual, qtd de registro por página, ordenação e
	   //dummy uma variavel criada para evitar que o IE guarde em cache a navegação com isso essa variavel "nunca" será igual.
	   var url = "http://localhost/ajax/listagem.php";
	   var parametros = "?pagina="+pagina+"®PorPag="+qtdRegPorPag+"&ordenacao="+ordenacao+'&milisegundos='+new Date().getTime(); 
	   ajax.open("GET", url+parametros, true);
	   //Enquanto o processa a listagem mostra para o usuário a mensagem "carregando".
	   var divCarregando = document.getElementById("carregando");
	   divCarregando.style.display = 'block';
	   //invoco a função paginar
	   ajax.onreadystatechange = paginar;
       ajax.send(null);
	 }
   }

Esse trecho acima utiliza o objeto AJAX para enviar para o servidor assincronicamente. Isso está explicito na linha 16 (3º parâmetro).

function paginar(){
	 //Verifico se o servidor concluiu a solicitação.
	 if (ajax.readyState == 4) { 
	   //Informa o código de status do Servidor. "200" = OK
	   if(ajax.status == 200){
	    //valor guarda a resposta do servidor, que nesse caso é o XML gerado pelo arquivo listagem.php
		 var valor = ajax.responseXML;
		 
	     var pagAtual = valor.getElementsByTagName("pagAtual")[0].firstChild.nodeValue;//pagAtual = página atual
	     var totalReg = valor.getElementsByTagName("totalReg")[0].firstChild.nodeValue;//totalReg = qtd total de registro do SQL
	     var numPag   = Math.ceil(valor.getElementsByTagName("numPag")[0].firstChild.nodeValue);// numPag = qtd de páginas
	   
	     if (totalReg == 0){
		   //caso entre aqui é pq não existe registro o banco, com isso eu "desabilito" os dois select,
		   //exibo a mensagem "Nenhum registro encontrado" e
		   //apago todo o conteúdo da tabela que exibi os registros.
	       document.getElementById('ordenacao').disabled = true;
		   document.getElementById('regPag').disabled = true;
		   divCarregando = document.getElementById("carregando");
	       substituirTexto(divCarregando, "Nenhum registro encontrado");
		   if(document.getElementById('tabelaPrincipal').rows.length>1){
		     document.getElementById('tabelaPrincipal').deleteRow(0);
		     document.getElementById('tabelaPrincipal').deleteRow(0);
		   }

}else{
           //caso entre aqui é pq existe registro o banco, com isso eu "habilito" os dois select.
		   document.getElementById('ordenacao').disabled = false;
		   document.getElementById('regPag').disabled = false;
		   //dataArray = guarda uma matriz com todas informações do xml que pertencer a tag <estado></estado>
		   var dataArray   = valor.getElementsByTagName("cidade");
	       //Utilizado para Limpar a tabela que será usada para mostrar os resgistros
	       while(document.getElementById('tabelaPrincipal').rows.length>0){
		     document.getElementById('tabelaPrincipal').deleteRow(0);
	       }
	       
		   //definição do tamanho das tabelas:
		   //  "tabelaPrincipal" que é usada para mostrar os resgistros
		   //  "tabelaPaginas" que usada para paginar.
	       document.getElementById("tabelaPaginas").width="320";
	       document.getElementById("tabelaPrincipal").width="320";
	   
	       //Inicio da escrita da tebela com o resultado
		   
		   //isso gera uma linha com duas colunas e com conteúdo ID e NOME respectivamente
	       var x=document.getElementById('tabelaPrincipal').insertRow(0);
           var y=x.insertCell(0);
           var z=x.insertCell(1);
           substituirTexto(y, 'ID');
	       substituirTexto(z, 'NOME');
           
           //Início do processo da paginação propriamente dita
           //quarda a página anterior
		   var anterior = (pagAtual - 1); 
		   //guarda a página posterior
	       var proximo  = (Number(pagAtual) + 1);
		
	       if ((totalReg % dataArray.length!=0)){
		     while(totalReg % dataArray.length!=0){
		       totalReg++;
		     }
	       }
		
	       if (pagAtual > 1) {
		     //Exibi o texto com o link Primeiro e Anterior, caso a pagina atual seja > 1
			 document.getElementById("pri").style.display = 'block';
		     document.getElementById("ant").style.display = 'block';
		     document.getElementById("pri").href="javascript:listar(1)";
		     document.getElementById("ant").href="javascript:listar("+anterior+")";
	       } else { 
		     //Esconde o texto com o link Primeiro e Anterior, caso a página atual seja < 1
			 document.getElementById("pri").style.display = 'none';
		     document.getElementById("ant").style.display = 'none';
		     document.getElementById("pri").href="javascript:listar(1)";
		     document.getElementById("ant").href="javascript:listar("+anterior+")";
	       }

	       if (pagAtual < numPag) {
		     //Exibi o texto com o link Proximo e Ultimo, caso a pagina atual seja < qtd total de páginas
			 document.getElementById("pro").style.display = 'block';
		     document.getElementById("ult").style.display = 'block';
		     document.getElementById("pro").href="javascript:listar("+proximo+")";
		     document.getElementById("ult").href="javascript:listar("+numPag+")";
	       } else {
		     //Esconde o texto com o link Proximo e Ultimo, caso a pagina atual seja >= qtd total de páginas
			 document.getElementById("pro").style.display = 'none';
		     document.getElementById("ult").style.display = 'none';
		     document.getElementById("pro").href="javascript:listar("+proximo+")";
		     document.getElementById("ult").href="javascript:listar("+numPag+")";
	       } 
           //Laço para ir exibindo os resgistro linha a linha
	       for(var i = 0 ; i < dataArray.length ; i++) {
             var item = dataArray[i];
		     //contéudo dos campos no arquivo XML
		     var id   =  item.getElementsByTagName("id")[0].firstChild.nodeValue;
		     var nome =  item.getElementsByTagName("nome")[0].firstChild.nodeValue;
		     
			 //será exibido sempre no fim da tabela
			 var posicaoFimTabela = document.getElementById('tabelaPrincipal').rows.length;
	         var x=document.getElementById('tabelaPrincipal').insertRow(posicaoFimTabela)
             var y=x.insertCell(0);
             var z=x.insertCell(1);
             y.width = 30;
			 y.align = 'center';
			 substituirTexto(y, id);
			 z.width = 290;
		     substituirTexto(z, nome);
	       }
		   // Fim do processo da paginação propriamente dita
		   
		   //LimparTexto = função usado para limpar o texto e usada tb para a função substituirTexto
	       var divCarregando = document.getElementById("carregando");
	       divCarregando.style.display = 'none';
	     }
	   }
	 }
   }

Acima está o principal trecho, é responsável pela paginação do resultado.

*sql.txt*

CREATE TABLE `cidades` (
  `ID` int(10) unsigned NOT NULL auto_increment,
  `NOME` varchar(50) default NULL,
  PRIMARY KEY  (`ID`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;

INSERT INTO `cidades` (`ID`, `NOME`) VALUES
(1, 'Recife'),
(2, 'Olinda'),
(3, 'São Paulo'),
(4, 'Maceió'),
(5, 'João Pessoa'),
(6, 'Natal'),
(7, 'Rio de Janeiro'),
(8, 'Teresina'),
(9, 'Salvador'),
(10, 'Fortaleza'),
(11, 'São Luiz'),
(12, 'Osasco'),
(13, 'Manaus'),
(14, 'Paulista'),
(15, 'Jaboatão dos Guararapes'),
(16, 'Caruaru'),
(17, 'cidade 17'),
(18, 'cidade 18'),
(19, 'cidade 19'),
(20, 'cidade 20'),
(21, 'cidade 21'),
(22, 'cidade 22'),
(23, 'cidade 23'),
(24, 'cidade 24'),
(25, 'cidade 25'),
(26, 'cidade 26'),
(27, 'cidade 27'),
(28, 'cidade 28'),
(29, 'cidade 29'),
(30, 'cidade 30')

Observações

Existem algumas melhorias que podem ser feitas como, por exemplo: pode-se substituir o campo select com as quantidades de registro por página por uma caixa de texto para o usuário ficar livre de escolher quantos registro por página ele quer, porém não esquecer de validar a entrada de dados do mesmo.

Essa é minha primeira matéria no iMasters e espero que seja apenas a primeira de muitas. Um grande abraço a todos e até a próxima.

Faça o download do arquivo.