JQuery e AJAX – Combo de Estado e Cidade

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
[php]$con = mysql_connect( ‘localhost’, ‘root’, ” ) ;
mysql_select_db( ‘cadastro’, $con );[/php]

O PHP e o HTML dos Selects
[sourcecode language=”html”]<?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>
[/sourcecode]

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

[sourcecode language=”javascript”]
$(document).ready(function(){

// Por Rogerio Coli, www.rcoli.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’}); })
});
[/sourcecode]

O Arquivo AJAX que monta o JSon

[sourcecode language=”php”]
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 ) );

[/sourcecode]

22 Comentários para “JQuery e AJAX – Combo de Estado e Cidade”

  1. Davi Ferreira

    Show de bola a implementação, Rogerio, valeu!

  2. Adriano

    N√£o funcionou. Ele carregou o primeiro estado e cidade, mas n√£o os outros dois.

  3. Rogerio Coli

    Adriano, obrigado pelo seu contato. As informa√ß√Ķes que postou n√£o s√£o suficientes para encontrar o poss√≠vel erro. Caso tenha somente um combo estado/cidade tente tamb√©m a sugest√£o do Davi Ferreira. Abra√ßo.

  4. Willian

    Estou usando seu c√≥digo para categorias e subcategorias, no lugar de estados e cidades. Ao abrir um produto, busco no banco sua categoria e subcategoria, eu consegui fazer que a categoria do produto seja selecionada automaticamente na op√ß√£o categoria, mas n√£o consigo fazer a subcategoria vir tbm. Estou aprendendo Jquery e estou quebrando a cabe√ßa e nada… rsrs

  5. Rogerio Coli

    Willian, voc√™ precisa de um evento para pr√©-selecionar a “subcategoria”, note que no meu exemplo, o combo de cidades s√≥ √© carregado no evento change do select “$(‘#cod_estados’).change(…”. Para o seu caso, precisaria que a leitura do id de categoria fosse feito tamb√©m quando a p√°gina fosse carregada (onload). Tente algo como: “$(document).ready(function(){ $(‘#cod_estados’).carregaCidades({idSelectCidade: ‘#cod_cidades’}); })”. Boa Sorte.

  6. Carlos Souza

    Grande Rogério,

    Estou usando o wamp (v2.2) e o código não rodou perfeitamente.
    Aparecem os selects a ao lado o seguinte:
    $row[‘cod_cidades’], ‘nome’ => $row[‘nome’], ); } echo( json_encode( $cidades ) );

    Preciso instalar alguma biblioteca adicional?

    Tentei o Código do Davi e aconteceu o mesmo.
    Vlw!

  7. Rogerio Coli

    Carlos, n√£o h√° necessidade de instalar nenhuma biblioteca adicional. Fica dif√≠cil de definir o problema somente com o mencionado. Mas se ao lado do select de cidades aparece isso na TELA √© porque o php n√£o est√° sendo interpretado. Pode ser uma erro na abertura e fechamento da tag. Algumas instala√ß√Ķes s√≥ permitem a abertura dessa forma: < ?php ?>, j√° outras permitem a omiss√£o da sigla, ficando desta forma: < ? ?>. Pode ser um monte de coisas. V√° por partes, primeiro tente acessar diretamente a p√°gina de cidades passando a vari√°vel GET pela URL e veja se est√° rodando corretamente o XML. Tem que ir eliminando os erros aos poucos. Abra√ßo.

  8. Roberto

    Isto serve para worpdress também?

  9. Rogerio Coli

    Olá Roberto e obrigado pelo seu contato. Nunca implementei essa solução no WordPress mas imagino que deva funcionar sim, uma vez que é em PHP/MySQL. No entanto, para isso acredito que deva precisar de mexer no core do seu código ou mais elegante, instalar um plugin que permita a execução de scripts PHP no campo descrição dos seus posts. Espero ter ajudado. Abç.

  10. Rubens Monteiro

    Rog√©rio tudo bem? e a quest√£o do campo de estado ainda enviar com n√ļmero pro banco de dados? Como resolver? Abs

  11. Rogerio Coli

    Olá Rubens, desculpe não entendi sua pergunta. Poderia tentar reformulá-la? Abç.

  12. Danilo Rocha

    Muito bom, Rog√©rio, o √ļnico problema √© que ao dar refresh na pagina, a sele√ß√£o da cidade √© perdida, e n√£o habilita novamente para sele√ß√£o… Na pr√°tica, se antes de submeter o formul√°rio o sistema fizer alguma valida√ß√£o, e em seguida retornar para o formul√°rio, as informa√ß√Ķes selecionadas n√£o estar√£o l√°. como corrigir isso?

  13. Rogerio Coli

    Ol√° Danilo e obrigado pelo contato. No exemplo que citou, eu consigo pensar neste momento em duas solu√ß√Ķes. A primeira e mais f√°cil √© zerar o dois combos toda vez que a p√°gina for atualizada. Neste caso, toda vez o usu√°rio recarregasse a p√°gina, teria que escolher novamente o estado e a cidade. A segunda, mais complicada, seria resgatar o $codigo_estados e o $codigo_cidades enviados pelo POST inicial e ao recarregar a p√°gina, efetuar no atributo onload do body () a requisi√ß√£o ajax passando o $codigo_estados que ir√° recarregar as cidades do estado escolhido. Feito isso, temos que criar um novo m√©todo JQuery que selecione a cidade de acordo com $codigo_cidades resgatado. Espero que isso ajude. Depois passe aqui para informar como resolveu. Ab√ß.

  14. Antonio

    Olá Rogério, sou novo em programacao e to apanhando muito cara, tenho a mesma situacao só que o banco é o postgres tem como tu me passar o exemplo para esse bd?
    utilizo cake php como Framework

  15. Rogerio Coli

    Ol√° Ant√īnio e obrigado pela sua colabora√ß√£o. Segue a URL para download. Abra√ßo. http://www.rcoli.com.br/wp-content/uploads/2011/12/cidades.zip

  16. Laercio

    Ol√° Rog√©rio …o exemplo que voc√™ deixou para download n√£o funciona de jeito nenhum…
    A pagina carrega as combobox mas nem a select do Estado carrega, dando a impressão que o banco não esta conectado, mas não é o caso.

  17. Laercio

    Oi Rogerio complementando, parece que o require não estava realmente funcionando , nao sei pq , coloquei as linhas de conexao no index e o mesmo rodou, carregou todas as combobox dos estados , mas a função só funciona no primeiro combobox ou seja escolho o estado e carrega as cidades somente do primeiro combo.

  18. Rogerio Coli

    Olá Laércio e obrigado pelo seu contato. Me parece que pode ser algo relacionado aos ids dos campos no formulário. Verifique se eles estão com o mesmo valor

  19. André Oliveira

    Porque quando lista as cidades alguns mostram null e em seguida mostra as cidades certas ?

  20. Rogerio Coli

    Olá André e obrigado pelo seu contato. Certifique-se que a sua tabela MySQL não tenha registros vazios. Abç

  21. Danilo

    Ol√° Rog√©rio, desculpe a ignor√Ęncia, estou implementando algo semelhante para meu projeto da faculdade, e estou tendo dificuldades em entender em qual trecho do c√≥digo que voc√™ da um “GET” no valor selecionado do combo do estado.

    Abraços , obrigado!

  22. David

    Pessoal, usei os arquivos do site, mas n√£o deu certo, fiz umas altera√ß√Ķes nos codigos abaixo e deu certo.

    Vejam o resultado:
    http://www.cuponero.com.br/cidades/

    PS: Usei meu proprio banco de dados pois o que está aí nao esta separado por colunas, com isso ele nao permite alteracoes. Eu tenho o banco completinho inclusive com os DDDS das cidades, o CEP não inclui ele por falta de tempo, mas numa outra oportunidade vai.

    Os codigos que mexi foram estes (dentro de cidade.ajax.php)
    ‘cod_cidades’ => (utf8_encode($row[‘nome’])),
    ‘nome’ => (utf8_encode($row[‘nome’])),

    //// Notem que eu tirei o codigo que estava antes e coloquei este que estava l√° no outro blog, assim funcionou certinho.

    Não sou programador, por isso levei muito mais tempo, mas valeu pelas dicas aí, elas que solucionara.

    Abs

Comente este artigo!