1
votes

The web application I am developing using MyFaces 2.0.3 / PrimeFaces 2.2RC2 is divided into a content and a navigation area. In the navigation area, which is included into multiple pages using templating (i.e. <ui:define>), there are some widgets (e.g. a navigation tree, collapsible panels etc.) of which I want to preserve the component state across views.

For example, let's say I am on the home page. When I navigate to a product details page by clicking on a product in the navigation tree, my Java code triggers a redirect using

navigationHandler.handleNavigation(context, null,
  "/detailspage.jsf?faces-redirect=true")

Another way of getting to that details page would be by directly clicking on a product teaser that is shown on the home page. The corresponding <h:link> would lead us to the details page.

In both cases, the expansion state of my navigation tree (a PrimeFaces tree component) and my collapsible panels is lost. I understand this is because the redirect / h:link results in the creation of a new view.

What is the best way of dealing with this? I am already using MyFaces Orchestra in my project along with its conversation scope, but I am not sure if this is of any help here (since I'd have to bind the expansion/collapsed state of the widgets to a backing bean... but as far as I know, this is not possible). Is there a way of telling JSF which component states to propagate to the next view, assuming that the same component exists in that view?

I guess I could need a pointer into the right direction here. Thanks!


Update 1: I just tried binding the panels and the tree to a session-scoped bean, but this seems to have no effect. Also, I guess I would have to bind all child components (if any) manually, so this doesn't seem like the way to go.

Update 2: Binding UI components to non-request scoped beans is not a good idea (see link I posted in a comment below). If there is no easier approach, I might have to proceed as follows:

  • When a panel is collapsed or the tree is expanded, save the current state in a session-scoped backing bean (!= the UI component itself)
  • The components' states are stored in a map. The map key is the component's (hopefully) unique, relative ID. I cannot use the whole absolute component path here, since the IDs of the parent naming containers might change if the view changes, assuming these IDs are generated programmatically.
  • As soon as a new view gets constructed, retrieve the components' states from the map and apply them to the components. For example, in case of the panels, I can set the collapsed attribute to a value retrieved from my session-scoped backing bean.

Update 3: I got it working as described above. To sum it up, the solution is to store the relevant properties in a session-scoped bean instead of making the entire UIComponent session-scoped. Then, when the component is re-constructed after navigation has occurred, set the attribute values by retrieving the saved properties (using EL), e.g.

<p:panel collapsed="#{backingBean.collapsedState}" ... />

(This is a simplified example. Since I am using multiple panels, I am using a map to store these properties, as described above).

2

2 Answers

0
votes

One solution would be to use session-scoped beans.

0
votes

What do you mean by collapsible panels? I ask because there is a component that is closable as well as a component. I am using in the navigation pane in my project. The accordianPanel has an attribute named "activeIndex". Here's what I did in my sessionBean to maintain the state of my accordion tabs:

 private int tabIndex; //declared a private variable

    public SessionBean() {
       tabIndex = 100; //set the initial tab index to 100 so all tabs are closed when page loads.
    }

    public int getTabIndex(){
       return tabIndex;
    }

    public void setTabIndex(int tabIndex){
       this.tabIndex=tabIndex;
    }

in my navigation pane:



<p:accordionPanel activeIndex="#{sessionBean.tabIndex}" collapsible="true" autoHeight="false">
    <p:tab title="#{tab1_title}">
       <h:commandLink value="link here" action="target_page?faces-redirect=true" /><br/>
    </p:tab>
    <p:tab title="#{tab2_title}">
       <h:commandLink value="link here" action="target_page?faces-redirect=true" />
    </p:tab>
    <p:tab title="#{tab3_title}">
       <h:commandLink value="link here" action="target_page?faces-redirect=true" />
    </p:tab>
 </p:accordionPanel>

I'm not using the tree component for navigation as that presented my project with some difficulties that were easily overcome by using the accordionPanel, so I can't speak to that part of your navigation.