JSF com PrimeFaces: Problema com p:ConfirmDialog, p:DataTable e @ViewScoped no bean (dataTable não limpa quando exclui os registros)

Olá, estou fazendo um exercício de um curso, e estou passando por um problema, na verdade consegui fazer o exercício, mas não igual proposto no exemplo, sendo que no exemplo é usando @ViewScoped no bean.

O exercício é simples, apenas criar uma página com uma dataTable e carregar com registro fixos de um bean e testar a Inclusão com um dialog e aExclusão usando um confirmDialog.

Quando eu clico no botão para excluir pelo que vi uma nova requisição é feita.
O cliente é excluído da dataTable, mas não consigo limpar por completo o conteúdo da dataTable, como se sempre viesse um valor selecionado anteriormente via e dai a dataTable sempre fica com 02 registros, nunca “zerando” (no caso populei o bean com 03 registros apenas para teste).

Utilizei o p:confirm junto com um p:confirmDialog conforme os exemplos mais recentes do PrimeFaces.
Obs.: A inclusão está funcionando normalmente.

Página:

<!DOCTYPE html>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://xmlns.jcp.org/jsf/html"
	xmlns:f="http://xmlns.jcp.org/jsf/core"
	xmlns:p="http://primefaces.org/ui"
	xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
	template="/WEB-INF/template/LayoutPadrao.xhtml">

	<ui:define name="titulo">Teste Dialog cliente</ui:define>

	<ui:define name="corpo">

		<script>
			function checkHideDialog(args) {
				if (!args.validationFailed) {
					PF('inclusaoClienteDialogWidgetVar').hide();
				}
			}
		</script>

		<h1>Teste Dialog cliente</h1>

		<h:form id="frm">

			<p:growl id="message" showDetail="true" />

			<p:commandButton id="buttonAdicionarCliente" type="button"
				value="Adicionar cliente"
				onclick="PF('inclusaoClienteDialogWidgetVar').show();" />

			<p:dataTable id="tableClientes"
				value="#{testeClienteDialogBean.clientes}" var="cliente"
				emptyMessage="Não existem clientes.">

				<p:column headerText="Nome">
					<h:outputText value="#{cliente.nome}" />
				</p:column>

				<p:column headerText="Telefone">
					<h:outputText value="#{cliente.telefone}" />
				</p:column>

				<p:column style="width: 40px">
					<p:commandButton icon="ui-icon-trash"
						title="Excluir" action="#{testeClienteDialogBean.excluirCliente}"
						process="@this" update=":frm:message :frm:tableClientes">

						<f:setPropertyActionListener
							target="#{testeClienteDialogBean.clienteSelecionado}"
							value="#{cliente}" />

						<p:confirm header="Exclusão de cliente"
							message="Tem certeza que deseja excluir o cliente (#{cliente.nome}) ?"
							icon="ui-icon-alert" />

					</p:commandButton>
				</p:column>
			</p:dataTable>

			<p:dialog id="inclusaoClienteDialog" header="Inclusão de cliente"
				widgetVar="inclusaoClienteDialogWidgetVar" modal="true"
				resizable="false">

				<p:messages autoUpdate="true" />

				<h:panelGrid columns="2">
					<p:outputLabel value="Nome" for="nome" />
					<p:inputText id="nome"
						value="#{testeClienteDialogBean.clienteSelecionado.nome}"
						required="true" />

					<p:outputLabel value="Telefone" for="telefone" />
					<p:inputText id="telefone"
						value="#{testeClienteDialogBean.clienteSelecionado.telefone}"
						required="true" />

					<p:commandButton value="Incluir"
						actionListener="#{testeClienteDialogBean.incluirCliente}"
						oncomplete="checkHideDialog(args)" update="tableClientes"
						process="inclusaoClienteDialog" />
				</h:panelGrid>
			</p:dialog>

			<p:confirmDialog global="true">
				<p:commandButton value="Sim" type="button"
					styleClass="ui-confirmdialog-yes" icon="ui-icon-check" />
				<p:commandButton value="Não" type="button"
					styleClass="ui-confirmdialog-no" icon="ui-icon-close" />
			</p:confirmDialog>

		</h:form>
	</ui:define>
</ui:composition>

Bean:

package com.exemplo.pedidovenda.controller;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;

import com.exemplo.pedidovenda.model.Cliente;

@ManagedBean
@SessionScoped
public class TesteClienteDialogBean implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private List<Cliente> clientes;
	private Cliente clienteSelecionado;
	
	public TesteClienteDialogBean() {
		clientes = new ArrayList<>();
			
		clientes.add(new Cliente("João da Silva", "34 9999-0000"));
		clientes.add(new Cliente("Maria Abadia Pereira", "11 1234-5678"));
		clientes.add(new Cliente("Sebastião Moreira Júnior", "34 3333-1111"));
		
		clienteSelecionado = new Cliente();
		
		System.out.println("Bean criado...");
	}
	
	public void incluirCliente() {
		clientes.add(clienteSelecionado);
		addMessageInfo("Cliente cadastrado com sucesso!", "Cliente cadastrado com sucesso!");
		clienteSelecionado = new Cliente();
	}
	
	public void excluirCliente() {
		System.out.println("Cliente selecionado: " + clienteSelecionado.getNome());
		System.out.println("Tamanho lista: " + clientes.size());
		
		clientes.remove(clienteSelecionado);
		
		System.out.println("Tamanho lista: " + clientes.size());
		addMessageInfo("Cliente excluído com sucesso!", "Cliente excluído com sucesso!");
	}

	private void addMessageInfo(String sumary, String detail) {
		FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, sumary, detail);		
		FacesContext.getCurrentInstance().addMessage("frm:message", msg);		
	}
	
	public List<Cliente> getClientes() {
		return clientes;
	}	
	
	public Cliente getClienteSelecionado() {
		return clienteSelecionado;
	}

	public void setClienteSelecionado(Cliente clienteSelecionado) {
		this.clienteSelecionado = clienteSelecionado;
		System.out.println("setClienteSelecionado: " + clienteSelecionado.getNome());
	}
}

Model:

package com.exemplo.pedidovenda.model;

import java.io.Serializable;

public class Cliente implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String nome;
	private String telefone;
	
	public Cliente() {
	}
	
	public Cliente(String nome, String telefone) {
		this.nome = nome;
		this.telefone = telefone;
	}
	
	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getTelefone() {
		return telefone;
	}

	public void setTelefone(String telefone) {
		this.telefone = telefone;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((nome == null) ? 0 : nome.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Cliente other = (Cliente) obj;
		if (nome == null) {
			if (other.nome != null)
				return false;
		} else if (!nome.equals(other.nome))
			return false;
		return true;
	}
}

Se alguém tiver idéia do que seja, eu agradeço.

Obs.: Quando digo que “sempre vem um valor selecionado anteriormente” me refiro ao #{testeClienteDialogBean.clienteSelecionado}, no confirmDialog (#{cliente.nome}) vem o nome correto do cliente da coluna, mas para a exclusão lá no bean ele pega uma referência anterior, e só consegui resolver isso trocando de @ViewScoped para @SessionScoped, não tenho certeza, mas observando o console parece que é feita uma nova requisição quando clico para excluir o registro.

Obrigado.

Muda o seu escopo para ViewScoped.
A sua tabela nunca zera pq o escopo é de requisição, ou seja, sempre que uma requisição é feita o bean é criado e toda vez que o bean é criado o construtor dele é chamado e no caso você popula uma lista no construtor, logo ele nunca zerará.

1 curtida

Eu tinha postado errado, na verdade usei @SessionScoped no bean, daí dá certo, corrigi o post.
Usando @ViewScoped não deu certo, conforme citei no início.

Muito obrigado, mas ainda não entendi porquê não estar dando certo com o @ViewScoped.

Ao meu ver, parece que após clicar no botão “Sim” da p:confirmDialog é feita uma nova requisição, pois no console do Eclipse é logada a mensagem de que o “bean foi criado”, conforme coloquei no construtor do bean, e no caso a p:dataTable está sendo atualizada (com os objetos restantes da requisição anterior) mas como a página não é recarregada ela não mostra todos os objetos da lista criados com a nova instância do bean.

Acho que alguma coisa na página possa estar fazendo alguma requisição, olhei, mas no template não parece ter nada que possa influenciar.

Testei melhor e percebi que o problema ocorre quando clica no p:commandButton que está dentro da coluna da dataTable:

<p:column style="width: 40px">
	<p:commandButton icon="ui-icon-trash" title="Excluir"
		action="#{testeClienteDialogBean.excluirCliente}" 
		process="@this" update=":frm:message :frm:tableClientes">

		<f:setPropertyActionListener
			target="#{testeClienteDialogBean.clienteSelecionado}"
			value="#{cliente}" />

		<p:confirm header="Exclusão de cliente"
			message="Tem certeza que deseja excluir o cliente (#{cliente.nome}) ?"
			icon="ui-icon-alert" />

	</p:commandButton>
</p:column>

Ao já se clicar nele, sem nem ao menos confirmar a exclusão do objeto pela confirmDialog já aparece no console que criou uma nova instância do bean, independente do escopo que eu uso nele, então deve ser alguma coisa na página, certo?

Edit: No meu pom.xml estava usando o PrimeFaces 6.2, troquei para 6.0 pra ver o que acontecia, mas não resolveu.

Venho novamente aqui @Mike.
Descobri o erro e agradeço a ajuda, pois revendo sua resposta e olhando outro exercício anterior descobri o erro.

E não era nada na página, realmente era erro no Bean, onde eu estava importando a anotação @ViewScoped do pacote errado, já que não estou usando CDI no projeto (vou colocar mais adiante).

O erro era o seguinte:
No bean eu estava importando a anotação @ViewScoped do pacote “javax.faces.view.ViewScoped”, mas no caso esse pacote só é usado quando se habilita o CDI no projeto e no caso ela já não passa a ser usada com a anotação @ManagedBean e sim com a @Named do pacote “javax.inject.Named”.

import javax.faces.view.ViewScoped;
import javax.inject.Named;
// ... Código
// @Named 
// @ViewScoped
// ... Código

Como é um projeto simples, e mais pra frente vou colocar CDI, por enquanto deixei o gerenciamento pelo próprio JSF mesmo, trocando o pacote de onde estava importando a @ViewScoped para javax.faces.bean.ViewScoped:

// ... Código
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
// ... Código

No final o Bean ficou assim:

package com.exemplo.pedidovenda.controller;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped; // <- Aqui
import javax.faces.context.FacesContext;

import com.exemplo.pedidovenda.model.Cliente;

@ManagedBean
@ViewScoped
public class TesteClienteDialogBean implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private List<Cliente> clientes;
	private Cliente clienteSelecionado;
	
	public TesteClienteDialogBean() {
		clientes = new ArrayList<>();
			
		clientes.add(new Cliente("João da Silva", "34 9999-0000"));
		clientes.add(new Cliente("Maria Abadia Pereira", "11 1234-5678"));
		clientes.add(new Cliente("Sebastião Moreira Júnior", "34 3333-1111"));
		
		clienteSelecionado = new Cliente();
	}
	
	public void incluirCliente() {
		clientes.add(clienteSelecionado);
		addMessageInfo("Cliente cadastrado com sucesso!", "Cliente cadastrado com sucesso!");
		clienteSelecionado = new Cliente();
	}
	
	public void excluirCliente() {
		clientes.remove(clienteSelecionado);
		addMessageInfo("Cliente excluído com sucesso!", "Cliente excluído com sucesso!");
        clienteSelecionado = new Cliente();
	}

	private void addMessageInfo(String sumary, String detail) {
		FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, sumary, detail);		
		FacesContext.getCurrentInstance().addMessage("frm:message", msg);		
	}
	
	public List<Cliente> getClientes() {
		return clientes;
	}	
	
	public Cliente getClienteSelecionado() {
		return clienteSelecionado;
	}

	public void setClienteSelecionado(Cliente clienteSelecionado) {
		this.clienteSelecionado = clienteSelecionado;
	}
}

Espero que isso possa ajudar outros no futuro.
Muito obrigado!