How to execute transition on value change in h:selectOneMenu? - jsf-2

Usually i do something like below. Clicking button execute transition.
<!-- view -->
<h:form>
<h:commandButton action="doit">
<f:ajax render="#form"/>
</h:commandButton>
</h:form>
<!-- flow -->
<transition on="doit">...</transition>
How to fire a transition on change value in (for example) h:selectOneMenu ?
<h:form>
<h:selectOneMenu value="#{selected}">
<f:selectItems value="#{items}/>
<f:ajax event="valueChange" render="#form" />
</h:selectOneMenu>
</h:form>
Edit:
I thought about registering listener to f:ajax and prepare webflow event, but how to use that event... ? Anybody help ?
<h:form>
<h:selectOneMenu value="#{selected}">
<f:selectItems value="#{items}/>
<f:ajax event="valueChange" render="#form" listener="#{bean.changeListener}" />
</h:selectOneMenu>
</h:form>
java:
import javax.faces.event.AjaxBehaviorEvent;
import org.springframework.webflow.execution.Event;
public class Bean {
public void changeListener(AjaxBehaviorEvent event) {
// prepare webflow event
Event e = new Event(event.getSource(), "doit");
// propagate this event... ???
}
}

I recently had a similar issue, and handle primefaces/richfaces events with a similar listener style. Here's an example:
public void changeListener(AjaxBehaviorEvent event) {
RequestContext requestContext = RequestContextHolder.getRequestContext();
RequestControlContext rec = (RequestControlContext) requestContext;
//place variables you need in next flow phase here; flash,application,session scope
rec.getFlashScope().put("someVarIneedInNextFlow", varName);
rec.handleEvent(new Event(this, "flow transition name here, i.e. next-stage"));
return;
}
That should transition to whichever flow event you desire :)

Related

PrimeFaces selectCheckboxMenu: Old values in toggleSelect actionListener

In my JSF view I have a p:selectCheckboxMenu where I want to perform some business logic via AJAX on the selected values.
For a simple change event it works fine, but for a toggleSelect event not. Inside my listener method I am retrieving the old selection, but I am expecting the new selection here.
See the following example:
#ViewScoped
#Named
public class RequestBean implements Serializable {
private List<String> list; // + getter/setter
#PostConstruct
private void init() {
list = new ArrayList<String>() {{
add("one"); add("two"); add("three");
}};
}
public void listener() {
System.out.println("Current content of \"list\":");
for(String s : list) {
System.out.println(s);
}
}
}
in JSF view:
<p:selectCheckboxMenu value="#{requestBean.list}" label="List">
<f:selectItem itemValue="one" itemLabel="one"/>
<f:selectItem itemValue="two" itemLabel="two"/>
<f:selectItem itemValue="three" itemLabel="three"/>
<p:ajax event="toggleSelect" listener="#{requestBean.listener}" />
<p:ajax event="change" listener="#{requestBean.listener}" />
</p:selectCheckboxMenu>
Now lets consider the following use-case: You are entering the view, "one" and "two" are selected. If I click the "select all" checkbox, the outcome is:
Info: Current content of "list":
Info: one
Info: two
But the expected outcome would look like this:
Info: Current content of "list":
Info: one
Info: two
Info: three
For the regular change event it works as expected. Here I am getting the new selection inside the listener. How may I fix it? Or what am I doing wrong?
GlassFish 4.1, running on Java 1.8.0_45
JSF 2.2.10 (Mojarra)
PrimeFaces 5.1
OmniFaces 1.8.1
This issue seems to be related with the listener being called too early. Doing some basic debugging, I've found that toggleSelect invokes the listener method before updating the model values, while the change event does it after modifying them. That's my current code:
RequestBean:
#ViewScoped
#ManagedBean
public class RequestBean implements Serializable {
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
System.out.println("Values set: " + list);
}
private List<String> list;
#PostConstruct
private void init() {
list = new ArrayList<String>() {
{
add("one");
add("two");
add("three");
}
};
}
public void listener() {
System.out.println("Listener called!");
}
}
page.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:comp="http://java.sun.com/jsf/composite/comp"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head />
<h:body>
<h:form>
<p:selectCheckboxMenu value="#{requestBean.list}" label="List">
<f:selectItem itemValue="one" itemLabel="one" />
<f:selectItem itemValue="two" itemLabel="two" />
<f:selectItem itemValue="three" itemLabel="three" />
<p:ajax event="toggleSelect" listener="#{requestBean.listener}" />
<p:ajax event="change" listener="#{requestBean.listener}" />
</p:selectCheckboxMenu>
</h:form>
</h:body>
</html>
And that's the trace for your current steps:
Values set: [one]
Listener called!
Values set: [one, two]
Listener called!
Listener called!
Values set: [one, two, three]
The last one is the toogle selection, as you can see the model is properly updated, but the listener is called before.
Let's play a little bit more with a custom PhaseListener:
Entering RESTORE_VIEW 1
Entering APPLY_REQUEST_VALUES 2
Entering PROCESS_VALIDATIONS 3
Entering UPDATE_MODEL_VALUES 4
Values set: [one]
Entering INVOKE_APPLICATION 5
Listener called!
Entering RENDER_RESPONSE 6
Entering RESTORE_VIEW 1
Entering APPLY_REQUEST_VALUES 2
Entering PROCESS_VALIDATIONS 3
Entering UPDATE_MODEL_VALUES 4
Values set: [one, two]
Entering INVOKE_APPLICATION 5
Listener called!
Entering RENDER_RESPONSE 6
Entering RESTORE_VIEW 1
Entering APPLY_REQUEST_VALUES 2
Listener called!
Entering PROCESS_VALIDATIONS 3
Entering UPDATE_MODEL_VALUES 4
Values set: [one, two, three]
Entering INVOKE_APPLICATION 5
Entering RENDER_RESPONSE 6
As you can see, the model values are always set in the UPDATE_MODEL_VALUES phase, while the change event performs in INVOKE_APPLICATION as it should, toggleSelect listener perform in APPLY_REQUEST_VALUES, which is before in the list.
This seems to be a Primefaces bug, which should be notified in their GitHub branch.
See also:
How to implement a PhaseListener which runs at end of lifecycle?
The listener gets triggered during the second phase of JSF lifecycle: Apply Request Values. The model (requestBean.list) gets updated later, during the fourth phase, Update Model Values. That's why the listener sees the old value.
BalusC posted a general workaround in this answer, but it will cause an infinite loop in this case because of the way PrimeFaces' components wrap and enqueue events. So I suggest going the phase listener way.
View
<f:view beforePhase="#{requestBean.beforePhase}">
<h:form id="form">
<p:selectCheckboxMenu id="list_menu" value="#{requestBean.list}" label="List">
<f:selectItem itemValue="one" itemLabel="one" />
<f:selectItem itemValue="two" itemLabel="two" />
<f:selectItem itemValue="three" itemLabel="three" />
<!-- still need p:ajax toggleSelect to trigger the request -->
<p:ajax event="toggleSelect" />
<p:ajax event="change" listener="#{requestBean.listener}" />
</p:selectCheckboxMenu>
</h:form>
</f:view>
Bean
public void beforePhase(PhaseEvent event) {
if (event.getPhaseId() == PhaseId.INVOKE_APPLICATION) {
Map<String, String> params = FacesContext.getCurrentInstance()
.getExternalContext().getRequestParameterMap();
String eventName = params.get(
Constants.RequestParams.PARTIAL_BEHAVIOR_EVENT_PARAM);
String source = params.get("javax.faces.source");
if ("form-list_menu".equals(source) && "toggleSelect".equals(eventName)) {
listener();
}
}
}
I stumbled into another workaround: basically you need to populate the label text from within the setter for the value. For example:
View
<p:selectCheckboxMenu
value="#{bean.selectedValues}"
label="#{bean.selectedValuesDisplayString}"
widgetVar="checkboxMenuWget">
<f:selectItems value="#{bean.allValues}"/>
<p:ajax event="change" listener="#{bean.updateDisplayString()}" update="#widgetVar(checkboxMenuWget)" />
<p:ajax event="toggleSelect" update="#widgetVar(checkboxMenuWget)" />
</p:selectCheckboxMenu>
Bean
// Properties
private List<String> allValues;
private List<String> selectedValues;
private String selectedValuesDisplayString;
// Getters and Setters
public void setSelectedValues(List<String> selectedValues) {
this.selectedValues = selectedValues;
updateDisplayString();
}
// UI Code
public void updateDisplayString() {
this.selectedValuesDisplayString = // code to generate display string...
}
I also faced this problem. It is still not fixed in PrimeFaces 5.2. My solution how to fix it:
add to faces-config.xml
<component>
<component-type>org.primefaces.component.SelectCheckboxMenu</component-type>
<component-class>yourpackage.SelectCheckboxMenu</component-class>
</component>
create this class:
public class SelectCheckboxMenu extends org.primefaces.component.selectcheckboxmenu.SelectCheckboxMenu {
#Override
public void queueEvent(FacesEvent event) {
FacesContext context = getFacesContext();
String eventName = context.getExternalContext().getRequestParameterMap().get(Constants.RequestParams.PARTIAL_BEHAVIOR_EVENT_PARAM);
if(event instanceof AjaxBehaviorEvent && eventName.equals("toggleSelect")) {
Map<String,String> params = context.getExternalContext().getRequestParameterMap();
String clientId = this.getClientId(context);
boolean checked = Boolean.valueOf(params.get(clientId + "_checked"));
// changed code
ToggleSelectEvent toggleSelectEvent = new ToggleSelectEvent(this, ((AjaxBehaviorEvent) event).getBehavior(), checked);
toggleSelectEvent.setPhaseId(event.getPhaseId());
getParent().queueEvent(toggleSelectEvent);
// end
}
else {
super.queueEvent(event);
}
}
}
You can use remote command for listener like following.
<p:remoteCommand name="test" id="test" actionListener"#{requestBean.listener}" update="#form" process="#form"></p:remoteCommand>
Call Following ways above code on toggleselect
<p:ajax event="toggleSelect" oncomplete="test();" />
Faced the same issue, after searching a while I found that you can bind your update logic to the onchange attribute. Like:
<p:selectCheckboxMenu value="#{requestBean.list}" label="List" onchange="#{requestBean.listener()}">
<f:selectItem itemValue="one" itemLabel="one" />
<f:selectItem itemValue="two" itemLabel="two" />
<f:selectItem itemValue="three" itemLabel="three" />
<p:ajax event="toggleSelect" listener="#{requestBean.listener}" />
<p:ajax event="change" listener="#{requestBean.listener}" />
</p:selectCheckboxMenu>
Consider: onchange="#{requestBean.listener()}" you have to call the listener using braces, otherwise you get an exception.
Don't know which PF version you are using, but I found the solution in this link. (PrimeFaces 5.3)

f:setPropertyActionListener doesn't call setter on backed bean

I cant't understand why in my PrimeFaces view when I call
<f:setPropertyActionListener value="#{item}" target="#{tagController.current}" />
it never calls the backed bean tagController.setCurrent method.
I've the following view:
<h:form id="tableForm" styleClass="form-horizontal">
<h:panelGroup id="dataPanel">
<p:dataTable value="#{tagController.lazyModel}"
lazy="true"
id="table"
var="item"
paginator="true"
rows="10"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="10,20,50" >
<p:column headerText="Name" sortBy="#{item.name}" filterBy="#{item.name}">
<h:outputText value="#{item.name}"/>
</p:column>
<p:column>
<p:commandLink id="testButton" update=":testForm:panel" process="#this">
<h:outputText value="Test" />
<f:setPropertyActionListener value="#{item}" target="#{tagController.current}" />
</p:commandLink >
</p:column>
</p:dataTable>
</h:panelGroup>
</h:form>
<br/>
<h:form id="testForm" styleClass="form-horizontal">
<p:panelGrid columns="4" id="panel" >
<h:outputLabel value="Name: #{tagController.current.name}" />
</p:panelGrid>
</h:form>
and the following backed bean:
#ViewScoped
#Named
public class TagController implements Serializable {
#Inject
TagFacade tagFacade;
protected LazyDataModel<Tag> lazyModel;
protected ResourceBundle bundle = ResourceBundle.getBundle("resources/messages");
#Getter
protected Tag current = new Tag();
public void setCurrent(Tag current) {
System.out.println(">>>>>>>>>> THIS METHOD IS NEVER CALLED ? WHY ?");
this.current = current;
}
public LazyDataModel<Tag> getLazyModel() {
//... omitted for brevity
}
}
The datatable is working well, I see data, the paginator, filters and sorting works well but when I click the test link nothing happens (no exceptions, no javascript errors into the html page, no message print into stdout...).
I'm using Glassfish 3.1.2 / Mojarra 2.1.22 / PrimeFaces 3.5.
Someone can help me ?
Many thanks in advance...
I notice that you are mixing CDI with JSF annotations. there is no view scoped annotations for CDI.you should replace view scoped with a CDI Scope or change #named annotations to #Managedbean
<p:column>
<p:commandButton id="bb" actionListener="#{yourmanagedbean.setmyvalue(item)}"
title="Test" update=":testForm:panel">
</p:commandButton>
</p:column>
In backing bean look like this
public void setCurrent(Tag current) throws Exception {
try {
this.current = current;
} catch (Exception e) {
// ...
}
}
Try to make panelgrid inside a panel and update it.
Since you are using annotation for getter, I suspect that by default the setter will pass directly by your variable.
You should try like this
private Tag current = new Tag();
public void setCurrent(Tag current) {
System.out.println(">>>>>>>>>> THIS METHOD IS NEVER CALLED ? WHY ?");
this.current = current;
}
public Tag getCurrent() {
return this.current;
}
More info :
Lombok Getter setter
I had the same problem but with a different solution. I had the annotations correct and so on, but had a dialog (I didn't even use) that had the following code
<h:inputText value="#{aSBean.newElement.vaSl}" validatorMessage="Nr bitte numerisch eingeben">
<f:validateDoubleRange minimum="1" maximum="999" />
</h:inputText>
Somehow (still don't know why) my setter doesn't work with this piece of code in the dialog. Just adding this in case others have this problem, because it took me days to find out.

Getting selected value of a SelectOneMenu

I'm testing the component "SelectOneMenu" on a jsf page. I'm populating this component dinamically though my ManageBean (that will get all Animals from database).
I would like to know if is possible to see the user selected item of that "SelectOneMenu" (combobox), I'm trying with value="#{animalsManage.animalSelect}" but it is only called on the beginning of the page. Also, I'm using an inputText to see the value of the selected intem of the "SelectOneMenu".
What I'm doing wrong?
JSF:
<body>
<ui:component>
<h:form>
<h:outputText value="Select one Mets File" />
<h:selectOneMenu id="combo" value="#{animalsManage.animalSelected}">
<f:selectItem itemLabel="Select..." noSelectionOption="true"/>
<f:selectItems value="#{animalsManage.allAnimals}" />
</h:selectOneMenu>
<h:inputText id="textbox" value="#{animalsManage.animalSelected }" />
</h:form>
</ui:component>
</body>
ManageBean:
#ManagedBean
#ViewScoped
public class AnimalsManage implements Serializable {
#EJB
private AnimalsFacadeREST animalsFacadeREST;
private String animalSelected;
private List< SelectItem> selectAnimals;
public List<SelectItem> getAllAnimals() {
List<Animals> al = animalsFacadeREST.findAll();
selectAnimals = new ArrayList< SelectItem>();
int i = 0;
for (Animals animal: al) {
selectAnimals.add(new SelectItem(i, animal.getName()));
i++;
}
return selectAnimals;
}
public String getAnimalSelected() {
return animalSelected;
}
public void setAnimalSelected(String animalSelected) {
this.animalSelected = animalSelected;
}
}
There are many solutions to the presented problem. I present here two basic ideas.
Server-side solution. Simply attach <f:ajax> tag inside your <h:selectOneMenu> to update selected values and rerender user's choice, like in
<h:selectOneMenu id="combo" value="#{animalsManage.animalSelected}">
<f:selectItem itemLabel="Select..." noSelectionOption="true"/>
<f:selectItems value="#{animalsManage.allAnimals}" />
<f:ajax execute="combo" render="textbox" />
</h:selectOneMenu>
<h:inputText id="textbox" value="#{animalsManage.animalSelected }" />
If you like, you may also do some custom logic with selected element in ajax listener by specifying listener="#{animalsManage.performCustomAjaxLogic}" of <f:ajax> tag.
Client-side solution. Simply update element with id="textbox" on basic change event. So, if you use jQuery the solution will be
$('#combo').change(function() {
$('#textbox').val($('#combo').val());
});
Thought the client-side solution will bind only text value of your input component.

Can not use hidden commandButton with #RequestScoped backing-bean

I have following sample code.
Initially, only commandButton Two is visible. When I click this button, commandButton One is also visible. But when I click One, the backing-bean method click1 does not get fired.
Following is my code:
xhtml
<h:form id="form1">
<h:inputHidden id="show" value="#{bean.show1}" />
<h:commandButton id="button1" value="One" action="#{bean.click1}"
rendered="#{bean.show1}" />
</h:form>
<h:form id="form2">
<h:inputHidden id="show" value="#{bean.show1}" />
<h:commandButton id="button2" value="Two" action="#{bean.click2}" />
</h:form>
backing-bean
#RequestScoped
#Named("bean")
public class JsfTrial implements Serializable {
private static final long serialVersionUID = 2784462583813130092L;
private boolean show1; // + getter, setter
public String click1() {
System.out.println("Click1()");
return null;
}
public String click2() {
System.out.println("Click2()");
setShow1(true);
return null;
}
}
I found a very informative answer by BalusC.
h:commandLink / h:commandButton is not being invoked
If I understand it correctly, my problem is due to point 5 of this answer.
Does that also mean we can not use hidden commandButton with #RequestScoped backing-bean?
You can use the request scope, you should only pass the condition as a request parameter to the subsequent requests by <f:param> instead of by a JSF hidden input field <h:inputHidden>. The value of the hidden input field is only set in the model during Update Model Values phase, while the condition of the rendered attribute is already evaluated during Apply Request Values phase which is earlier.
So, use <f:param> instead of <h:inputHidden>:
<h:form id="form1">
<h:commandButton id="button1" value="One" action="#{bean.click1}"
rendered="#{bean.show1}">
<f:param name="show1" value="#{bean.show1}" />
</h:commandButton>
</h:form>
<h:form id="form2">
<h:commandButton id="button2" value="Two" action="#{bean.click2}">
<f:param name="show1" value="#{bean.show1}" />
</h:commandButton>
</h:form>
This way you can extract them as request parameter in bean's (post)constructor.
public JsfTrial() {
String show1 = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("show1");
this.show1 = (show1 != null) && Boolean.valueOf(show1);
}
Ugly, but CDI doesn't offer a builtin annotation which substituties JSF's #ManagedProperty("#{param.show1}"). You could however homegrow such an annotation.

JSF 2.0 redirect with parameters

I my Java EE application I have articles administration. There is url like http://localhost:8080/articles/detail.xhtml?article=70 where article means id of article. This page displays article comments etc. it is not important. But there is button Edit and I would like to redirect on page edit.xhtml but user should still see http://localhost:8080/articles/detail.xhtml?article=70 because I don't want the edit page to be bookmarkable.
Can you help me how to set up faces-config to change page but not url? I thought that if I don't write <redirect /> then url would stay same but I was wrong. Url changes from detail.xhtml?article=70 to detail.xhtml
Thanks for any advise.
I'd suggest to bring in some ajaxical powers so that no synchronous request is fired.
<h:panelGroup id="article" layout="block">
<h:panelGroup rendered="#{!bean.editmode}">
View article (can if necessary be an ui:include)
<h:form>
<h:commandButton value="Edit" action="#{bean.edit}">
<f:ajax render=":article" />
</h:commandButton>
</h:form>
</h:panelGroup>
<h:panelGroup rendered="#{bean.editmode}">
Edit article (can if necessary be an ui:include)
<h:form>
<h:commandButton value="Save" action="#{bean.save}">
<f:ajax render=":article" />
</h:commandButton>
</h:form>
</h:panelGroup>
</h:panelGroup>
Bean:
private boolean editmode;
public void edit() {
this.editmode = true;
}
public void save() {
this.editmode = false;
}
public boolean isEditmode() {
return editmode;
}

Resources