dezembro 11, 2011

JQuery e AJAX – Combo de Estado e Cidade

Por Rogerio Coli

Essa semana precisei implementar uma solução de combo dinâmico de Estado / Cidade que carregasse as opções das cidades de acordo com a escolha do estado. Obviamente teria que usar Ajax (JQuery) e o Json (JavaScript Object Notation) que é um formato leve de troca de dados (fonte: http://www.json.org/).

Fiz uma pesquisa rápida na NET e de cara encontrei um excelente artigo de Davi Ferreira: “Populando selects de cidades e estados com AJAX (PHP e jQuery)” (http://www.daviferreira.com/posts/populando-selects-de-cidades-e-estados-com-ajax-php-e-jquery). No entanto para o meu caso a solução do Davi teria que ser reinventada tendo em vista que no meu form haviam 3 conjuntos de Estado/Cidade. Um para os dados de endereço do usuário e outros dois para os dados profissionais. Uma opção seria repetir o código 3 vezes, uma para cada conjunto, o que não é nada elegante.

A opção foi portanto, criar uma função dentro do Javascript que fizesse o trabalho sujo de forma dinâmica. Ao invés de criar uma função JS que aproveitasse o JQuery, segui a sugestão do Basil Godman, “Definindo suas próprias funções com o JQuery” (http://blogs.microsoft.co.il/blogs/basil/archive/2008/09/22/defining-your-own-functions-in-jquery.aspx) e criei uma função do próprio JQuery.

Outra coisa que fiz também foi retirar o campo de “span” estático com a mensagem “Carregando …” como sugere o Davi. Ela agora aparece de forma dinâmica dentro da própria função. Facilitando a manutenção, inclusão de em gif animado, uma classe específica e despoluindo o HTML, imagina isso para 4 ou 5 campos Estado/Cidade!

Abaixo você poderá baixar o código completo inclusive com o DDL e o DML do MySQL, além de verificar os 4 arquivos necessários para que tudo funcione comentados.

Arquivos completos para download

Arquivo de conexão com o Banco

$con = mysql_connect( 'localhost', 'root', '' ) ;
mysql_select_db( 'cadastro', $con );

O PHP e o HTML dos Selects

<?php
require("conn.open.php");

function listaEstadosOrderIdAsc(){
	return mysql_query("SELECT cod_estados, sigla FROM estados ORDER BY sigla ASC");
}
?>
<p>
  <label for="cod_estados">Estado:</label>
  <select name="cod_estados" id="cod_estados">
    <option value="">(selecione aqui)</option>
    <?php $estados = listaEstadosOrderIdAsc(); while ($row = mysql_fetch_object($estados)) { ?>
    <option value="<?php echo $row->cod_estados; ?>"><?php echo $row->sigla; ?></option>
    <?php } ?>
  </select>
  <label for="cod_cidades">Cidade:</label>
  <select name="cod_cidades" id="cod_cidades">
    <option value="">-- Escolha um estado --</option>
  </select>
</p>

Aqui a função JQuery propriamente dita, comentada para facilitar o entendimento.

$(document).ready(function(){

	// Por Rogerio Coli, https://rogeriocoli.com.br - favor não remover
	jQuery.fn.carregaCidades = function() {
		
		// Objeto que guarda os argumentos
		var args 					= arguments[0] || {};
		
		//id do Select de Cidades
		var idSelectCidade 			= args.idSelectCidade;
		
		// Página que irá criar o JSon
		var paginaPhpCidades		= 'cidades.ajax.php';
		
		// Conteúdo do elemento span que vai aparecer enquanto carregam as cidades, 
		// pode ser substituído por uma imagem. Coloque a tag completa	
		var carregandoMsg			= 'Aguarde, carregando...' 	
		
		// Classe do elemento span que vai aparecer enquanto carregam as cidades
		var carregandoClass			= 'class';
		// após as cidades carregarem aparece esta mensagem
		var jsonPrimeiroElemento 	= '(selecione a cidade)';
		// Aqui eu pego a frase do primeiro option de Cidade  
		var primeiroElemento		= $(idSelectCidade).find('option:first').html();
		
		
		if( $(this).val() ) {
			// escondendo as cidades até carregarem
			$(idSelectCidade).hide();
			// mensagem de espera: carregando
			$(idSelectCidade).after('<span class='+ carregandoClass +'>'+carregandoMsg+'</span>');
			
			$.getJSON(paginaPhpCidades+'?search=',{cod_estados: $(this).val(), ajax: 'true'}, function(j){
					// É importante que o value seja vazio pra que o formulário não seja enviado vazio
					// caso use o form validate
				var options = '<option value="">'+jsonPrimeiroElemento+'</option>';    
				for (var i = 0; i < j.length; i++) {
					// É importante que o value seja vazio pra que o formulário não seja enviado vazio
					// caso use o form validate
					options += '<option value="' + j[i].cod_cidades + '">' + j[i].nome + '</option>';
				} 
				// mostrando as cidades após carregarem e removendo a mensagem de espera
				$(idSelectCidade).html(options).show();
				$(idSelectCidade).next().remove();
			});
		} else {
			$(idSelectCidade).html('<option value="">'+primeiroElemento+'</option>');
		}
		
	};
	//Inciando o SELECT, importante ao recarregar a página
	$("#cod_estados option:first").attr('selected','selected');
	// Aqui eu chamo a função e o método que irá carregá-la
	$('#cod_estados').change(function(){ $(this).carregaCidades({idSelectCidade: '#cod_cidades'}); })
});

O Arquivo AJAX que monta o JSon

header( 'Cache-Control: no-cache' );
header( 'Content-type: application/xml; charset="utf-8"', true );

require("conn.open.php");

$cod_estados = mysql_real_escape_string( $_REQUEST['cod_estados'] );

$cidades = array();

$sql = "SELECT cod_cidades, nome
		FROM cidades
		WHERE estados_cod_estados=$cod_estados
		ORDER BY nome";
$res = mysql_query( $sql );
while ( $row = mysql_fetch_assoc( $res ) ) {
	$cidades[] = array(
		'cod_cidades'	=> $row['cod_cidades'],
		'nome'			=> htmlentities($row['nome']),
	);
}

echo( json_encode( $cidades ) );