11
votes

I am trying to inject JSF ViewScoped bean as ManagedProperty into a RequestScoped bean which implements javax.faces.validator.Validator. But always a fresh copy of ViewScoped bean is injected.

ViewScoped Bean

@ViewScoped
@ManagedBean
public class Bean {

     private Integer count = 1;     

     private String field2;      

     public String action(){
          ++count;
          return null;
     }

     public String anotherAction(){
          return null;
     }

     //getter and setter

}

Validator

@RequestScoped
@ManagedBean
public class SomeValidator implements Validator {

     public void validate(FacesContext context, UIComponent comp, Object value)
        throws ValidatorException {

           //logging bean.getCount() is always one here. Even after calling ajax action a few times

     }
     @ManagedProperty(value = "#{bean}")
     private Bean bean;
}

xhtml page

<!DOCTYPE html>
 <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>

</h:head>

<h:body>
   <h:form>

    <h:panelGroup layout="block" id="panel1">


        <h:commandButton type="submit" value="Action" action="#{bean.action}">
            <f:ajax render="panel1"></f:ajax>
        </h:commandButton>

        <h:outputText value="#{bean.count}"></h:outputText>

    </h:panelGroup>

    <h:panelGroup layout="block" id="panel2">

        <h:inputText type="text" value="#{bean.field1}">
            <f:validator binding="#{someValidator}" />
        </h:inputText>

    </h:panelGroup>

    <h:commandButton type="submit" value="Another Action" action="#{bean.anotherAction}">
        <f:ajax execute="panel2" render="panel2"></f:ajax>
    </h:commandButton>

 </h:form>

</h:body>

</html>

As mentioned in code, even after calling ajax action a few times, when logging bean.getCount() always display one.

But the same scenario works if I change ViewScoped to SessionScoped. Also if I remove Validator implementation of RequestScoped bean and use a logger in PostConstruct, the count does increment for every ajax request as expected.

Am I doing something wrong? or is this how it should work? Thanks in advance

1

1 Answers

14
votes

That's because binding attribute of <f:validator> is evaluated during view build time. At that moment, the view scope is not available yet (it makes sense, it's still busy being built...), so a brand new view scoped bean would be created which has then the same effect as a request scoped bean. In the upcoming JSF 2.2, this chicken-egg issue will be solved.

Until then, if you're absolutely positive that you need a view scoped bean inside the validate() method (I'd rather look for other ways such as <f:attribute>, EJBs, multi-field validators, etc), then the only way would be evaluating #{bean} programmatically inside validate() method itself instead of getting it injected by @ManagedProperty.

You can use Application#evaluateExpressionGet() for this:

Bean bean = context.getApplication().evaluateExpressionGet(context, "#{bean}", Bean.class);

See also: