What scope to use in JSF 2.0 for Wizard pattern? - jsf-2

I have a multi-page form, aka a Wizard pattern, where Page 1 corresponds to Step 1 of the wizard form, Page 2 corresponds to Step 2, etc. Each page other than the last has a Next button on it that takes you to the next page in the form. The final page has a submit button that submits all the data for all pages in the wizard.
What scope should I use to maintain the state of the data entered on each form? e.g. should I use a View Scoped bean that holds all the data entered on all pages? Will that work since I'll be navigating to different pages (Which I believe are considered to be different "views"; and if they're different views, I believe the View Scoped data will be lost when you navigate to the next page in the wizard)

I believe the View Scoped data will be lost when you navigate to the next page in the wizard)
That's correct. The view scope lives as long as you're interacting with the same view and get trashed whenever a new view get created. You're looking for the "conversation scope". This isn't available by any of the JSF managed bean scopes. This is however available by CDI #ConversationScoped. So if your environment happen to support CDI, you could make use of it:
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Inject;
import javax.inject.Named;
#Named
#ConversationScoped
public class Wizard implements Serializable {
#Inject
private Conversation conversation;
#PostConstruct
public void init() {
conversation.begin();
}
public void submitFirstStep() {
// ...
}
// ...
public String submitLastStep() {
// ...
conversation.end();
return "someOtherPage?faces-redirect=true";
}
// ...
}
The conversation is managed by the automatically inserted cid request parameter.
If you'd like to stick to the JSF view scope, then your best bet is to create a single page wherein you render the multiple steps conditionally:
<h:panelGroup rendered="#{wizard.step == 1}">
<ui:include src="/WEB-INF/wizard/step1.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{wizard.step == 2}">
<ui:include src="/WEB-INF/wizard/step2.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{wizard.step == 3}">
<ui:include src="/WEB-INF/wizard/step3.xhtml" />
</h:panelGroup>
Or, you could use a 3rd party component library like PrimeFaces which has a <p:wizard> component for exactly this purpose.

From my pov, good choice here is session scoped beans. When needed, user will be able to interrupt the wizard, visit other pages, doc, manuals, whatever, and get back to the same wizard step. Of course it can be done via view-scoped beans (see BalusC answer). Personally I prefer view-scoped beans when ajax is heavily involved. In that case I'd recommend to combine these two scopes.

You can find an example using the conversation scope for creating a wizard at this site:
JEE6 – CDI and Conversation Scope

Related

jsf 2.2 enable disable button with EL and not javascript [duplicate]

I have an inputField, or some other tag , that I want to be disabled unitl user clicks on it.
Something like this, but I cant get it to work in JSF.
$("div").click(function (evt) {
$(this).hide().prev("input[disabled]").prop("disabled", false).focus();
});
I add disabled=true" to my input field, and div value set on < h:form > (all parent tags in this case only one), something like j_idt13 and div of input field, so "div" value looks like j_idt13:inputID
Can someone help me with jQuery solutin?
I wold like to know can it be done in JSF, and how.
You need to toggle it via server side, not via client side. JSF as being a stateful component based MVC framework safeguards this way against tampered/hacked requests wherein the enduser uses client side languages/tools like HTML or JS to manipulate the HTML DOM tree and/or HTTP request parameters in such way that the outcome of JSF's disabled, readonly or even rendered attribute is altered.
Imagine what would happen if the JSF developer checked an user's role in such a boolean attribute against the admin role like so disabled="#{not user.hasRole('ADMIN')}" and a hacker manipulated it in such way that it isn't disabled anymore for non-admin users. That's exactly why you can only change the mentioned attributes (and the required attribute and all converters, validators, event listeners, etc) via the server side.
You can use <f:ajax> in any ClientBehaviorHolder component to achieve the requirement. You can let JSF generate a HTML <div> via <h:panelGroup layout="block">, which is also a ClientBehaviorHolder:
<h:form>
<h:panelGroup layout="block">
Click this div to toggle the input.
<f:ajax event="click" listener="#{bean.toggle}" render="input" />
</h:panelGroup>
<h:inputText id="input" ... disabled="#{not bean.enabled}" />
</h:form>
With this #ViewScoped managed bean (#RequestScoped wouldn't work for reasons mentioned in #5 of commandButton/commandLink/ajax action/listener method not invoked or input value not updated):
#Named
#ViewScoped
public class Bean implements Serializable {
private boolean enabled;
public void toggle() {
enabled = !enabled;
}
public boolean isEnabled() {
return enabled;
}
}
See also:
What is the need of JSF, when UI can be achieved from CSS, HTML, JavaScript, jQuery?
Why JSF saves the state of UI components on server?
Unrelated to the concrete problem, head to the following answers in case you're actually interested in how to obtain the HTML representation of JSF components via JS/jQuery:
How to select JSF components using jQuery?
How can I know the id of a JSF component so I can use in Javascript

Replacing a managed bean instance from the backing bean

Doing a CRUD, I have a RequestScoped Ticket bean.
I have an XHTML page that until now I have been using to create the new Tickets; the ticket fields are populated directly (#{ticket.description} and so on)
Now I have a search form that lists the tickets, and with each ticket a link with the ticket id as a parameter. I want the backing bean to retrieve the ticket bean from EJB/JPA (already done) and put it into the request. I see 3 ways to do so:
Copy the data from the bean retrieved from JPA into the bean provided by injection. Ugly / prone to omissions.
Use ExternalContex#getRequestMap and put the bean there myself. Does not look very proper. Am I right?
Include my ticket bean into another bean class so I can do myNewBean.setTicket(ticketFromJpa);. Seems the best of the options, yet I do not like having to prefix all my EL in the page just for this.
There is out there any cleaner, more proper way of doing what I want?
Thanks in advance.
UPDATE:
To reword what I want, with a little more information. I have a commandLink in page P1, that calls action A in backing bean B1. B1 does its logic and redirects to page P2.
I wanted to set a (request scoped) bean B2 in action A, and that B2 would be available to draw P2.
From experiments, I have found that after leaving action A the framework creates a new B2 request scoped bean, so it looks like that the request scope is shorter than I expected.
The flash scope propesed by Damian looks like it works more like I want, but forces me to redesign the page around #{flash} and that (when I want to use it to create a new bean) I must also add the bean to the flash in an action method (currently it justs goes to the page and the managed bean is available)
I expected a Request scoped bean to be maintained since
To view the ticket in another page, you could do one of the following:
1) use h:link with a f:param containing the ID of the ticket
<h:link value="#{ticket.description}" outcome="/viewTicket" >
<f:param name="id" value="#{ticket.id}" /
</h:link>
Then in the viewTicket.xhtml (or whatever you called the page) you read the ID parameter, and get the ticket from JPA.
This is how the managed would basically look like:
#ManagedBean
#ViewScoped
public class ViewTicketMBean implements Serializable {
private String ticketId;
private Ticket ticket;
#PostConstruct
public void init() {
ticketId = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id");
// read ticket from JPA by ID
}
}
2) instead of h:link, if you don't want to expose ticket ID, you can use a commandLink, which before navigating to viewTicket.xhtml, gets the ticket from JPA and puts it in flash scope. Then, in viewTicket you get the ticket from the flash scope. This is how the action method of the commandLink couldl look like
Ticket ticket = null;
// get ticket from JPA
FacesContext.getCurrentInstance().getExternalContext().getFlash().put("ticket", ticket);
return "/viewTicket";

viewscoped bean doesn't save session parameter

I have a page with datatable with product information from which at a product selection action I redirect to product info page passing a parameter:
configurableNavigationHandler.performNavigation("productInfo?faces-redirect=true&prId=" + selectedCpl.getP().getPrId());
In my viewscoped bean in my init method I get the request parameter and fill the objects needed:
#ManagedBean
#ViewScoped
public class ProductInfo implements Serializable {
private Product p;
private Integer prId;
#PostConstruct
private void init() {
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
if (request.getParameter("prId") != null) {
prId = Integer.parseInt(request.getParameter("prId"));
p = pf.find(prId);
On my productInfo.xhtml I have a product info view and a dialog to edit the product info but when I press submit my the request parameter is null:
<p:commandButton styleClass="button-edit" value="Submit" actionListener="#{productInfo.saveProduct()}" update="prodInfo" oncomplete="dlg.hide();"/>
I'm using jsf 2.0 with primefaces elements.
Can anyone help me? Thank you.
That's not a session parameter. That's a request parameter. That it's null is because you are not sending it along with the submit request. Send it by <f:param>.
<p:commandButton ...>
<f:param name="prId" value="#{productInfo.prId}" />
</p:commandButton>
Unrelated to the concrete problem, there are several other potential problems. First, the view scoped bean should not be recreated when you submit the form. Perhaps you're using tag handlers in the view. Second, you should absolutely avoid hauling the raw javax.servlet API from under the JSF covers as much as possible. Use ExternalContext#getRequestParameterMap() instead. Third, the <f:viewParam> is much cleaner than that postconstruct. Fourth, redirecting by a navigation handler smells like a design problem in the view, e.g. why not use just a GET link?
The in-depth explanations on all of those issues are available in Communication in JSF 2.0.

A multi-step wizard form with multiple <ui:include>s and one request scoped bean, how to proceed to next step?

We are working with JSF 2.0. Actually, we are migrating from JSF 1.2 to JSF 2.0.
I have a form on mainPage.xhtml wherein we include 3 forms by <ui:include> tag.
The mainPage.xhtml page look like below,
<c:if test="#{myManagedBean.stepRender eq 'firstPage'}">
<ui:include src="/public/firstPage.xhtml"/>
</c:if>
<c:if test="#{myManagedBean.stepRender eq 'secondPage'}">
<ui:include src="/public/secondPage.xhtml"/>
</c:if>
<c:if test="#{myManagedBean.stepRender eq 'thirdPage'}">
<ui:include src="/public/thirdPage.xhtml"/>
</c:if>
The firstPage.xhtml has a few input fields and one <h:commandButton> with action="myManagedBean.continueStepOne".
The secondPage.xhtml has a few input fields and one <h:commandButton> with action="myManagedBean.continueStepTwo".
The thirdPage.xhtml has also a few input fields and one <h:commandButton> with action="myManagedBean.continueStepThree".
I used only one bean for all included pages and mainPage.xhtml page. The bean is request scoped.
The code of the bean class
public String continueStepOneAction(){
== validation logic ==
stepRender="secondPage";
return "mainPage";
}
public String continueStepTwoAction(){
== validation logic ==
stepRender="thirdPage";
return "mainPage";
}
Initially when I send a request to mainPage.xhtml, depending on the stepRender value, it will include a particular page which is initially firstPage.xhtml.
If we enter data and click on commandbutton, then it will call continueStepOne action. When validations are successful, then stepRender value is changed. If it equals to secondPage, then secondPage.xhtml page will be included. And so on for the thirdPage.
The problem is, from firstPage to secondPage the method call and navigation are working properly, but in secondPage, when we click on the command button, then it will call only the constructor of myManagedBean and it is not calling the continueStepThreeAction() method in the bean. It will navigate to firstPage.xhtml, because in the stepRender value defaults to firstPage.
I am not able to understand where is the problem is. How can I solve it?
is your managed bean in request scope?, try it pushing to session scope by declaring it as session scoped bean.

Is there any way to have a bean in ViewScope and RequestScope at same time

I have a table of Items, and in each row there is a link that forwards to the edit item page. To load data in the edit page I need the managed bean in request scope (if I put it in view scope I loose data in the forward).
To use ajax in the edit page I need the managed bean in view Scope due to some values I must keep. If I were working with JSF 1.0 and RichFaces I would do it with request scope and a4j:keepalive.
How do I get this funcionality with PrimeFaces and JSF 2.0 or how can I redefine the interface to get this?
Ok, finally based on the post below this is what worked for me:
CommandButton in the Items table:
<o:commandButton id="editButton"
action="#{itemTableMB.editItem(item.id)}" styleClass="botonTabla">
<h:graphicImage styleClass="imagenBotonTabla" url="/resources/images/icons/pencil.png"/>
</o:commandButton>
Action in the managed bean:
public String editItem(Integer id){
return "/pages/items/edit.xhtml?faces-redirect=true&id="+id;
}
edit.xhtml:
<f:metadata>
<f:viewParam id="id" name="id" value="#{itemMB.item.id}" required="true">
</f:viewParam>
<f:event type="preRenderView" listener="#{itemMB.loadItem}" />
</f:metadata>
Listener in itemMB:
public void loadItem(){
this.item = this.itemManager.get(this.item.getId());
}
To load data in the edit page I need the managed bean in Request scope (If I put it in view scope I loose data in the forward).
Are you using a navigation rule (or implicit navigation) without a redirect?
One solution would be to put backing beans of both the "table of items"-page and the "edit item"-page in view scope, and then go from the first to the second one directly via a GET request (e.g. using <h:link>) or a POST/redirect with a request parameter representing the row on which the user clicked.
Use <f:viewParam> on the second page to conveniently convert the request parameter back to an entity representing the item being edited.
If you were indeed using navigation without redirect, then this has the additional benefit that you won't suffer from the notorious 'one-URL-behind-problem', which can be rather confusing to users of your application and be a nightmare for support.

Resources