Archive for setembro, 2008
Refresh reenviando os dados
Imagine o seguinte cenário: Você termina de preencher os campos de uma página de inclusão. Ao clicar em OK, o sistema envia o formulário e executa um forward para a página de listagem do seu cadastro com a mensagem: “Registro incluído com sucesso.”
No JSF (e imagino que em várias outras soluções MVC) quando você dá um refresh (F5), o sistema reenvia os dados que você preencheu na tela de cadastro e acaba incluindo um novo registro (ou tentando incluir, caso tenha problema com algum campo que não pode se repetir), e na atual listagem aparece dois dados iguais.
Andei pesquisando a maneira mais simples de não dar um forward após a inclusão do dado. Acabei caindo numa boa explicação do Rafael Pontes sobre a diferença entre o forward e redirect.
Vamos a solução:
Na regra de navegação, agora foi usada uma tag (<redirect />)na passagem do cadastro para a listagem:
<navigation-rule>
<from-view-id>/empresa/cadastroEmpresa.jsp</from-view-id>
<navigation-case>
<from-outcome>listagem</from-outcome>
<to-view-id>/empresa/listagemEmpresa.jsp</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
Com isso perdemos as nossas mensagens adicionadas ao messages através do método FacesContext.getCurrentInstance().addMessage(), já que essas mensagens são válidas só por requisição. A solução para contornar esse problema foi a utilização de alguma classe que tem o escopo de sessão. A escolhida no meu caso, foi a classe de login do usuário, que é a classe que ele carrega para “provar” que é um usuário cadastrado e logado no sistema. Dentro dessa classe incluí:
private List<String> mensagens;
public void showMensagens() {
if(this.mensagens==null)
this.mensagens = new ArrayList<String>();
for(String msg : this.mensagens)
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, null));
this.mensagens = new ArrayList<String>();
}
public void addMensagens(String mensagem) {
if(this.mensagens==null)
this.mensagens = new ArrayList<String>();
this.mensagens.add(mensagem);
}
e na classe ancestral dos backbeans o seguinte método:
protected void addNewMessage(String msg) {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
HttpSession session = (HttpSession) externalContext.getSession(false);
LoginCodebehind login = (LoginCodebehind) session.getAttribute("login");
login.addMensagens(msg);
}
protected void showMessages() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
HttpSession session = (HttpSession) externalContext.getSession(false);
LoginCodebehind login = (LoginCodebehind) session.getAttribute("login");
login.showMensagens();
}
O primeiro método busca no contexto o meu objeto de sessão login e adiciona a mensagem. Esse método será chamado após uma inclusão com sucesso como por exemplo:
public void actionGravar(ActionEvent event) {
try {
EmpresaBO.getInstance().create(empresaTO);
this.addNewMessage("Empresa cadastrada com sucesso.");
} catch (RegraNegocioException e) {
e.printStackTrace();
this.addErrorMessage(e);
throw new AbortProcessingException();
}
E o segundo método será chamado pelas telas de listagem, para exibirem as mensagem quando houverem.
public ListagemEmpresa() {
try {
this.showMessages();
empresaList = EmpresaBO.getInstance().findAll();
} catch (RegraNegocioException e) {
this.addErrorMessage(e);
}
}
Esse solução foi feito para exibir somente as mensagens de sucesso, já que teoricamente são as únicas que passam de uma página para a outra. Quando houver a necessidade de exibir mensagem de erro, não deverá mudar de tela, então vai exibir no mesmo request. Mas é possível adicionar um parâmetro nas chamadas para que seja acrescentada o tipo da mensagem.
[]‘s