12/11/09

JSF e ValueChangeListeners que não funcionam

Um problema que vi vários encontrarem quando usam JSF é que os listeners do ValueChangeEvent tinham suas mudanças sobrescritas/não efetuadas sem motivo aparente. Eu mesmo tive este problema esta semana: usando um bean com escopo request, o método usado para tratar o ValueChangeEvent não conseguia alterar um valor na minha classe.

Na página JSF, o componente usado era um h:selectOneRadio, da seguinte maneira:

<h:selectOneRadio 
    value="#{bean.valor}" 
    valueChangeListener="#{bean.mudaTipo}" 
    onchange="submit();" 
    onclick="submit();">
O método a seguir é como eu o tinha feito originalmente no bean:
public void mudaTipo(ValueChangeEvent event) {
    this.setValor(!valor);
}
Embora o método fosse chamado, o valor atribuído na chamada não era preservado quando o resto do form era submetido. Mudar o bean para escopo session resolve o problema, mas não era uma solução ideal no meu caso.

Após um bom tempo procurando e batendo a cabeça encontrei este post salvador, e "meus pobrema se acabaram-se"! O autor fala sobre três soluções para o problema, mas a que resolveu meu problema foi a segunda: mover o evento ValueChangeEvent para a fase UPDATE_MODEL_VALUES.

Com o código atualizado, o método ficou assim:
public void mudaTipo(ValueChangeEvent event) {
  PhaseId phaseId = event.getPhaseId();
  //pega o novo valor
  boolean newValue = (Boolean) event.getNewValue();
  if (phaseId.equals(PhaseId.ANY_PHASE))
  {
    //agenda o evento para a fase que nos interessa
    event.setPhaseId(PhaseId.UPDATE_MODEL_VALUES);
    event.queue();
  }
  else if (phaseId.equals(PhaseId.UPDATE_MODEL_VALUES))
  {
    //aqui vai o método de antes (um pouco mudado)
    this.setValor(newValue);
  }
}
Assim, o setter do bean é chamado junto com os outros setters no envio da form, e tudo funciona como esperado.
De novo, o crédito da solução é do Dave.

No comments: