In RichFaces 4.1, rich:progressBar 'currentValue' from the ManagedBean does not updating with for-loop.
progressBar.xhtml
<h:form id="formProgress">
<h:commandLink action="#{progressBarBean.startProcess}" value="click here"/>
<rich:progressBar mode="ajax" value="#{progressBarBean.currentValue}" interval="1000" id="pb"
enabled="#{progressBarBean.enabled}" minValue="0" maxValue="100">
<h:outputText value="Retrieving #{progressBarBean.currentValue} of #{progressBarBean.totalRecords}" />
</rich:progressBar>
</h:form>
Bean
package ap;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class ProgressBarBean implements Serializable {
private static final long serialVersionUID = 8775622106408411357L;
private boolean enabled = false;
private Integer totalRecords;
private Integer currentValue;;
public String startProcess() {
setEnabled(true);
setTotalRecords(100);
return null;
}
public Integer getCurrentValue() {
if (isEnabled()) {
for(currentValue=0;currentValue < totalRecords;) {
currentValue++;
}
}
return currentValue;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Integer getTotalRecords() {
return totalRecords;
}
public void setTotalRecords(Integer totalRecords) {
this.totalRecords = totalRecords;
}
}
When i click the 'click here' link, the currentValue updates very fastly and reaches the totalRecords to 100 suddenly. It was not updating in the incremental way(present value in for-loop). The progress bar is not updated by the present value return by the method.
Any help please.
There are two problems: your Java code does not do what you want it to do and you're not telling the page to update (that won't happen automatically).
Take a look at the getCurrentValue() again: It increments currentValue from 0 to 100 and returns the result which is 100. #{progressBarBean.currentValue} does not care (or know) what happens with the variable, it only cares about the result of the getCurrentValue() method.
So in order for it all to work it will have to look like this:
Page
<a4j:commandLink action="#{progressBarBean.startProcess}" value="click here" render="pb" execute="#this"/>
<rich:progressBar mode="ajax" value="#{progressBarBean.currentValue}" interval="1000" id="pb"
enabled="#{progressBarBean.enabled}" minValue="0" maxValue="100">
<a4j:ajax event="begin" listener="#{progressBarBean.increment}" render="text"/>
<h:outputText value="Retrieving #{progressBarBean.currentValue} of #{progressBarBean.totalRecords}" id="text" />
</rich:progressBar>
The a4j:ajax is fired each second (i.e. each interval), it increments the currentValue and updates the text.
You also need a4j:commandLink (or a4j:ajax inside the h:commandLink) in order to rerender the progressbar - in your example you enable the progressbar in the bean but the value on the page does not change.
Bean
public Integer getCurrentValue() {
return currentValue;
}
public void increment() {
if (isEnabled() && currentValue < totalRecords) {
currentValue++;
}
}
Ask if anything isn't clear.
Related
I'm working on a State-City Dropdown based on ajax requests in JSF 2.0. The thing is that i want to reuse this jsf module (beans and xhtmls) for future implementations.
Is there any solution that make it easy to the parentBean retrieve the id from a dropdown that is implemented in another file ?
The first solution that it came in my head was using ui:include but i'm experiencing null value at the variable that store the final selected option.
Here is the snippet. The basic structure is:
StateCityBean (Ajax Menu controller)
stateCityBean.xhtml (Ajax Menu)
ParentBean (selectedCity variable is used here)
parentBean.xhtml (ui:include to stateCityBean.xhtml is here)
stateCityBean
#ManagedBean(name = "stateCityBean")
#ViewScoped
public class StateCityBean implements Serializable {
private static final long serialVersionUID = 7344375121014662582L;
private static final Logger LOG = Logger.getLogger(MenuEstadoCidadeBean.class);
private Collection<SelectItem> stateList;
private Collection<SelectItem> stateCity;
private String selectedState; //not used yet, test purpose
private Integer selectedCity; //not used yet, test purpose
//All the code here is perfectly fine. Cities are loading based on the selected state in parentBean.
}
stateCityBean.xhtml
<t:subform id="stateCitySelectMenu">
<span class="label tamanho20">STATE:</span>
<span aria-live="polite">
<h:selectOneMenu class="tamanho10" id="stateList" name="stateList" value="#{bean.selectedState}" title="State">
<f:selectItems value="#{stateCityBean.stateList}" />
<f:ajax event="valueChange" render="CityList" listener="#{stateCityBean.searchCities(bean.selectedState)}" onevent="loadingGif"/>
</h:selectOneMenu>
</span>
<span class="tamanho20">CITY:</span>
<span aria-live="polite">
<h:selectOneMenu class="tamanho20" title="City" id="CityList" name="CityList" value="#{bean.selectedCity}">
<f:selectItems value="#{stateCityBean.cityList}" />
</h:selectOneMenu>
</span>
</t:subform>
parentBean.xhtml
<ui:include src="stateCityBean.xhtml">
<ui:param name="bean" value="#{parentBean}" />
</ui:include>
parentBean.java
#ManagedBean
#RequestScoped
public class parentBean implements Serializable {
private static final long serialVersionUID = -874412419784226660L;
private static final Logger LOG = Logger.getLogger(parentBean.class);
String selectedState;
Integer selectedCity;
public String getSelectedState() {
return selectedState;
}
public void setSelectedState(String selectedState) {
this.selectedState = selectedState;
}
public Integer getSelectedCity() {
return selectedCity;
}
public void setSelectedCity(Integer selectedCity) {
this.selectedCity= selectedCity;
}
public void doSomethingWithTheSelectedCityId(){
//bla bla bla
return "anotherPage"
}
}
Ok. I solved the problem.
Apparently was the s
at stateCity.xhtml i removed:
<t:subform id="stateCitySelectMenu">
And that's it.
My progressbar is not updated, why ?
The controller method is called as it should and the progess variable is correctly incremented:
XHTML
<p:dialog>
<h:outputLabel value="Count:" for="batchCount"/>
<p:inputText id="batchCount" required="true"
value="#{batchModel.count}">
</p:inputText>
<p:commandButton id="generateBatchButton" value="GO"
actionListener="#{batchController.sendBatch()}"
onclick="PF('progressVar').start();"
oncomplete="PF('dialogBatchParams').hide();"/>
<p:progressBar id="progressBar"
widgetVar="progressVar"
value="#{batchModel.progress}"
labelTemplate="{value}%">
</p:progressBar>
</p:dialog>
CONTROLLER Method
public void sendBatch() {
for (int i = 0; i < batch.size(); i++) {
batchModel.setProgress(Math.round(100 * (float) i / (float) batch.size()));
// Do stuff here
}
}
MODEL
#Named
#ViewScoped // or #SessionScoped
public class BatchModel implements Serializable {
private static final long serialVersionUID = 1L;
private int count = 100;
private int progress;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public int getProgress() {
return progress;
}
public void setProgress(int progress) {
this.progress = progress;
}
}
My progress is correctly updated, I get this output when logging it:
2016-10-19 10:08:49,707 INFO controller.BatchController -> Sending batch
0
10
20
30
40
50
60
70
80
90
2016-10-19 10:08:57,432 INFO controller.BatchController -> Done sending batch
I am using PF 6. I tried with and without "update" tag, and I played around with the ajax tag, but no dice.
Your question started of with a RequestScoped bean. Those are created each request. Since an update of the bar requires a request, you will get a new bean with progress set to 0 again.
It's best to use ViewScoped on your bean (and controller).
Also, you are missing ajax="true" in your progress bar (it expects you to do the updates on the client side). You should change it to:
<p:progressBar id="progressBar"
ajax="true"
widgetVar="progressVar"
value="#{batchModel.progress}"
labelTemplate="{value}%"/>
I have been following this tutorial
http://www.primefaces.org/showcase-ext/sections/dynaform/basicUsage.jsf
I have been able to create tree Dynaform objects and send it to the page. But I am having a hard time obtaining the values that the user entered once they clicked submit. I want to be able to get these values in the backbean.
Here is submit button
<p:commandButton value="Submit" action="#{dynaFormController.submitForm}"
process="dynaForm" update=":mainForm:dynaFormGroup :mainForm:inputValues"
oncomplete="handleComplete(xhr, status, args)"/>
<p:commandButton type="reset" value="Reset" style="margin-left: 5px;"/>
I know the submit calls this function
<h:outputScript id="dynaFormScript" target="body">
/* <![CDATA[ */
function handleComplete(xhr, status, args) {
if(args && args.isValid) {
PF('inputValuesWidget').show();
} else {
PF('inputValuesWidget').hide();
}
}
/* ]]> */
</h:outputScript>
Then in the bean we have:
public String submitForm() {
FacesMessage.Severity sev = FacesContext.getCurrentInstance().getMaximumSeverity();
boolean hasErrors = (sev != null && (FacesMessage.SEVERITY_ERROR.compareTo(sev) >= 0));
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.addCallbackParam("isValid", !hasErrors);
return null;
}
How would I be able to get either the fields values from the submitted form?
I have 3 dynaforms that I would like to submit them and be able to get the values in the back bean. Can anyone explain? I tried looking up some tutorials but I didn't find any explaining this.
Thanks.
It's the same as plain JSF.
You need a variable in your bean, its getters and setters.
Then, you compare it to the DynaFormControl.
#ManagedBean
#SessionScoped
public class DynaFormController implements Serializable {
private static final long serialVersionUID = 1L;
private DynaFormModel model;
private BookProperty bookProperty;
public String getBookProperty() {
return bookProperty;
}
public void setBookProperty(BookProperty bookProperty) {
this.bookProperty = bookProperty;
}
public String submitForm() {
//your code
List<DynaFormControl> controls = model.getControls();
for (DynaFormControl control : controls) {
if(control.getData() instanceof BookProperty) {
BookProperty bp = (BookProperty) c.getData();
//use the object
}
}
return null;
}
}
I added an <p:inputSwitch> in my JSF page, but this is not working.
Get and set method is not called when I change the stat
The JSF page :
<p:inputSwitch value="#{SystemController.statSystem}" />
The managed bean
#ManagedBean
#ViewScoped
public class SystemController extends AbstractController implements Serializable {
private Boolean statSystem;
public Boolean getStatSystem() {
return statSystem;
}
public void setStatSystem(Boolean statSystem) {
this.statSystem=statSystem;
}
I added an ajax tag and it works ! Get and Set methods are working now.
<p:inputSwitch value="#{SystemController.statSystem}" >
<p:ajax />
</p:inputSwitch>
Your statSystem variable is not initialized.
Init like:
private Boolean statSystem = false;
or instead change data type to primitive:
private boolean statSystem;
I'm having a hard time doing basic AJAX updates of a timeline.
Let me start with a basic example where I want to update the start and end times of a timeline based on the selection of a dropdown list:
<h:form id="form">
<h:outputLabel for="period" value="#{str.schedule_period}"/>
<h:selectOneMenu id="period" value="#{timelineController.period}" label="#{str.schedule_period}">
<f:selectItems value="#{timelineController.periodWeeks}" />
<p:ajax event="change" update="timeline" />
</h:selectOneMenu>
<pe:timeline id="timeline" value="#{timelineController.model}"
editable="true"
eventMargin="0"
minHeight="120"
stackEvents="false"
start="#{timelineController.timelineStart}"
min="#{timelineControllertimelineStart}"
end="#{timelineController.timelineEnd}"
max="#{timelineController.timelineEnd}"
showNavigation="false" showButtonNew="false"
showCurrentTime="false"
axisOnTop="true"
timeZone="#{timelineController.timezone}"
zoomMin="28800000"
dropActiveStyleClass="ui-state-highlight" dropHoverStyleClass="ui-state-hover">
<p:ajax event="drop" listener="#{timelineController.onDrop}"
global="false" process="timeline"/>
</pe:timeline>
</h:form>
When I select an item in the dropdown list, an AJAX event fires and sets the period property in the backing bean, but the new value is not reflected in the timeline component. As a workaround, I wrapped the timeline in a p:outputPanel and updated the wrapper instead and it works:
...
<h:selectOneMenu id="period" value="#{timelineController.period}" label="#{str.schedule_period}">
<f:selectItems value="#{timelineController.periodWeeks}" />
<p:ajax event="change" update="wrapper" />
</h:selectOneMenu>
...
<p:outputPanel id="wrapper">
<pe:timeline id="timeline" value="#{timelineController.model}"
editable="true"
eventMargin="0"
minHeight="120"
stackEvents="false"
start="#{timelineController.timelineStart}"
min="#{timelineControllertimelineStart}"
end="#{timelineController.timelineEnd}"
max="#{timelineController.timelineEnd}"
showNavigation="false" showButtonNew="false"
showCurrentTime="false"
axisOnTop="true"
timeZone="#{timelineController.timezone}"
zoomMin="28800000"
dropActiveStyleClass="ui-state-highlight" dropHoverStyleClass="ui-state-hover">
<p:ajax event="drop" listener="#{timelineController.onDrop}"
global="false" process="wrapper"/>
</pe:timeline>
</p:outputPanel>
Note that I also had to change the process attribute of p:ajax to wrapper.
So my first question is: why doesn't the update work without wrapping the timeline component?
My second question is about drag and drop. As you can you see from my code above, I have attached a drop listener to the timeline. And I'm also able to drag and drop events from a p:dataList BEFORE I make a selection in the dropdown list. Once I select a new period in the dropdown list, the timeline gets updated appropriately, but I'm not able to drag and drop events to the timeline any more (the onDrop listener doesn't get fired). Here's my p:dataList:
<p:dataList id="eventsList" value="#{timelineController.users}"
var="user" itemType="none">
<h:panelGroup id="eventBox" layout="box" style="z-index:9999; cursor:move;">
#{user.toString()}
</h:panelGroup>
<p:draggable for="eventBox" revert="true" helper="clone" cursor="move"/>
</p:dataList>
Any ideas what's wrong here?
I'm also including the TimelineController class for reference:
#ManagedBean
#ViewScoped
public class TimelineController {
#EJB UserService userDao;
private TimelineModel model;
private String name;
private ZoneId timezone;
private Period period;
private Duration defaultShiftDuration;
private LocalDateTime timelineStart;
private LocalDateTime timelineEnd;
#PostConstruct
protected void initialize() {
timezone = ZoneId.of("Europe/Berlin);
period = Period.ofWeeks(2);
defaultShiftDuration = Duration.ofHours(8);
timelineStart = LocalDateTime.now().with(DayOfWeek.MONDAY).withHour(0).withMinute(0).truncatedTo(ChronoUnit.MINUTES);
// create timeline model
model = new TimelineModel();
}
public TimelineModel getModel() {
return model;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTimezone() {
return timezone.getId();
}
public void setTimezone(String timezone) {
this.timezone = ZoneId.of(timezone);
}
public List<SelectItem> getPeriodWeeks() {
List<SelectItem> weeks = Lists.newArrayList();
weeks.add(new SelectItem(1, "1 " + JsfUtil.getStringResource("schedule_week")));
weeks.add(new SelectItem(2, "2 " + JsfUtil.getStringResource("schedule_weeks")));
weeks.add(new SelectItem(3, "3 " + JsfUtil.getStringResource("schedule_weeks")));
return weeks;
}
public int getPeriod() {
return period.getDays() / 7;
}
public void setPeriod(int nWeeks) {
this.period = Period.ofWeeks(nWeeks);
timelineEnd = null;
}
public Date getTimelineStart() {
return Date.from(timelineStart.atZone(timezone).toInstant());
}
public Date getTimelineEnd() {
if (timelineEnd == null) {
timelineEnd = timelineStart.plus(period);
}
return Date.from(timelineEnd.atZone(timezone).toInstant());
}
public void setStartsOn(String startsOn) {
timelineStart = LocalDateTime.parse(startsOn + "T00:00");
timelineEnd = null;
}
public List<User> getUsers(){
return userDao.findAll();
}
public void onDrop(TimelineDragDropEvent e) {
// get dragged model object (event class) if draggable item is within a data iteration component,
// update event's start and end dates.
User user = (User) e.getData();
Date endDate = Date.from(e.getStartDate().toInstant().plus(defaultShiftDuration));
// create a timeline event (not editable)
TimelineEvent event = new TimelineEvent(user, e.getStartDate(), endDate, true, e.getGroup());
// add a new event
TimelineUpdater timelineUpdater = TimelineUpdater.getCurrentInstance(":form:timeline");
model.add(event, timelineUpdater);
}
}
The problem was a missing widgetVar attribute in the timeline component. This looks like a bug to me, since I'm not using the client side API of the component. I will file a bug in PF Extensions project.