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
$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 ) );
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
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!
Porque quando lista as cidades alguns mostram null e em seguida mostra as cidades certas ?
Olá André e obrigado pelo seu contato. Certifique-se que a sua tabela MySQL não tenha registros vazios. Abç
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.
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
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.
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
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
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?
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ç.Rogério tudo bem? e a questão do campo de estado ainda enviar com número pro banco de dados? Como resolver? Abs
Olá Rubens, desculpe não entendi sua pergunta. Poderia tentar reformulá-la? Abç.
Isto serve para worpdress também?
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ç.
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!
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.
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
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.
Não funcionou. Ele carregou o primeiro estado e cidade, mas não os outros dois.
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.
Show de bola a implementação, Rogerio, valeu!