Rails: Alterando as OPTIONs dos SELECTs
Publicado em Developer
Ontem estava dando uma mão à noite para a minha esposa, que é webdesigner e está aprendendo a programar agora, adivinhem, com Rails (e adivinhem quem é o suporte-técnico-oficial-24-horas-VIP dela, tô ferrado) e ontem ela teve um probleminha para preencher alguns SELECTs. Vou descrever uma situação parecida com a que ela teve:
- Tenho 3 SELECTs, um para país, outro para estado e outro para cidade;
- A sequência lógica está toda aí: seleciona-se o país, o SELECT do estado é preenchido, seleciona-se o estado, e as cidades são preenchidas;
- Usei observe_field para país e estado, indicando os controladores e ações correspondentes;
Até aí, tudo bem, mas surgiram uns probleminhas:
- Eu dei uma boa pesquisada e não achei uma maneira de alterar somente as OPTIONs do SELECT, o que a turma anda recomendando é ou usar replace_html e trocar tudo do elemento onde o SELECT se encontra, (nesse caso achei que ia complicar um pouco o layout) ou usar replace para trocar o próprio elemento, ou seja, criar um novo SELECT com esse método.
- Com qualquer uma das opções acima, o observe_field ficava maluco. Não é por menos, removemos e inserimos o elemento que ele estava "vigiando", e ele para de procurar as cidades do estado no momento que removemos e inserimos o elemento de estado com os valores retornados pela consulta por país.
Vou tentar ilustrar um pouco o problema e a solução com código (onde juntei tudo em um lugar só apenas para fins didáticos, separem seus controladores certinho!). Gerei um controlador chamado Geral com as ações (métodos, dã!) estados, cidades e novo, e um monte de hashes e arrays para fazer uma simulação de dados vindo do banco de dados.
A jogada que fiz foi usar o método call, que chamada uma função JavaScript na página que requisitou a chamada AJAX. Essa função (porcamente inserida ali dentro da ação, para efeito do exemplo - se forem usar a coloquem em um arquivo .js) recebe o id do SELECT que queremos atualizar e o array com os dados a serem inseridos, faz um "limpa" no SELECT e adiciona as OPTIONs novas criadas a partir dos valores do array enviado. O controlador fica assim:
class GeralController < ApplicationController def estados @paises_estados = {1=>[['SP',1],['MG',2]],2=>[['J1',3],['J2',4]], 3=>[['C1',5],['C2',6]]} render :update do |page| page.call 'fill', 'estados', @paises_estados[params[:id].to_i] page.call 'fill', 'cidades', [] end end def cidades @estados_cidades = {1=>[['Mirassol',1],['Rio Preto',2]], 2=>[['Belo Horizonte',3],['Divinópolis',4]],3=>[['Tokio',5]], 4=>[['Osaka',6]],5=>[['Alberta',5]],6=>[['Vancouver',6]]} render :update do |page| page.call 'fill', 'cidades', @estados_cidades[params[:id].to_i] end end def novo @paises = [['Brasil',1],['Japão',2],['Canadá',3]] end end
Ali no controlador tem uma jogada de enviar um array vazio de cidades toda vez que são retornados novos estados, evitando a inconsistência de ficar com as cidades selecionadas anteriormente preenchidas. E a view novo, já com a função fill, fica assim:
<script type="text/javascript"> function fill(id,val){ var select = $(id); var length = select.length; for(var i=0; i<length; i++) select.remove(0); if(val.lenght<=0) return; select.appendChild(document.createElement("option")); val.each(function(opt){ var option = document.createElement("option"); option.text = opt[0]; option.value = opt[1]; select.appendChild(option); }) } </script> <form> <p> <label for="paises">Países</label> <select id="paises"> <option></option> <%= options_for_select(@paises) %> </select> </p> <p> <label for="estados">Estados</label> <select id="estados"> </select> </p> <p> <label for="cidades">Cidades</label> <select id="cidades"> </select> </p> <p id="status"></p> <%= observe_field 'paises', :url=>{:action=>'estados'}, :with=>'id' %> <%= observe_field 'estados', :url=>{:action=>'cidades'}, :with=>'id' %> </form>
Eu procurei no Prototype e não vi muita coisa para se lidar com SELECTs, por isso fiz algumas coisas meio "na unha" ali na função, como apagar os elementos e criar as OPTIONs, se alguém souber um jeito direto no Prototype me envie um email que eu atualizo o código. Estava muito tarde ontem e eu não estava com saco de ficar fuçando demais na API do Prototype não (apesar de ter melhorado bastante, ainda fica devendo uma busca, tem muita coisa lá).
Com essa solução evitei ficar removendo e inserindo o SELECT que quero popular e evitando quaisquer problemas relacionados à isso.
Comentários
Comentários fechados.
Sem nenhum comentário.
Artigos anteriores
- Pull requests em modo raiz - sex, 22 de dezembro de 2023, 09:57:09 -0300
- Qual a idade do seu repositório? - ter, 27 de dezembro de 2022, 12:50:35 -0300
- Utilizando ctags em projetos Rails mais recentes - qui, 24 de junho de 2021, 08:23:43 -0300
- Fazendo o seu projeto brotar - seg, 15 de julho de 2019, 08:57:05 -0300
- Learn Functional Programming with Elixir - sex, 02 de março de 2018, 18:47:13 -0300
- Ambiente mínimo - Driver Driven Development - qua, 23 de agosto de 2017, 15:15:03 -0300
- Ambiente mínimo - repositórios de código - dom, 16 de abril de 2017, 13:02:14 -0300
- Ambiente mínimo - terminal e navegador - dom, 02 de abril de 2017, 21:43:29 -0300
- Utilizando muitas gems no seu projeto? - sáb, 29 de outubro de 2016, 11:57:55 -0200
- Desenvolvedores e inteligência artificial - seg, 11 de julho de 2016, 09:09:38 -0300