Exibir mais resultados com VRaptor + Ajax

32 respostas
A

Boa tarde, pessoal.

Estou tentando desenvolver um paginador no mesmo estilo do Twitter para minha aplicação, exibindo os 100 primeiros resultados por vez.
Pesquisei no forum, em sites e não encontrei nenhuma solução que fizesse da forma que preciso.

Então tentei realizar da seguinte forma:

Os parametros que utilizarei para paginar são limit + offset (no banco de dados).

No meu Controller, criei o seguinte método

public List<Pessoa> nome(String nome, Integer cidade, Integer offset) { // este offset SEMPRE será zero, pois se trata do método da primeira busca
	result.include("nome", nome);
	result.include("cidade", cidade);
	return pessoaRepository.buscaNome(nome, cidade, offset);
}

Ou seja, simplesmente entrego para o repository os parametros de nome, cidade e o offset 0. E ele me retorna a lista perfeitamente.

Pensei em incluir os parametros que utilizei anteriormente na página onde seriam exibidos os resultados, pois daí poderia utilizar um form escondido com inputs do tipo “hidden” com os valores que eu preciso para fazer a nova busca, claro que caso se o tamanho da lista mostrada fosse igual a 100. Ou seja, teria de ter um formulario mais ou menos assim:

<form method="post" action="/mais">
 <input type="hidden" name="nome" value="${ cidade }" />
 <input type="hidden" name="cidade" value="${ cidade }" />
 <input type="hidden" name="offset" value="100" />
 <button type="submit">Mais resultados</button>
</form>

inicialmente, o valor do offset seria 100. Javascript poderia cuidar de incrementar esse valor no futuro.

E no meu Controller, seria mais ou menos assim:

@Post @Path("/mais")
public void maisResultados(String uf, String nome, Integer cidade, Integer offset){
	List<Pessoa> pessoaList = pessoaRepository.buscaNome(nome, cidade, offset);
	result.use(Results.json()).from(pessoaList).serialize(); //é assim que se envia para a página a lista?
}

Então vamos para as minhas dúvidas:

A solução que eu utilizei está correta? ela tem brechas para injeção de sql ou algo do tipo?

Como eu faria para aplicar estes resultados na minha página? eu pesquisei e só encontrei soluções em php. Alguém já viu alguma solução dessas para java?
A única que entendi mais ou menos foi essa:

Agora não estou conseguindo adaptá-la para o meu código. Como faria para esse $.ajax({}); jogar as informações na div #conteudo e incrementar o valor do campo offset no formulário do botão “mais”?

Muito obrigado pessoal :wink:

32 Respostas

Lucas_Cavalcanti

faça algo parecido com:

<form id="mais" method="post" action="/mais">
      ....
     <input type="hidden" id="offset" name="offset" value="100" />  
</form>

e no javascript:

$('#mais').submit(function() {
    $.getJSON($(this).action(), $(this).serialize(), function(json) {
           //itera no json e joga as informações em #conteudo
    });
    $('#offset').val(parseInt($('#offset').val()) + 10);
    return false; //pro form não submeter
});
A

Opa cara, obrigado aí pela dica.

Fiz exatamente como você me falou, mas mesmo com o return false do javscript, o form está submetendo do mesmo jeito para a url. Como se tentasse achar a página “maisResultados.jsp”.

Será que tem outra forma de se fazer isso?

EDIT: Este erro seria pois estou usando o método com @Post? Estou utilizando post por causa da segurança.

Obrigado.

Lucas_Cavalcanti

se vc usa post, vc tem que usar o $.ajax mesmo (ou $.post)

post não te dá segurança, por assim dizer. Qualquer um consegue fazer um post, vc só dificultou um pouquinho, pois eles não vão conseguir fazer direto no browser :wink:

semanticamente vc deveria usar GET mesmo (a menos que vc mude algo no servidor)

de qqer modo, vc pode usar isso pra post:

$.ajax({
    url: $(this).action(),
    data: $(this).serialize(),
    dataType: "json",
    method: "post",
    success: function(data) {
          ...
    }
});
A

Lucas,

Conforme você sugeriu, troquei o método do formulário para GET mesmo e consegui fazer funcionar :D
O único problema é que não consigo jogar a requisição para a div mesmo (problema de javascript, creio eu).

Ele direciona para o resultado da lista (como se eu acessasse o método DIRETO pela url), ignorando o return false.

Olha o trecho de código que eu estou usando para tentar alimentar a minha div:
$('#mais').submit(function() {
	$.ajax({
		url: $(this).action(),
		data: $(this).serialize(),
		dataType: "json",
		success: function(data) {
			var lista = data.list;
			for(var i in lista) {
				var pessoa = $('<p>' + lista[i].codigo + ' - ' + lista[i].nome + '</p>');
				pessoa.appendTo('#conteudo');
			}
		}
	});

    $('#offset').val(parseInt($('#offset').val()) + 100);
    return false;
});

Eu sei que é dúvida de javascript, mas tem algo de errado aí? Obrigado!

Lucas_Cavalcanti

tá entrando nessa function? espalha uns console.log ou alert aí dentro pra ver se está funcionando.

é possível que tenha um erro na linha 15, daí ele não chega a executar o return false

A

Cara, tava dando tudo errado, aí resolvi utilizar os parâmetros dentro da url, da seguinte forma:

Arranquei o form, deixei só um Anchor mesmo. E coloquei assim:

$('#mais').click(function(e) {
	$.ajax({
		url: '/mais?offset=100&nome=MARIA&cidade=0',
		dataType: 'json',

E deu tudo muito certo! Obrigado mesmo :smiley:

Mas aí vem a dúvida. Eu posso restringir esta url (método get) para que apenas usuário logados possam ver, utilizando um controle de acesso, certo?

Lucas_Cavalcanti

sim, vc pode colocar um interceptor de controle de acesso que passe pela lógica do /mais também.

Spidey

Eu estou fazendo algo parecido e veio a dúvida: e se eu quisesse utilizar o mesmo método do meu controller para gerar o JSON e o HTML? Digo, a primeira solução foi muito boa, porque se o JS estiver desligado, o form funciona “normal” e você poderia fazer ele redirecionar para os 100 próximos resultados em HTML, e se estiver ligado você dá um “override” no onSubmit() do form e força o pedido por JSON da mesma página.

Minha dúvida é: como, lá no método do meu controller, eu diferencio um pedido do outro? Pra eu poder fazer algo como

if (json){

result.use(Results.representation()).from(meuResultado).serialize();

return;

} //else -> chama o meu_motodo.jsp normalmente
Lucas_Cavalcanti

se vc chama:

result.use(representation()).from(meuResultado).serialize();

e o formato for html, ele chama a jsp, incluindo o meuResultado na response

A

Lá venho com mais uma dúvida.

Minha lista está funcionando perfeitamente. Sendo que quando recebo a lista, preciso verificar se o valor está nulo, para então decidir se exibo ou não o rotulo referente a essa informação. O que faço é o seguinte:

<c:if test="${ cliente.telefone != null }">
   <p><span class="rotulo">Telefone:</span>${ cliente.telefone }</p>
</c:if>

Como pode ver, muito fácil de se fazer com jstl.

Agora como se fazer isso com a lista do json? tenho como manipulá-la de outra forma, ou só pelo Javascript, como estou fazendo neste trecho de código? Que verifico se o CPF / CNPJ do cliente está nulo, para, aí sim, exibí-lo na tela

success: function(json) {
   var lista = json.list;
   for(var i in lista) {
      var topo = $('<div class="assinante">');
      if (lista[i].cpfCnpj != null) { var var_cpfCnpj = $('<p>' + lista[i].cpfCnpj + '</p>'); }
      var rodape = $('</div>');
      var cliente = topo + var_cpfCnpj + rodape;
      cliente.appendTo('#resultado');
   }
}

Aliás, quando peço para retornar as 3 vars em uma só, dá erro, alguém sabe porque isso acontece? Quando eu ponho na var cliente apenas um dos 3 campos, da certo.

Obrigado mais uma vez.

Spidey
Tem um tempinho que nao mexo com JS, mas se essa forma de for, estilo foreach, funciona em JS, a sua variavel i jah esta recebendo cada item.

Ou seja, ou faz:

for(i=0; i<lista.size(); i++){

var item = lista[i];

}

ou

for(var item in lista){

//item jah eh meu elemento

}
A

Entendi! essa forma já está iterando e já está tudo ok, minha dúvida era se havia alguma forma que não utilizasse Javascript, mas pelo visto não há.

Preciso agora saber como resolver o problema que ocorre quando eu tento unir todas essas variáveis, como você pode ver na linha 7

success: function(json) {
   var lista = json.list;
   for(var i in lista) {
      var topo = $('<div class="assinante">');
      if (lista[i].cpfCnpj != null) { var var_cpfCnpj = $('<p>' + lista[i].cpfCnpj + '</p>'); }
      var rodape = $('</div>');
      var cliente = topo + var_cpfCnpj + rodape;
      cliente.appendTo('#resultado');
   }
}

Quando eu atribuo à var cliente apenas uma das 3 variáveis, ele passa. Quando eu aplico as 3 juntas, ele não passa.

Lucas_Cavalcanti

vc quer a soma das variáveis mesmo?

faça isso:

success: function(json) {  
   var lista = json.list;  
   for(var i in lista) {  
      var topo = $('<div class="assinante">'),
          cpfCnpj = lista[i].cpfCnpj ? $('<p>' + lista[i].cpfCnpj + '</p>') : '',
          rodape = $('</div>');  
      var cliente = topo + cpfCnpj + rodape;  
      cliente.appendTo('#resultado');  
   }  
}
A

Na verdade, Lucas, eu gostaria de concatenar as strings.

Fiz exatamente como você sugeriu, o ajax não completa. Se eu deixar apenas uma das 3 variáveis (sem concatenar), funciona.

Para o resultado final ficar assim:

<div class="assinante">
 <p><span class="rotulo">CPF:</span>[CPF removido]</p> //caso o cliente tenha o CPF cadastrado, claro.
 ...
</div>
Lucas_Cavalcanti

faz sentido.

quando vc faz:

$('<div>')

ele já cria a div, não é só a string.

faça isso:

success: function(json) {    
   var lista = json.list;    
   for(var i in lista) {    
      var cliente = $('<div class="assinante">');
      if (lista[i].cpfCnpj) 
           cliente.append($('<p>').html(lista[i].cpfCnpj))
      cliente.appendTo('#resultado');    
   }    
}
A

Funcionou!

Sou muito fraco de Javascript :frowning:
Me perco sempre quando é para fazer as coisas mais simples.

Então o que você fez foi criar uma variável e ir adicionando conteúdo nela, e depois da variável toda pronta, eu dou append nela na div “resultado”, é isso?
Como eu fecharia esse

aberto na linha 6? (sinto que eu estou falando besteira).

Lucas_Cavalcanti

isso não é só javascript. É jQuery. sugiro que vc dê uma olhada no que é possível fazer com ele. (http://docs.jquery.com)

var s = '<p>Isso é uma string, não  pra dar append</p>'

var p = $('<p>'); //isso cria uma tag p (já aberta e fechada) e retorna o objeto jquery.

p.append('posso colocar qqer html aqui ou objetos jquery');
p.html('substitui o html de dentro da tag');

enfim, tem mta coisa q dá pra fazer

A

Cara, brigadão mesmo por todas as dicas! :smiley:

Spidey

Primeiro, não faz atribuição com vírgula não, não é boa prática de programação. Use a vírgula só em casos específicos, dentro de um “for”, por exemplo, para evitar re-avaliação das expressões. Por exemplo:

for(var i=0, tam=vetor.size(); i<tam; i++){ //iterações }

No seu caso do JavaScript, você tá verificando se as strings são nulas, mas é bem possível que estejam vindo não nulas, mas vazias. Melhor checar direto como se a variável fosse já um valor booleano, ou se a string não está vazia:
if(minha_string)

ou

if(minha_string != null && minha_string != ‘’)

Lucas_Cavalcanti

Spidey, onde vc viu que fazer atribuições com vírgula é má prática?

isso é verdade em várias linguagens, mas em javascript isso é bem normal, e faz parte de uma das convenções mais adotadas. De qqer forma é uma questão de gosto só, não é uma má prática (em javascript)

Spidey

O problema é que gera expressões longas e ilegíveis. Se for pra compactar o código, que use um compactor como o do YUI Compressor ou o JSMin.
Mas é bem opinião mesmo.

Lucas_Cavalcanti

não fica necessariamente longa e ilegível, é só questão de gosto e não tem a ver com compactação:

var a = 1,
    b = "abc",
    c = ["a", "b"],
    d = $('p');

//vs
var a = 1;
var b = "abc"
var c = ["a", "b"];
var d = $('p');
A

Em relação ao JSON, ele pega apenas valores nativos, certo? ele não faz acesso a getters ou setters não?

Pois tenho uma classe que tem um atributo Date chamado nascimento.
Para puxá-lo, tenho um getNascimento() que pega o valor cru dele ( ex: 2011-02-28 ). e outro getter getNascimentoFormatado() que retorna em String o valor da data bonitinha ( ex: “28/02/2011” ).

A dúvida é, para puxar a data bonitinha na tela, terei então que criar um objeto String na minha classe de nome, por exemplo: nascimentoFormatado para que o JSON possa obtê-lo?

Valeu :slight_smile:

Lucas_Cavalcanti

ou criar essa propriedade, ou criar um conversor para a sua entidade e registrar num CustomJSONSerialization.

o XStream, que o VRaptor usa, por padrão serializa os fields, não usa os getters e setters

A

Eu sei que meus problemas estão seguindo outro rumo, mas como não achei um forum específico de Javascript aqui no GUJ, estou incomodando vocês e postando aqui mesmo.

Terei que usar o submit do form para enviar as informações, ao invés de usar um com uma id qualquer
Aonde, pelo amor de deus, estou errando?

Estou fazendo o seguinte:

$(function() {
	 $("#busca-nome").submit(function(e) {
		$('#resultado').html(''); //limpando a div para exibir novos resultados
		e.preventDefault(); //causador de problemas
		$.ajax({
			url: $(this).action(),
			data: $(this).serialize(),
			dataType: 'json',
			contentType: "charset=ISO-8859-1",
			success: function(json) {
	 	 		 // preenchendo a div de resultado
	 	 	 }
		});
	});
 });

Se eu tirar o e.preventDefault();, ele simplesmente mostra o resultado do json em outra página (o que não preciso).
Se eu coloco o e.preventDefault();, ele não submita o form mas também NÃO executa o ajax.

Por que isso?

Lucas_Cavalcanti

tira o e.preventDefault();, e coloca um return false; no final da function

A

Se eu tirar o preventDefault(), ele simplesmente executará o ajax e vai pra outra página.
Qualquer linha depois do success: function(json) { (linha 10) é ignorada :frowning:

Lucas_Cavalcanti

return false faz a mesma coisa que o preventDefault

Lucas_Cavalcanti

btw:

$(function() {  
$("#busca-nome").submit(function() {  
    $('#resultado').html(''); //limpando a div para exibir novos resultados  
    $.ajax({  
        url: $(this).action(),  
        data: $(this).serialize(),  
        dataType: 'json',  
        contentType: "charset=ISO-8859-1",  
        success: function(json) {  
             // preenchendo a div de resultado  
         }  
    });  
    return false;
});  
});
A

Exatamente, parceiro. Fiz EXATAMENTE o que está aí nesse código (copiei e colei, pra garantir).

O Problema é simples, ele não chega nesse return false, ele simplesmente direciona para a página com os resultados logo de cara, sem nem ler NADA abaixo da linha 09 do seu código.

E se eu coloco o return false antes, ele retorna antes de ler a função…
Será que eu sou muito azarado ou esse bug só acontece comigo?

E sim, muito obrigado pela insistência em me ajudar :smiley:

Lucas_Cavalcanti

$(this).action() não existe :wink:

use

$(this).attr('action')

[]'s

A

Cara funcionou!

Assim, sabe o que eu acho?
$(this).action() existe, porque estava funcionando. Mas eu acho que ele EXECUTA a action normalmente, ignorando todo resto (tou chutando porque sou fraco de Javascript).

De qualquer forma, muito obrigado, Lucas. Obrigado mesmo! :smiley:

Criado 20 de fevereiro de 2011
Ultima resposta 4 de mar. de 2011
Respostas 32
Participantes 3