[Resolvido] Spring + Ajax - Desafio Caelum - Removendo um registro da listagem com Ajax/JQuery

Oi pessoal! Estava estudando Spring pela apostila FJ-21 e estou com dificuldades para implementar o desafio da remoção de um item de uma lista com Ajax:

“use o JQuery para acionar o método removeTarefa quando clicado em um botão de “excluir”. Para isso, crie uma nova coluna na tabela com um link que o onClick vai chamar o endereço associado a removeTarefa, e via AJAX devemos remover a linha da tabela.”

Obs.: No lugar de Tarefa estou usando Categoria.
Abaixo seguem os arquivos:

lista.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>Controle de Livros</title>
	<link type="text/css" href="resources/css/style.css" rel="stylesheet" />
	<script type="text/javascript" src="resources/js/jquery.js"></script>
	<script type="text/javascript">
		function removeCategoria(id) {
			$.post("removeCategoriaAjax", {'id': id}, function(){
				$("#tabelaCategorias").closest("tr").hide;
			});
		}
	</script>
</head>
<body>
	<a href="novaCategoria">Criar nova categoria</a>
	<br /> <br />
	<table id="tabelaCategorias" class="its">
	<thead>
		<tr>
			<td>Id</td>
			<td>Nome</td>
			<td></td>
			<td></td>
		</tr>
	</thead>
	
	<c:forEach items="${categorias}" var="categoria" varStatus="status">
		<tr bgcolor="${(status.index % 2) == 0 ? 'lightblue' : 'white' }">
			<td>${categoria.id}</td>
			<td>${categoria.nome}</td>
			<td><a href="removeCategoriaAjax?id=${categoria.id}">Remover</a></td>
			<td><a href="mostraCategoria?id=${categoria.id}">Alterar</a></td>
		</tr>
	</c:forEach>
	</table>
</body>
</html>

spring-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc 
						http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<context:component-scan base-package="br.com.coldsoft.livros" />
	<mvc:annotation-driven />

	<mvc:resources location="/resources/" mapping="/resources/**"/>
	
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/"/>
		<property name="suffix" value=".jsp"/>
	</bean>
	<mvc:default-servlet-handler/>
	<!-- <mvc:interceptors>
		<bean class="br.com.projetos.blank.interceptor.AutorizadorInterceptor" />
	</mvc:interceptors> -->

</beans>

CategoriasController.java

@Controller
public class CategoriasController {

	@RequestMapping("removeCategoriaAjax")
	public void removeTarefaAjax(Long id, HttpServletResponse response){
		Categoria categoria = new Categoria();
		categoria.setId(id);
		new CategoriaDao().remove(categoria);
		response.setStatus(200);
	}
	
	/*@RequestMapping("removeCategoria")
	public String removeCategoria(Categoria categoria) {
		new CategoriaDao().remove(categoria);
		return "redirect:listaCategoria";
	}*/
	
	@RequestMapping("listaCategoria")
	public String lista(Model model) {
		CategoriaDao dao = new CategoriaDao();
		model.addAttribute("categorias", dao.listAll());
		
		return "categoria/lista";
	}
	
	@RequestMapping("novaCategoria")
	public String form() {
		return "categoria/formulario";
	}
	
	@RequestMapping("adicionaCategoria")
	public String adiciona(@Valid Categoria categoria, BindingResult result) {
		if (result.hasErrors()) {
			return "categoria/formulario";
		}
		
		CategoriaDao dao = new CategoriaDao();
		dao.insert(categoria);
		return "redirect:listaCategoria";
	}
	
}

Quando clico no link para remover a Categoria no lista.jsp:

uma página em branco aparece com o seguinte endereço:

Clico em voltar para a página http://localhost:8080/livros/listaCategoria clico em atualizar e vejo que o registro foi excluído, mas não deveria acontecer dessa forma, certo?

Alguém tem alguma sugestão de como fazer o Ajax funcionar normalmente?

A sua URL esta retornando o removeCategoria, porém este método esta comentado no sua CategoriasController, logo ele não existe, o seu método de remoção removeTarefaAjax e do tipo void, porém não retorna nada , porque vc não altera para String e adiciona o return “redirect:listaCategoria”; como vc fez no removeCategoria.

Obrigado pela resposta, amigo. Fiz conforme você falou, porém o problema é que dessa maneira a página dá refresh. Meu objetivo é usar o Ajax para não ter que dar refresh na página. Por isso uso este código:

@RequestMapping("removeCategoriaAjax")
public void removeCategoriaAjax(Long id, HttpServletResponse response){
	Categoria categoria = new Categoria();
	categoria.setId(id);
	new CategoriaDao().remove(categoria);
	response.setStatus(200);
}

Ele é semelhante o que a apostila ensina para atualizar o status de uma tarefa na mesma tela de listagem de tarefas:

@RequestMapping("finalizaTarefa")
public void finaliza(Long id, HttpServletResponse response){
    TarefaDAO dao = new TarefaDAO();
    dao.finaliza(id);
    response.setStatus(200);
}

Alguma outra idéia?

Perfeito, não tinha atentado para esse detalhe!!
Mas analisando sua linha Remover percebi que vc não utliza o evento onClick, pois pela descrição o registro deve ser removido quando ocorrer o click no link remover, e outra coisa, que talvez possa esta errado pois não manjo muito de javaScript, com relação ao link vc chama por removeCategoriaAjax, o certo não seria chamar a função criada no script ?, ou seja, removeCategoria, algo assim:

ou assim

Camarada, fiz esse teste como você falou. Minha página jsp ficou assim:

[code]<%@ page language=“java” contentType="text/html; charset=UTF-8"
pageEncoding=“UTF-8”%>
<%@ taglib uri=“http://java.sun.com/jsp/jstl/core” prefix=“c” %>
<%@ taglib uri=“http://java.sun.com/jsp/jstl/fmt” prefix=“fmt” %>

Controle de Livros Criar nova categoria

<c:forEach items="${categorias}" var="categoria" varStatus="status">
	<tr bgcolor="${(status.index % 2) == 0 ? 'lightblue' : 'white' }">
		<td>${categoria.id}</td>
		<td>${categoria.nome}</td>
		<td><a href="#" onClick="removeCategoriaAjax(${categoria.id})">Remover</a></td>
		<td><a href="mostraCategoria?id=${categoria.id}">Alterar</a></td>
	</tr>
</c:forEach>
</table>
[/code] O que acontece é o seguinte: eu clico no link e não acontece nada mas quando faço um refresh a linha some. Ou seja também é preciso que eu faça um refresh manual. Acho que estamos perto de resolver, mas o que será que está faltando?
Id Nome

Bom, isso parece ser definido na sua function, tenta adicionar o id que recebe a açao AJAX no tabelaCategorias como abaixo:

<script type="text/javascript"> function removeCategoriaAjax(id) { $.post("removeCategoriaAjax", {'id': id}, function(){ $("#tabelaCategorias"+id).closest("tr").hide; }); } </script>
ou assim que defini apenas o elemento que recebe a ação

<script type="text/javascript"> function removeCategoriaAjax(id) { $.post("removeCategoriaAjax", {'id': id}, function(){ $(this).closest("tr").hide(); }); } </script>

Tentei aqui das duas formas que você me mostrou mas ainda assim dá o mesmo problema. Tenho que dar refresh para atualizar a linha removida.
Será que estou usando o JQuery errado?

Tenta assim

<script type="text/javascript"> function removeCategoriaAjax(id) { $.post("removeCategoriaAjax", {'id': id}, function(){ $("#tabelaCategorias"+id).closest("tr").hide; // aqui vc deixa como esta }); } </script>

<c:forEach items="${categorias}" var="categoria" varStatus="status"> <tr bgcolor="${(status.index % 2) == 0 ? 'lightblue' : 'white' }" id="tabelaCategorias${categoria.id}"> // aqui vc adiciona o id que sera atualizado na ação <td>${categoria.id}</td> <td>${categoria.nome}</td> <td><a href="#" onClick="removeCategoriaAjax(${categoria.id})">Remover</a></td> <td><a href="mostraCategoria?id=${categoria.id}">Alterar</a></td> </tr> </c:forEach>
Com relação ao JQuery não sei te informar.

Obrigado pela ajuda. A página ficou assim, mas ainda assim tenho que dar refresh. Acha que faltou alguma coisa?

[code]<%@ page language=“java” contentType="text/html; charset=UTF-8"
pageEncoding=“UTF-8”%>
<%@ taglib uri=“http://java.sun.com/jsp/jstl/core” prefix=“c” %>
<%@ taglib uri=“http://java.sun.com/jsp/jstl/fmt” prefix=“fmt” %>

Controle de Livros Criar nova categoria

<c:forEach items="${categorias}" var="categoria" varStatus="status">
	<tr bgcolor="${(status.index % 2) == 0 ? 'lightblue' : 'white' }" id="tabelaCategorias${categoria.id}">
		<td>${categoria.id}</td>
		<td>${categoria.nome}</td>
		<td><a href="#" onClick="removeCategoriaAjax(${categoria.id})">Remover</a></td>
		<td><a href="mostraCategoria?id=${categoria.id}">Alterar</a></td>
	</tr>
</c:forEach>
</table>
[/code]
Id Nome

Blz, a ultima alteração que talvez quem sabe resolva e adicionar um parametro na sua segunda function no seu script, como neste link http://www.guj.com.br/posts/listByUser/143709.java ficando assim;
<script type="text/javascript"> function removeCategoriaAjax(id) { $.post("removeCategoriaAjax", {'id': id}, function(dados){ $("#tabelaCategorias"+id).closest("tr").hide; }); } </script>

Não funcionou. Parece que o javascript está ok. Imagino se o problema não está no Spring ou no JQuery…

Amigo, talvez o problema não esteja no JSP. Talvez o problema esteja no controller, tente colocar algum retorno no controller, por exemplo: uma String, que retorne um JSon com o resultado da remoção.

@RequestMapping(“removeCategoriaAjax”)
public @ResponseBody String removeTarefaAjax(Long id, HttpServletResponse response){
Categoria categoria = new Categoria();
categoria.setId(id);
String retorno = null;

    try {
        retorno = new CategoriaDao().remove(categoria);
    } catch (Exception e) {
        retorno = "{ \"messageError\": \"" + e.getMessage() + "\"}";
         e.printStackTrace();
    }

    return retorno;
}  

Na função JQuery é so pegar o resultado:

function removeCategoriaAjax(id) {
$.post(“removeCategoriaAjax”, {‘id’: id}, function(dados){
if ( dados.messageError == null || dados.messageError == ‘’ ){
alert( dados.messageError );
}

        });  
    }

Camarada, estou tentando implementar do jeito que você mostrou. Perdoe a minha ignorância, não sei usar JSON. Hoje o meu método remove do CategoriaDao está assim:

	public void remove(Categoria categoria) {
		Transaction tx = session.beginTransaction();
		session.delete(categoria);
		tx.commit();
	}

Qual seria o retorno adequado para ele em JSON? Poderia postar um exemplo? A propósito, muito obrigado pela ajuda.

public String remove(Categoria categoria) { Transaction tx = session.beginTransaction(); session.delete(categoria); tx.commit(); return "Como seria este retorno?????"; }

usar:

ao invés de:

testa ai, pois tabelaCategorias+id ja é o próprio TR

douglaskd, a dica do remove que você deu era o que estava faltando para funcionar. Valeu!
Agora, ficou uma dúvida: quando rodo no Firefox funciona sem refresh mas no Chrome ainda vejo que ainda ocorre o refresh.

Isso é problema do Chrome ou tem alguma coisa errada no meu código mesmo?

[size=18] [/size]Tente Colocar o Script da Function dentro do Body do jsp Comigo funcionou e o mais importante era q vc estava esquecendo de colocar os () depois do hide.hide é um metodo q aceita ate parametros como “slow” q faz akela parte desaparecer mais devagar

Fala Peull!!! Você acertou na mosca!! O meu problema é que não coloquei os parênteses depois da função hide!!! Não acredito que não consegui enxergar isso! :x
Agora sim está funcionando do jeito que eu queria!!!

Mais uma vez obrigado pela sua ajuda!!