I have to retain information across a 3 page flow which uses ajax functionlity across all the three pages . So this is how i have designed the structure
1 ) One view scoped bean ( @ ViewScoped) containing action methods, Ajax methods as well as attributes needed for three pages
2) Wizard.xhtml
|_ Step1.xhtml
|_ step2.xhtml
|_ step3.xhtml
The Wizard xhtml includes these three facelets with conditional rendering based on which page the user is currently operating . So basically while navigating from step 1 to step 2 I would mention wizard.xhtml . so my doubt how does the bean instance live here . Does it live throughout the flow ( and if yes will it cause any performance issues) or it gets destroyed and then recreated again ?
Using this approach , I am able to retain the values across all pages but I wanted to know if this is really a good design approach and what implications/disadvantages it might bring to the table .? Thanks in advance :)
You basically have 2 options of constructing a wizard-type flow. The first option is to show all steps within one view, the other one is to have the number of views as there are steps in your wizard.
As Xtreme Biker rightfully mentions the most natural way of designing the view is separating every step in a conditionally rendered component, like <h:panelGroup>
and updating bean's property currentStep upon entering a different wizard step.
The basic view setup:
<h:panelGroup id="step1">
<h:panelGroup rendered="#{bean.currentStep eq 1}">
<ui:include src="step1.xhtml"/>
</h:panelGroup>
</h:panelGroup>
...
The included page (step2.xhtml):
...
<h:commandButton value="Back" action="#{bean.back}">
<f:ajax execute="step2" render="step1 step2"/>
</h:commandButton>
<h:commandButton value="Forward" action="#{bean.forward}">
<f:ajax execute="step2" render="step1 step2"/>
</h:commandButton>
...
Backing bean:
@ManagedBean
@ViewScoped
public class Bean implements Serializable {
...
private int currentStep = 1;//getter+setter
public String forward() {
...
if(currentStep == 2) {
doSomethingWithValues();
currentStep++;
}
...
}
public String back() {
...
if(currentStep == 2) {
clearNecessaryValues();
currentStep--;
}
...
}
}
This approach is good if you want to embed a customized content in your view. If you are good with a 'standard' approach, you would rather not reinvent the wheel and use <p:wizard>
tag of Primefaces library, which does basically the same under the covers.
If you are going to navigate to a different view by calling back/forward buttons and returning different navigation case outcomes each time your job can be done by using a flash
object to transfer needed data to the next view.
So, the setup is going to be: wizard/step2.xhtml
(one view per step) and one view scoped bean Bean
.
One of the views (the second view)
...
<h:commandButton value="Back" action="#{bean.back}">
</h:commandButton>
<h:commandButton value="Forward" action="#{bean.forward}">
</h:commandButton>
...
Backing bean:
@ManagedBean
@ViewScoped
public class Bean implements Serializable {
...
private int currentStep = 1;//getter+setter
@ManagedProperty("#{flash}")
private Flash flash;//getter+setter
private Data step1Data;
private Data step2Data;
private Data step3Data;
...
@PostConstruct
public void init() {
int step = Integer.parseInt(flash.get("newStep"));
Data step1 = (Data)flash.get("step1");
Data step2 = (Data)flash.get("step2");
Data step3 = (Data)flash.get("step3");
this.currentStep = step;
this.step1Data = step1;
this.step2Data = step2;
this.step3Data = step3;
...
}
public String forward() {
...
if(currentStep == 2) {
doSomethingWithValues();
currentStep++;
flash.put("step", currentStep);
flash.put("step1", step1Data);
flash.put("step2", step2Data);
return "wizard/step3?faces-redirect=true"
}
...
}
public String back() {
...
if(currentStep == 2) {
clearNecessaryValues();
currentStep--;
flash.put("step", currentStep);
flash.put("step1", step1Data);
return "wizard/step1?faces-redirect=true"
}
...
}
}