#PostConstruct and commandButton/commandLink parameters case - jsf

I'm wondering if there is a common pattern for the following scenario.
Let's say I have one JSF page backed with one request scoped bean.
I want to fech all data rows from a database table when a user enters this page.
The same JSF page contains a form to provide query criteria. When the user provides query criteria and submits the form, I want to display the result on the same page, too.
The suitable place to fetch all rows at page entry is the #PostConstruct method. This is a nice place to do that since additional (injected) request parameters are already available here and can be used in the query. However, parameters submited from the form are not available yet. They can be accessed in the action method.
If the user queries the database table using the form criteria the database will be queried twice in this case. The request scoped bean will be recreated and #PostConstruct method fetching all rows will be called prior to the form action method fetching what the user wants.
Of course I could redirect the form result to another JSF page backed by a different bean with DB query only in the action method. But is there a way to accomplish fetching only what is needed with one JSF page and one managed bean?

What you should use is a combination of ViewScoped managed bean and ajax engine. You're right about using #PostConstruct method to fetch all initial rows of your data table. Any further requests from your query form will not trigger this method again. Instead, you only need to make an ajax call to sort out the entries in your data table and update it at the end of the call. It would be something like this:
<h:dataTable id="myTable">
...
</h:dataTable>
<h:form>
...
<h:commandButton actionListener="#{viewScopedBean.sortEntries}">
<f:ajax render="myTable" execute="myForm" />
</h:commandButton>
</h:form>

Related

How to restore ViewScoped bean when user clicks back button?

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.

PrimeFaces datatable.filter() and url parameter

I have a .xhtml model with a primeface datatable in it.
I call the page with an URL like this:
http://localhost:8080/myproject/mypage.jsf?Id=51&startDate=04-05-2015&name=whatever
The URL parameters are used to retrieve what will be displayed in the datatable, so it allow me to filter the content.
I used URL parameter because this page is displayed when I select a row in another datable so I have to make a manual redirect to this page on the baking bean.
However everytime I use one of primeface functionality like sorting or pagination primeface seems to do an ajax call to the backing bean but WITHOUT the parameters, so every object are displayed instead of a filtered list of Objects.
Therefore how can I force primefaces to use these parameters? Or how can I pass them to primefaces scope (they are #ManagedProperty on the backing bean)
The best and easiest way is to use the OmniFaces utility library and more specifically their <o:form>.
From the documentation:
The <o:form> is a component that extends the standard <h:form> and provides a way to keep view or request parameters in the request URL after a post-back
...
You can use it the same way as <h:form>, you only need to change h: to o:.
So, replace your <h:form> by either
<o:form includeRequestParams="true">
or
<o:form useRequestURI="true">
See also:
Retaining GET request query string parameters on JSF form submit

jsf navigation to another method

I am developing an application using jsf 2.0 and i have the following situation:
I have 2 managed beans.
On the first managed bean i have a method that process some data and then store the result in session.After the data was stored in session i need to invoke a method on the second bean that will display the information from session.
The problem is that if i return a string from the first bean method that correspond to the seconds bean method view the data is not processed, and of course not displayed.
What i need is some navigation rule that from the first bean would redirect me to the second bean method, and the second bean method would return a string with the corresponding view name.
update
maybe a
<f:event listener="#{secondBean.methodToBeInvoked}" type="preRenderView" />
would help me achieve that.
Is this the right approach ?

passing datatable object var as parameter in EL expression: how to make it robust?

In Core JavaServer Faces we find the following example for deleting rows out of a DataTable, where tableData.names is a list of Name objects:
<h:dataTable value="#{tableData.names}" var="name" ... />
<h:commandLink value="Delete" action="#{tableData.deleteRow(name)}" />
</h:dataTable>
It comes with a warning that this may not delete the correct row if we use request scope and the list changes "between the rendering of the table and the decoding of the response".
CAUTION: If the value of the data table has request scope, be sure that the data does not change between the rendering of the table and the decoding of the response
(page 226 of the 3rd edition)
Can anyone explain that quote in terms of the JSF lifecycle? If "render response" is the final phase in the JSF lifecycle, where does "decoding the response" fit in? Do they mean the decoding of the following postback REQUEST (which sends a generated ID by which to identify the row and hence the name object)?
And: how can we implement this in a more robust fashion?
The method expression is not evaluated during the request of displaying the form (the encode step as the book is talking about), but during the request of processing the form submit (the decode step as the book is talking about). The inputs and actions of the datatable row are determined on basis of the table's row index. During processing of the form submit, JSF re-iterates over the data model in order to find the submitted values and the invoked actions.
So, if the <h:dataTable value> is tied to a request scoped bean and thus the data model is reinitialized on a per-request basis, then you'll risk the #{item} to actually reference the item at the wrong index during the processing the form submit, because between the requests of displaying the form and submitting the form, the DB might have retrieved a new item, or have removed another item, which would potentially move the desired item to a different index.
To avoid this, the bean needs to be placed in the view scope so that exactly the same datamodel which is initialized in (post)constructor of the initial request will be preserved across postbacks without the need to reinitialize it during the beginning of every single request and thus potentially containing different items or in a different order. The influence is bigger if loading of the datamodel is tied to a specific request parameter under full control of the enduser like a search query.
An alternative, which is actually a bad one in this particular non-idempotent "Delete this item" case, but a good one for the idempotent "Edit this item" case, would be to use a GET link instead. The desired item is immediately rendered as query string parameter of the <a> element.
<h:link value="Edit" outcome="edit">
<f:param name="id" value="#{item.id}" />
</h:link>

whats the practice to write a review page in JSF?

I have a JSF Page which gets few inputs from the User, I want to show a review page to the user - as what he has given as inputs in the previous page. I am using h:OutputText in the review page to show all the inputs the User had given - but when the user has reviewed and if the user wants to save them - I have a commandButton which is bound to an action method in the backing bean - which will not submit any values as the outputTexts are not submitted. What are the options do I have to show a review page at the same time get the values at the server side - I dont want to have the bean session-scoped.
I am using Apache My faces Implementation of JSF 1.2 without any component libs.
You can use h:inputHidden to retain data for the subsequent POST request. E.g.
<h:outputText value="#{bean.property}" />
<h:inputHidden value="#{bean.property}" />
When you don't have an aversion against component libraries, I'd have suggested Tomahawk's t:saveState for this. This way you don't need to specify every property separately in a hidden field.
<t:saveState value="#{bean}" />
When you're already on JSF 2.0, just putting the bean in the view scope would have been sufficient (assuming that you're conditionally rendering input form and review form in the same view). This way it lives as long as you're interacting with the same view.
#ManagedBean
#ViewScoped
public class Bean {
// ...
}

Resources