[RESOLVIDO] VRaptor - Criar um formulário de inserção com uma busca dentro

Fala galera, to com uma bronca aqui. To usando o VRaptor pra fazer um sistema de loja.
Eu tenho um objeto que é atributo de outro objeto (no caso, Categoria é atributo de Produto).
Eu consegui colocar na inserção do Produto a lista de categorias em um combo box, pra depois salvar no banco. Isso aí funcionou que foi uma beleza.
O problema é que estou tentando fazer de outra maneira, já que se o número de categorias for muito grande, não fica legal mostrar tudo isso num combo box.
Então no que eu pensei, vou fazer um campo de busca dentro do formulário de inserção, para selecionar a categoria desejada. Mas não tá dando certo.
Nos exemplos que eu tenho aqui, a busca com put e autocomplete requer outro form, e já sei que não é possivel form dentro de outro form.
Alguém pode me dar uma idéia de como eu poderia fazer isso? Grato desde já :slight_smile:

Produto.java

    @ManyToOne
    @JoinColumn(name="id_categoria")
    private Categoria categoria;

Você não precisa ter um form dentro do outro só para fazer o autocomplete.

Você está usando VRaptor, certo?

Use ajax e jQuery…

// functions.js
$(document).ready( function() {
			$('#seuInputAqui').autocomplete({
				source:
					function(request, response){
						$('#seuInputAqui').addClass('inputLoading16'); // Coloca um gif animado para indicar um loading
						$.ajax({
							url: '', // A URL que vai entregar um JSON de categoria, baseado na busca... Dica, use o result.use(Results.json()) do VRaptor.
							type: 'post',
							data: {
								term: $.trim(request.term), // O que está sendo digitado pelo usuário.
								produto: $('#produto').val() // Passe o produto também, para trazer as categorias referentes somente à esse produto.
							},
							success: function(data) { // data é um JSON de categoria, acredito que você quer pegar o name...
			                	response($.map(data,
			                		function(item){
				                		return {
				                			label: item.name,
				                			value: item.name,
				                			id: item.id // Você pode criar quantas variáveis quiser, mas o plugin usa somente Label e Value... Essa aqui é só para eu te mostrar como usar esse ID...
				                		};
				                	}
				                ));
			                	$('#seuInputAqui').removeClass('inputLoading16'); // Remove o loading...
			                },
			                error: function(){
				            	$('#seuInputAqui').removeClass('inputLoading16'); // Em caso de erro tambem remove o loading...
			                }
						});
		            },
				minLength: 0,
				select: function(event, ui){ // Acionado quando um usuário seleciona um registro
					alert('ID da categoria selecionada: ' + ui.item.id); // Você também pode acessar o label e o value...
				},
				open: function () { // Função bem interessante que vai deixar a primeira ocorrência da palavra digitada em negrito...
					var data = $(this).data('autocomplete');
					data.menu.element.find('a').each(function () {
						var self = $(this);
						var keywords = $.trim(data.term).replace(/^(\*)/g, '').replace(/\s/g, '\\ ');
						if (keywords.length > 0)
							self.html(self.text().replace(new RegExp('(' + keywords + ')(.*)', 'gi'), '<b>$1</b>$2'));
					});
		        }
			});
});
&lt;!-- // JSP --&gt;
&lt;input id="seuInputAqui" type="text" name="nomeDoSeuInputAqui" /&gt;

Fala Rafael, obrigado por responder… eu fiz o que você indicou, mas devo estar fazendo algo errado, pois não funcionou…vê só…

criei o método do json no controller do produto:

	@Get("/categoria/buscacategoria.json")
	public void buscaCategoriaJson(String q){
		result.use(json()).withoutRoot()
			.from(cdao.busca(q))
			.exclude("id_categoria")
			.serialize();
	}

e meu jsp ficou assim:

<script type="text/javascript">
$(document).ready( function() {  
    $('#categoria').autocomplete({  
        source:  
            function(request, response){  
                $('#categoria').addClass('inputLoading16'); // Coloca um gif animado para indicar um loading  
                $.ajax({  
                    url: '<c:url value="/produtos/buscacategoria.json"/>', // A URL que vai entregar um JSON de categoria, baseado na busca... Dica, use o result.use(Results.json()) do VRaptor.  
                    type: 'post',  
                    data: {  
                        term: $.trim(request.term), // O que está sendo digitado pelo usuário.  
                        produto: $('#produto').val() // Passe o produto também, para trazer as categorias referentes somente à esse produto.  
                    },  
                    success: function(data) { // data é um JSON de categoria, acredito que você quer pegar o name...  
                        response($.map(data,  
                            function(categoria){  
                                return {  
                                    label: categoria.descricao,  
                                    value: categoria.descricao,  
                                    id: categoria.id_categoria // Você pode criar quantas variáveis quiser, mas o plugin usa somente Label e Value... Essa aqui é só para eu te mostrar como usar esse ID...  
                                };  
                            }  
                        ));  
                        $('#categoria').removeClass('inputLoading16'); // Remove o loading...  
                    },  
                    error: function(){  
                        $('#categoria').removeClass('inputLoading16'); // Em caso de erro tambem remove o loading...  
                    }  
                });  
            },  
        minLength: 0,  
        select: function(event, ui){ // Acionado quando um usuário seleciona um registro  
            alert('ID da categoria selecionada: ' + ui.categoria.id_categoria); // Você também pode acessar o label e o value...  
        },  
        open: function () { // Função bem interessante que vai deixar a primeira ocorrência da palavra digitada em negrito...  
            var data = $(this).data('autocomplete');  
            data.menu.element.find('a').each(function () {  
                var self = $(this);  
                var keywords = $.trim(data.term).replace(/^(\*)/g, '').replace(/\s/g, '\\ ');  
                if (keywords.length > 0)  
                    self.html(self.text().replace(new RegExp('(' + keywords + ')(.*)', 'gi'), '<b>$1</b>$2'));  
            });  
        }  
    });  
});  
</script>
<form action="<c:url value="/produtos"/>" method="POST">
	<fieldset>
		<legend>Adicionar Produto</legend>

		<label for="nome">Nome:</label>
		<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
		
		<label for="descricao">Descrição:</label>
		<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
		
		<label for="preco">Preço:</label>
		<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
		
		<label for="categoria">Categoria:</label>
		<input id="categoria" type="text" name="produto.categoria" value="${produto.categoria }"/>		

		<button type="submit">Enviar</button>
	</fieldset>
</form>

Fiquei com dúvida na url ali, acho que não está correta. Mas deve ter outros erros aí tbm. Agradeço novamente sua ajuda :slight_smile:

Galera, consegui que o nome da categoria ficasse no input mas na hora de salvar deu o seguinte erro:

SEVERE: Servlet.service() for servlet [default] in context with path [/goodbuy] threw exception
br.com.caelum.vraptor.InterceptionException: exception raised, check root cause for details: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: br.com.goodbuy.modelo.Categoria

eu peguei o método do json do controlador de categoria:

	@Get("/categoria/busca.json")
	public void buscaJson(String q){
		result.use(json()).withoutRoot()
			.from(cdao.busca(q))
			.exclude("id_categoria")
			.serialize();
	}

e o jsp do formulario de produto:

<form action="<c:url value="/produtos"/>" method="POST">
	<fieldset>
		<legend>Adicionar Produto</legend>

		<label for="nome">Nome:</label>
		<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
		
		<label for="descricao">Descrição:</label>
		<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
		
		<label for="preco">Preço:</label>
		<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
		
		<label for="categoria">Categoria:</label>
		<input id="categoria" type="text" name="produto.categoria" value="${produto.categoria }"/>
		<script type="text/javascript">
			$("#categoria").puts("Digite a categoria");
			$("#categoria").autocomplete('<c:url value="/categoria/busca.json"/>',
					{
						dataType : "json",
						parse : function(categorias) {
							return $.map(categorias, function(categoria) {
								return {
									data : categoria,
									value : categoria.descricao,
									result : categoria.descricao
								};
							});
						},
						formatItem : function(categoria) {
							return categoria.descricao;
						}
					});
		</script>	
		<button type="submit">Enviar</button>
	</fieldset>
</form>

Alguém poderia me ajudar a pegar o id da categoria pra salvar certo no banco? Obrigado :slight_smile:

[quote=pauloaguila]Fala Rafael, obrigado por responder… eu fiz o que você indicou, mas devo estar fazendo algo errado, pois não funcionou…vê só…

criei o método do json no controller do produto:

	@Get("/categoria/buscacategoria.json")
	public void buscaCategoriaJson(String q){
		result.use(json()).withoutRoot()
			.from(cdao.busca(q))
			.exclude("id_categoria")
			.serialize();
	}

[/quote]
Se o nome do argumento é ‘q’, você precisa passar o ‘q’ no data do ajax… Sugiro você trocar de ‘q’ para ‘term’…
Você não vai usar o produto tambem para pegar as categorias de determinado produto?

[quote=pauloaguila]
e meu jsp ficou assim:

<script type="text/javascript">
$(document).ready( function() {  
    $('#categoria').autocomplete({  
        source:  
            function(request, response){  
                $('#categoria').addClass('inputLoading16'); // Coloca um gif animado para indicar um loading  
                $.ajax({  
                    url: '<c:url value="/produtos/buscacategoria.json"/>', // A URL que vai entregar um JSON de categoria, baseado na busca... Dica, use o result.use(Results.json()) do VRaptor.  
                    type: 'post',  
                    data: {  
                        term: $.trim(request.term), // O que está sendo digitado pelo usuário.  
                        produto: $('#produto').val() // Passe o produto também, para trazer as categorias referentes somente à esse produto.  
                    },  
                    success: function(data) { // data é um JSON de categoria, acredito que você quer pegar o name...  
                        response($.map(data,  
                            function(categoria){  
                                return {  
                                    label: categoria.descricao,  
                                    value: categoria.descricao,  
                                    id: categoria.id_categoria // Você pode criar quantas variáveis quiser, mas o plugin usa somente Label e Value... Essa aqui é só para eu te mostrar como usar esse ID...  
                                };  
                            }  
                        ));  
                        $('#categoria').removeClass('inputLoading16'); // Remove o loading...  
                    },  
                    error: function(){  
                        $('#categoria').removeClass('inputLoading16'); // Em caso de erro tambem remove o loading...  
                    }  
                });  
            },  
        minLength: 0,  
        select: function(event, ui){ // Acionado quando um usuário seleciona um registro  
            alert('ID da categoria selecionada: ' + ui.categoria.id_categoria); // Você também pode acessar o label e o value...  
        },  
        open: function () { // Função bem interessante que vai deixar a primeira ocorrência da palavra digitada em negrito...  
            var data = $(this).data('autocomplete');  
            data.menu.element.find('a').each(function () {  
                var self = $(this);  
                var keywords = $.trim(data.term).replace(/^(\*)/g, '').replace(/\s/g, '\\ ');  
                if (keywords.length > 0)  
                    self.html(self.text().replace(new RegExp('(' + keywords + ')(.*)', 'gi'), '<b>$1</b>$2'));  
            });  
        }  
    });  
});  
</script>
<form action="<c:url value="/produtos"/>" method="POST">
	<fieldset>
		<legend>Adicionar Produto</legend>

		<label for="nome">Nome:</label>
		<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
		
		<label for="descricao">Descrição:</label>
		<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
		
		<label for="preco">Preço:</label>
		<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
		
		<label for="categoria">Categoria:</label>
		<input id="categoria" type="text" name="produto.categoria" value="${produto.categoria }"/>		

		<button type="submit">Enviar</button>
	</fieldset>
</form>

Fiquei com dúvida na url ali, acho que não está correta. Mas deve ter outros erros aí tbm. Agradeço novamente sua ajuda :slight_smile: [/quote]
A url precisa ser ‘/categoria/buscacategoria.json’ pois foi o que você colocou no seu método. Se você olhar bem, no seu método, você está esperando um GET… Ou seja, troque o type: ‘post’ para type: ‘get’…

Eu acho que vc vai precisar pegar as novas versões do jquery e do jquery ui, pois esse método autocomplete existe no jquery ui… Apague o antigo js do autocomplete:
http://jquery.com/
http://jqueryui.com/

PS: eu vi que mudou o nome de ‘item’ para ‘categoria’. Você precisa deixar como ‘item’ pois o plugin usa ‘item’…

Valeu Rafael, consegui resolver aqui :smiley:
Obrigado.

Paulo, desculpe estar ressuscitando o tópico, mass
o que você fez para resolver o ultimo problema apresentado?

obrigado.