I would like to create a registration form with two PrimeFaces components: Wizard and Progress Bar. For the backing bean, I am using the following one:
Backing bean: UserWizard.xhtml
import java.io.Serializable;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import org.primefaces.context.RequestContext;
import org.primefaces.event.FlowEvent;
@ViewScoped
@Named
public class UserWizard implements Serializable {
private User user = new User();
private boolean skip;
private Integer progress = 0;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public void save() {
FacesMessage msg = new FacesMessage("Successful", "Welcome :" + user.getFirstname());
FacesContext.getCurrentInstance().addMessage(null, msg);
}
public boolean isSkip() {
return skip;
}
public void setSkip(boolean skip) {
this.skip = skip;
}
public String onFlowProcess(FlowEvent event) {
String oldStep = event.getOldStep();
Integer oldValue = getStepNumber(oldStep);
String newStep = event.getNewStep();
Integer newValue = getStepNumber(newStep);
if(oldValue < newValue)
progress += 25;
else
progress += 25;
return event.getNewStep();
}
public Integer getStepNumber(String Step) {
Integer StepNumber;
switch(Step) {
case "personal":
StepNumber = 1;
break;
case "address":
StepNumber = 2;
break;
case "contact":
StepNumber = 3;
break;
default:
StepNumber = 4;
break;
}
return StepNumber;
}
public Integer getProgress() {
return progress;
}
public void setProgress(Integer progress) {
this.progress = progress;
}
public void onComplete() {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Progress Completed"));
}
public void cancel() {
progress = null;
}
}
Registration Form: registration.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<body>
<f:view contracts="#{view.locale.language}">
<ui:composition template="/template.xhtml">
<ui:define name="centralBody">
<h:form>
<p:growl id="growl" sticky="true" showDetail="true"/>
<p:wizard flowListener="#{userWizard.onFlowProcess}">
<p:tab id="personal" title="Personal">
<p:panel header="Personal Details">
<p:messages />
<h:panelGrid columns="2" columnClasses="label, value">
<h:outputText value="Firstname: *" />
<p:inputText value="#{userWizard.user.firstname}" required="true" label="Firstname"/>
<h:outputText value="Lastname: *" />
<p:inputText value="#{userWizard.user.lastname}" required="true" label="Lastname"/>
<h:outputText value="Age: " />
<p:inputText value="#{userWizard.user.age}" />
<h:outputText value="Skip to last: " />
<h:selectBooleanCheckbox value="#{userWizard.skip}" />
</h:panelGrid>
</p:panel>
</p:tab>
<p:tab id="address" title="Address">
<p:panel header="Address Details">
<p:messages />
<h:panelGrid columns="2" columnClasses="label, value">
<h:outputText value="Street: " />
<p:inputText value="#{userWizard.user.street}" />
<h:outputText value="Postal Code: " />
<p:inputText value="#{userWizard.user.postalCode}" />
<h:outputText value="City: " />
<p:inputText value="#{userWizard.user.city}" />
<h:outputText value="Skip to last: " />
<h:selectBooleanCheckbox value="#{userWizard.skip}" />
</h:panelGrid>
</p:panel>
</p:tab>
<p:tab id="contact" title="Contact">
<p:panel header="Contact Information">
<p:messages />
<h:panelGrid columns="2" columnClasses="label, value">
<h:outputText value="Email: *" />
<p:inputText value="#{userWizard.user.email}" required="true" label="Email"/>
<h:outputText value="Phone: " />
<p:inputText value="#{userWizard.user.phone}"/>
<h:outputText value="Additional Info: " />
<p:inputText value="#{userWizard.user.info}"/>
</h:panelGrid>
</p:panel>
</p:tab>
<p:tab id="confirm" title="Confirmation">
<p:panel header="Confirmation">
<h:panelGrid id="confirmation" columns="3" columnClasses="grid,grid,grid">
<h:panelGrid columns="2" columnClasses="label, value">
<h:outputText value="Firstname: " />
<h:outputText value="#{userWizard.user.firstname}" styleClass="outputLabel"/>
<h:outputText value="Lastname: " />
<h:outputText value="#{userWizard.user.lastname}" styleClass="outputLabel"/>
<h:outputText value="Age: " />
<h:outputText value="#{userWizard.user.age}" styleClass="outputLabel"/>
</h:panelGrid>
<h:panelGrid columns="2" columnClasses="label, value">
<h:outputText value="Street: " />
<h:outputText value="#{userWizard.user.street}" styleClass="outputLabel"/>
<h:outputText value="Postal: " />
<h:outputText value="#{userWizard.user.postalCode}" styleClass="outputLabel"/>
<h:outputText value="City: " />
<h:outputText value="#{userWizard.user.city}" styleClass="outputLabel"/>
</h:panelGrid>
<h:panelGrid columns="2" columnClasses="label, value">
<h:outputText value="Email: " />
<h:outputText value="#{userWizard.user.email}" styleClass="outputLabel"/>
<h:outputText value="Phone " />
<h:outputText value="#{userWizard.user.phone}" styleClass="outputLabel"/>
<h:outputText value="Info: " />
<h:outputText value="#{userWizard.user.info}" styleClass="outputLabel"/>
<h:outputText />
<h:outputText />
</h:panelGrid>
</h:panelGrid>
<p:commandButton value="Submit" actionListener="#{userWizard.save}" update="growl" process="@this"/>
</p:panel>
</p:tab>
</p:wizard>
<p:progressBar id="progressBar" widgetVar="pbAjax" ajax="true" value="#{UserWizard.progress}" labelTemplate="{value}%" styleClass="animated" global="false">
<p:ajax event="complete" listener="#{UserWizard.onComplete}" update="growl" oncomplete="PF('startButton2').enable()"/>
</p:progressBar>
</h:form>
</ui:define>
</ui:composition>
</f:view>
</body>
</html>
My purpose is that, when the registration flow goes through the different registration tabs (Personal, Address, Contact and Confirmation), the progress bar in the lower part of the screen to be updated according to the next or back buttons. In order to achieve this, I want to use the method
public String onFlowProcess(FlowEvent event) {
String oldStep = event.getOldStep();
Integer oldValue = getStepNumber(oldStep);
String newStep = event.getNewStep();
Integer newValue = getStepNumber(newStep);
if(oldValue < newValue)
progress += 25;
else
progress -= 25;
return event.getNewStep();
}
public Integer getStepNumber(String Step) {
Integer StepNumber;
switch(Step) {
case "personal":
StepNumber = 1;
break;
case "address":
StepNumber = 2;
break;
case "contact":
StepNumber = 3;
break;
default:
StepNumber = 4;
break;
}
return StepNumber;
}
But I do not know how to update the progress bar. I have tried:
<p:wizard ... update="progress_bar">
. I have realized that the attribute update is not allowed for the Wizard component.<p:ajax listener="#{UserWizard.onFlowProcess}" update="@this">
within the <p:progressBar>
element.Since the wizard (at least in/upto PrimeFaces 6.0) does not seem to support the update
attribute when using a flowListener
and no explicit ajax events are supported, the only option I see is to use the PrimeFaces RequestContext
to update the other component. Keep in mind that you need to use the 'full absolute path' to the element, so including all the id's of the namingcontainers it is in. It is best then to explicitly assign id's to all namingcontainers (including a form!)
So using
RequestContext.getCurrentInstance().update("formId:progress_bar");
like e.g.
public String onFlowProcess(FlowEvent event) {
String oldStep = event.getOldStep();
Integer oldValue = getStepNumber(oldStep);
String newStep = event.getNewStep();
Integer newValue = getStepNumber(newStep);
if(oldValue < newValue)
progress += 25;
else
progress -= 25;
RequestContext.getCurrentInstance().update("formId:progress_bar");
return event.getNewStep();
}
in the flowListener should work. Keep in mind that in the updates from beans, the ':' as a prefix for absolute paths is not needed, they are assumed to be always absolute