How to restore ViewScoped bean when user clicks back button? - jsf

Lets say I have a #ViewScoped Bean behind my current page A. Now the user navigates to page B via a normal get request, lets say to www.google.com.
When the user clicks the back button of the browser, I would like to restore the #ViewScope of the previous page, so that it appears exactly as it was left. Is that possible to achieve somehow?
I dont want to make my page A #SessionScoped so that the backing beans do not disturb each others state when opened in two browser tabs.

Since version 2.6 OmniFaces has this feature, is called #ViewScoped(saveInViewState = true) But with some caution!
It's very important that you understand that this setting has potentially a major impact in the size of the JSF view state, certainly when the view scoped bean instance holds "too much" data, such as a collection of entities for a data table, and that such beans will in fact never expire as they are stored entirely in the javax.faces.ViewState hidden input field in the HTML page. Moreover, the #PreDestroy annotated method on such bean will explicitly never be invoked, even not on an unload as it's quite possible to save or cache the page source and re-execute it at a (much) later moment.
A more programmatical solution is the #ConversationScoped. With the convertsation id as parameter can you restore the view.
conversationscope example

Yes it is possible, pass parameter like this using f:param this will pass your parameter to the next screen.
<h:commandLink action="screenName" value="#{search.participantName}">
<f:param value="#{searchcus.participantId}" name="PARTICIPANT_ID"/>
<f:param name="PARENT_SCREEN_CODE" value="SEARCH_PARTICIPANT"/>
</h:commandLink>
After that in init() method get value as a parameter to fetch the result.

Related

How to pass objects from one page to another page in JSF without writing a converter

first of all sorry for my english. I have two pages in JSF2, one to list Passengers and another one to create/update passengers. I have also two #ViewScoped beans, one with the list of passengers and one for hold in pageB the selected passenger. I see the ways to pass the passenger through viewParam or #ManagedProperty but i don´t want to write a converter.
What i want to know if there is a way to pass the object from pageA to pageB without passing the id of the passenger and write a converter or without passing the id and then go to the DB to retrieve the passenger.
What i do and works is the following. I set in flash scope through setPropertyActionListener the selected object and navigate to pageB, in the #PostConstruct of the viewScopedBean i get the flashScope and retrieve the object. As i said, this works but i don´t know if it is correct. Here is the code
Page A:
<p:column width="10" style="text-align: center;">
<p:commandButton icon="ui-icon-pencil" action="editClientes?faces-redirect=true">
<f:setPropertyActionListener target="#{flash.pax}" value="#{row}"/>
</p:commandButton>
</p:column>
#PostConstruct of pageB bean
#PostConstruct
private void initBean(){
this.pax = (Passenger) JSFUtils.getFlashScope().get("pax");
if(this.pax == null){
this.pax = new Passenger();
}
}
Is this correct, or the correct way is to write a converter?
Thanks.
Depends on whether you want the /editClientes request to be idempotent ("bookmarkable") or not.
The flash approach is not idempotent. It's not possible to link/share/bookmark the /editClientes URL in order to edit a specific client. When the enduser copies this URL for sharing/bookmarking and re-executes the request on it (even though it's just pressing [enter] in browser's address bar), all the enduser would face is an empty edit form for a new client instead of the one the enduser initially selected via flash scope.
The request parameter approach is idempotent. The enduser is able to get exactly the same response everytime the enduser re-executes the request.
It's not our decision whether your /editClientes page should be idempotent or not. It's yours.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)

Using #ViewScoped with master template doesn't reset inputs

I am using a master template (i.e. <ui:composition template> / <ui:define>) where I have a navigation panel on the left, and the content panel in the center. When I go to one of my pages that is #ViewScoped, I edit some of the fields, go to another page which reloads the content area, and then go back to the original page, and the fields are all still populated with data. This implies that the view never ended. I tried #RequestScoped which produces the results I want, but breaks all of the ajax in the page related to that bean.
What is the best way to reset a page to its original state?
Can anyone see what is happening, exactly, regarding my layout and content area (consisting of a ui:include) being updated that would cause this not to be considered a change of View?
By navigating the content of the page using a backing bean, a ui:include updated by ajax, all in the same page, JSF's current view never goes out of scope, which is why the backing ViewScoped beans are never reset after navigating.
One possible fix for this is to redesign the page to use traditional navigation. In other words, lets say you have several p:menuitems. Rather than assigning the actionListener to some backing bean function that sets the content page (followed by an update of the panel containing the ui:include), you would set the "action" of each menuitem to the page to which each corresponds. This has the effect of causing a page to be loaded when a menuitem is clicked.
This generated the requirement that I had to reorganize my template a little so that every page of my site was embedded in it. There's a little bit of flicker as the entire page reloads, but the beans are being reset accordingly.

How to change the order of creation/restoring managed beans?

I have a complex problem with order of 'JSF bean life cycle actions'.
I have two beans with different scopes. The first, let's call it, managerBean is session scope bean. The second one, someBean has view scope (someBean really is many different beans). ManagerBean takes some action once per page loading and few others view scope beans are using the results of this action in their constructors.
Everything was working just fine until I've started getting forms IDs in xhtml files from java beans. Now action from managerBean is taken after someBean is created and I'm getting expected result only when the page is reloaded (on refresh, so someBean is using the first results of ManagerBean work).
This is how it looks like now:
<!-- mainTemplate is a main templete of the page which is rendered once
per page view (every other actions are taken via ajax). This is a place
of ManagerBean work after re rendering the page -->
<ui:composition template="/mainTemplate.xhtml">
<ui:define name="mainContent">
<h:form id="#{someBean.formID}">
some inputs
</h:form>
(...)
</ui:define>
</ui:composition>
So when form id was constant String everything worked like I want and now it doesn't. It looks like JSF must calculate ID first and take any other after this (including ManagerBean action).
My question is: Is there a way to change this situation?
If something isn't clear enought, please ask. I was trying to simplify the problem because it has many factors. Maybe all my thinking is wrong (the way I want to take some action per page and some actions after it).
Any help will be good!
The id (and binding) attribute of a JSF UI component is evaluated during view build time. The view build time is that moment when the XHTML source code is turned into a JSF UI component tree. All other attributes of a JSF UI component like value and all events like preRenderView are evaluated/executed after the view build time, usually during view render time (when the JSF UI component tree needs to produce HTML output). This is not something which you can change by just turning a setting or so. It's just the way how JSF works. You can't render something which isn't built yet. You can only change this by writing code the right way.
I can't think of any real world scenario why you need to make the ID attribute dynamic like this. If it were inside a <c:forEach>, or part of dynamic component generation, then okay, but this seems just to be a static form. So I would in first place recommend to forget it and just hardcode the ID in the view and rely on other variables (perhaps a hidden input field? depends all on concrete functional requirement which isn't mentioned anywhere in the question nor guessable based on the code posted so far).
If you really need to make it dynamic, then you need to split the formID property off from the view scoped bean and move it to a different and independent bean, perhaps an application scoped one.
See also:
JSTL in JSF2 Facelets... makes sense? - component's id attribute has same lifecycle as JSTL tags

No saved view state

I have a view scoped bean that has a method call generateLicenseFile.
The method returns a String with this value
/licenseGenerated.xhtml?faces-redirect=true"
The JSF code for the page (agreementDetail.xhtml) looks like this
<h:commandButton value="Generate License File" action="#{agreement.generateLicenseFile}" />
When I click the button, I get an error
javax.servlet.ServletException: /agreementDetail.xhtmlNo saved view state could be found for the view identifier: /agreementDetail.xhtml
Any ideas?
--EDIT--
Just FYI, the "generateLicenseFile" method is not even executed, since I have logging that proves this.
No Saved View generally occurs when something goes wrong in the JSF Servlet life cycle.Maybe it's because of the View Scoped Bean.Try to change it to Session Scoped and make sure your managed bean entry is there in the faces-config.xml.
Another suggestion is in the generateLicenseFile method in the agreement bean return a String as "someStringName" and make sure you create a navigation handler in Faces Config with navigation rule and navigation case.

JSF and richfaces: h:commandlink in richfaces table not working properly

When using h:commandlink(or commandbutton) inside a rich:dataTable, the action specified is never invoked, neither is the corresponding managed bean instantiated(whether it is at request or session scope)...
instead, the same request is performed.. (page reloads)..
have seen what appeared to be similar issue on forums, but is not actually the problem i am having..
the h:commandlink /button work ok outside of the rich:datatable..
Does anyone have any advice?
here is a code snippet:
<h:commandLink id="commLink" actionListener="#{hBean.test}" action="#{hBean.viewTranslation}">
<h:outputText value="#{trans.translationName}"/>
</h:commandLink>
</rich:column>
The bean is apparently request scoped and the datamodel is not been loaded during bean's construction (at least, during apply request values phase of the subsequent request). You need to preserve the same datamodel for the subsequent request, else JSF cannot locate the row item associated with the clicked link. The most straightforward way is to load the datamodel in the bean's constructor or #PostConstruct method.
A quick fix/test is to put bean in session scope. The datamodel will then be saved in the session scope and be available in the subsequent request. But this has more impact on user experience (e.g. unexpected results when having the same page opened in different browser windows/tabs in the same session). If you're already on JSF 2.0 (which is likely not the case since you're using RichFaces), then the new view scope would have been the solution.
Related questions:
h:commandLink is not been invoked - contains an overview of all possible causes of this behaviour.
If you are using RichFaces 3.0.0 through 3.3.3, use the tag a4j:keepAlive. It will works even with request scope.

Resources