Injecting one view scoped bean in another view scoped bean causes it to be recreated

10,690

Solution 1

This will happen if you're navigating from one to the other view on a postback. A view scoped bean is not tied to a request, but to a view. So when you navigate to a new view, it will get a brand new instance of the view scoped bean. It won't reuse the same bean instance which is associated with a previous view.

I understand that the attivita bean is created on the initial view and reused on postback. I understand that nota bean is associated with the new view where you're navigating to. When injecting attivita in it, it will simply get a new and distinct instance even though there's another instance in the very same request. This is all expected (and admittedly a bit unintuitive) behaviour.

There is no standard JSF solution for this. CDI solves this with @ConversationScoped (the bean lives as long as you explicitly tell it to live) and the CDI extension MyFaces CODI goes a bit further with @ViewAccessScoped (the bean lives as long as the navigated view references it).

You could however workaround this by storing the bean as an attribute in the request scope.

@ManagedBean
@ViewScoped
public class Attivita implements Serializable {

    public String submit() {
        FacesContext.getCurrentInstance().getExternalContext()
            .getRequestMap().put("attivita", this);
        return "nota";
    }

}

and

@ManagedBean
@ViewScoped
public class Nota implements Serializable {

    private Attivita attivita;

    @PostConstruct
    public void init() {
        attivita = (Attivita) FacesContext.getCurrentInstance().getExternalContext()
            .getRequestMap().get("attivita");
    }

}

Note that this is rather hacky. There may be better solutions depending on the concrete functional requirement. Also note that you should in the nota view reference the desired Attivita bean instance as #{nota.attivita} and not as #{attivita}, because it would give you a new and different instance, for the reasons already explained before.

Solution 2

Your attivita bean is @ViewScoped and that doesn't guarantee that your instance will be hold in session. You need a @SessionScoped bean. However, if you need attivita for some reason to be @ViewScoped, then you can pass params through them in other ways, e.g. using viewParam or using other @SessionScoped bean between them.

Page Params

http://mkblog.exadel.com/2010/07/learning-jsf2-page-params-and-page-actions/

JSF 2 Managed Bean Scopes

http://balusc.blogspot.com.es/2011/09/communication-in-jsf-20.html#ManagedBeanScopes

Share:
10,690
supertarmax
Author by

supertarmax

My activities are mainly focused on open-source technologies, such as: J2EE, Mysql, Spring MVC, Apache. I acquired experience in Natural Language Process and Latent Semantic Analysis.

Updated on June 23, 2022

Comments

  • supertarmax
    supertarmax almost 2 years

    I need to use some data saved in a view scoped bean in an other view scoped bean.

    @ManagedBean
    @ViewScoped
    public class Attivita implements Serializable {
        //
    }
    

    and

    @ManagedBean
    @ViewScoped
    public class Nota implements Serializable {
    
        @ManagedProperty("#{attivita}")
        private Attivita attivita;
    
        // Getter and setter.
    }
    

    Now, maybe my theory about it is still quite poor, I have noticed that when #{attivita} is injected, the Attivita constructor is invoked and thus creating another instance. Is it the right behaviour? What about if I want to reference the same instance and not create a new one?

  • GreenieMeanie
    GreenieMeanie about 9 years
    This works for sure - I had the same issue. However, it is very hacky as you stated, and is yet ANOTHER huge shortcoming of JSF. It kind of eliminates the purpose of CDI or Managed Properties when you have to add a PostConstruct and have to manually pull a bean out of the Faces Context. Is there a plan to have @ViewAccessScoped added to the standard JSF API and not as part of Myfaces only?
  • BalusC
    BalusC about 9 years
    @GreenieMeanie: JSF 2.2 added @FlowScoped to cover this, but unfortunately it still requires a bit of XML configuration and autogenerated request parameters, because it also needed to survive GET requests. It's, say, between @ViewAccessScoped and @SessionScoped. As a probable "better solution", you can think of just redirecting it with entity ID as param, or conditionally rendering part of the very same view with another include. See also a.o. stackoverflow.com/questions/15521451/…
  • GreenieMeanie
    GreenieMeanie about 9 years
    Are you suggesting to have view a.xhtml have a "dummy" include to b.xhtml and perhaps wrap all the content of b.xhtml in one huge ui:fragment that only actually renders anything when it is redirected to (via b.xhtml) and NOT when it is included as a dummy reference from a.xhtml?
  • BalusC
    BalusC about 9 years
    @GreenieMeanie: See e.g. stackoverflow.com/questions/7108668/… (apply the idea on this single "Wizard" page, not on entire site).