Primefaces automatically pass parameter from selectOneMenu without using command button - ajax

I would like to call an action from a backing bean immediately after a value in a PF selectOneMenu is selected without using any button or link. I already looked at this question:
When to use valueChangeListener or f:ajax listener?
While very useful, I am not able to apply the recommended business logic that would allow the action to be called dynamically based on ONLY the selected value from the menu. I know how to use ajax to render changed values between two menus (where the value of one menu is changed). The options discussed on the above questions do not seem to work in my case which leads me to look deeper into the EL and backing bean methods to understand these concepts better.
I tried two ways to allow dynamic processing of my menu paramteres. The first was to use the valueChangeListener ayttribute in the EL:
<p:selectOneMenu id="Ctl" value="#{actionBean.department}"
valueChangeListener="#{actionBean.ShowListDeptAct(event)}" onchange="submit()">
<f:selectItem itemLabel="Dept A" itemValue="Dept A"/>
<f:selectItem itemLabel="Dept B" itemValue="Debt B"/>
<f:selectItem itemLabel="Dept C" itemValue="Dept C"/>
</p:selectOneMenu>
The bean method:
public void ShowListDeptAct(ValueChangeEvent event) {
department = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("department");
listDeptActions();
}
public String listDeptActions() {
//department = getDepartment();
deptActions = facade.findByDept(department);
return "DeptAction";
This only results in refreshing the page and the bean method does not even to be functioning.
The second way was to use the p ajax tag:
<p:selectOneMenu id="Ctl" value="#{actionBean.department}">
<f:selectItem itemLabel="Dept A" itemValue="Dept A"/>
<f:selectItem itemLabel="Dept B" itemValue="Debt B"/>
<f:selectItem itemLabel="Dept C" itemValue="Dept C"/>
<p:ajax listener="#{actionBean.ShowListDeptAct}" oncomplete="submit()"/>
</p:selectOneMenu>
bean method:
public void ShowListDeptAct(AjaxBehaviorEvent event) {
department = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("department");
listDeptActions();
}
public String listDeptActions() {
//department = getDepartment();
deptActions = facade.findByDept(department);
return "DeptAction";
}
This resulted in exactly the same behavior which makes me think my problem might be a mistake in how I"m writing the listener.
The EL is supposed to pass the name of the selected department to the following backing bean methods which will display a dataTable of the data for the selected department. The backing bean methods:
After reading the PF User Guide and some questions here, I'm neither sure about how to pass the correct EL nor the right backing bean methods to achieve this. Would appreciate some guidance. Thank you!

When you work with Ajax, it is very important to always keep in mind which part of the view needs to be submitted to the server and which part needs to be updated as the response. This is the number one error source when dealing with Ajax-Requests.
For the <p:ajax>-Tag, this is specified with the attributes process and update, in pure JSF <f:ajax> the attributes execute and render are used.
This is very helpful to read to understand how these work:
Understanding PrimeFaces process/update and JSF f:ajax execute/render attributes
In your first attempt you try to get the parameters from the request parameter map, which is not a good idea, because the key of each request parameter will be the id of the component that is sent. You would have to know the components' id to get the value. It is not recommended to work with component ids on the server side, if possible, as they may change when enclosing naming containers change and are therefore unreliable. Also this heavily interferes with separation of view and program logic.
The best attempt is to bind the values of components to variables on server side beans, as you already did, an work with them.
In your second attempt, the default value of process in the <p:ajax>-Tag is null, so there are no values of components sent to the server. If you write
and bind the value of the <p:selectOneMenu> to a variable on the server, you can use the value of the department in the doAction()-Method.

Related

How to sort f:selectItems in each p:selectOneMenu of my application?

Eg:
<p:selectOneMenu value="#{UserBean.country}" id="countryId">
<f:selectItem itemLabel="Japan" itemValue="Japan"/>
<f:selectItem itemLabel="Russia" itemValue="Russia"/>
<f:selectItem itemLabel="India" itemValue="India"/>
<p:ajax listener="#{UserBean.onChangeCountry}" process="#this"/>
</p:selectOneMenu>
Like above I have many other selectOneMenu in other jsf pages which is not in sorted form, I want a solution where in surrounding the selectOneMenu tag with the custom tag will display the contents in sorting order (or can suggest any other way also we can achieve this)
You don't need a custom tag.
In the case of a p:selectOneMenu you can just use f:selectItems (plural) and return a sorted collection (replacing your individual f:selectItem tags). In this case, that would be something like;
<f:selectItems value="#{view.countries}"/>
Where countries is a getter, getCountries(), returning a list of Map<String, String> sorted according to your choice.
Imagine for a second there was no f:selectItems though, and the only option available to us was f:selectItem (singular) - like in your example. Then the solution would be to use a JSF pre-render hook and sort the JSF components before that section was rendered. You can read exactly how this works here; Variable order of components (panels) in JSF page. In that example, a list of p:panel components are shuffled randomly inside a h:panelGroup. It can easily be applied to this case though.
To make it more transparent and hide the f:event tag to the pre-render hook on the pages, you could go one step further and define a composite component;
https://stackoverflow.com/tags/composite-component/info
This composite component could just use a selectOneMenu and introduce a sort attribute that would allow you to invoke the sorting pre-render hook as described above depending on if the value of the sort attribute is true or false.
You could create a custom renderer for the p:selectOneMenu component. Create a new class, say my.custom.MySelectOneMenuRenderer, and extend SelectOneMenuRenderer. In this you want to #Override the encodeInput method to something like:
public class MySelectOneMenuRenderer extends SelectOneMenuRenderer {
#Override
protected void encodeInput(FacesContext context, SelectOneMenu menu, String clientId, List<SelectItem> selectItems, Object values, Object submittedValues, Converter converter) throws IOException {
// Sort the items
Collections.sort(selectItems, Comparator.comparing(SelectItem::getLabel));
// Delegate to super to continue rendering
super.encodeInput(context, menu, clientId, selectItems, values, submittedValues, converter);
}
}
I've checked this with PrimeFaces 10. If you need the source for a different PrimeFaces version, check the SelectOneMenuRenderer source code and select the according version tag (note that from PrimeFaces 11 the path changed). Note that the method you need to override, might be different in other versions (not likely, but possible).
Add your custom renderer to the render-kit section in your faces-config.xml like:
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.SelectOneMenuRenderer</renderer-type>
<renderer-class>my.custom.MySelectOneMenuRenderer</renderer-class>
</renderer>
</render-kit>
Please note that this will sort the options every time for any p:selectOneMenu render, which comes with a performance penalty.

Form values not submitted, reset on ajax

In my current project I have a construct, where you have an entity where you can change the values through a approval workflow.
If you want to save a new version of an entity, you click on a button and a dialog pops up. There you can set the one to approve your change and an additional, optional comment.
My problem is, that the values from this dialog are not submitted.
The dialog is included in my main template, but outside of the main form. The dialog is appended to the Body, like this:
<p:dialog id="idEntityCommentDialog"
header="#{msgs['label.approval.dialogHeader.'.concat(bean.targetStatus)]}"
width="350" widgetVar="entityCommentDlg" modal="true"
maximizable="false" resizable="false" appendTo="#(Body)">
Both fields set their value to an object inside the sessionScoped backing bean:
<p:selectOneMenu id="selUsers"
value="#{bean.state.releasedBy}" style="width: 100%">
<f:selectItems value="#{bean.fetchUserList()}" var="user"
itemValue="#{user.id}"
itemLabel="#{user.name}" />
</p:selectOneMenu>
<p:inputTextarea id="changeComment"
value="#{bean.state.newComment.eventDescription}"
style="width: 98%; height: 100px">
<f:converter converterId="xssConverter" />
</p:inputTextarea>
Before, I had standard jsf controls, h:sekectOneMenu and h:inputTextArea. I have switched to the respective primefaces controls, and now at least the value from the selectOneMenu is submitted, but still, the value from the inputTextarea is not submitted. I have set breakpoints in the used converter, and I receive an empty string in my converter, which works in different other places where I use it.
I tried various combinations of update parameters and I also tried to set the value in an ajax request via
<p:ajax event="change" update="#this">
When I use the p:ajax tag, I can see, that when I change the value, after the ajax request, the value is immediately reset to the original value before the change. This applies for both fields (when using the standard controls) Like I said, I fixed it with the primefaces control (I don't understand why, but it seems to work) but it doesn't work for the inputTextArea.
My button is inserted with a toolbar facet from primefaces.
<p:commandButton actionListener="#{bean.saveEntity}"
styleClass="toolbarButton" update="dialogForm2 :blocker:contentWrapper"
onstart="PF('waitDialog').show();"
oncomplete="PF('waitdialog').hide();PF('entityCommentDlg').hide();"
value="#{msgs['label.approval.button.'.concat(bean.targetStatus)]}">
</p:commandButton>
The form is defined inside the dialog with just an id:
<h:form id="dialogForm2">
I am mostly confused, that if I use the p:ajax control, that my value is reset immediately, so there must be something very wrong here.
I also checked that I have no failed validations that I maybe don't see for some reason (via a phaseListener) but that's not the case, no facesMessages occur.
Please respect that I can't show you complete chunks of code from the project, my customer wouldn't want that, but if you need further information about other code or architecture, I will try to provide it. I also changed some ids and names, but I made sure that they are correct and referenced properly in the original code.
Do you have any idea what could be the reason for this kind of behaviour?
I know there are various articles about form values not submitted, I have seen them and tried the solution, but nothing seems to work for me here. A colleague of mine also has no idea why it shouldn't work. I use nearly the same construct (with a different backing bean and objects) in another part of the application, there, everything works fine, with the standard jsf controls.
Ok, I finally found the problem. The solution was to remove appendTo="#(Body)"
I had very strange behaviour before I found the solution.
You see, in the onStart and onComplete events of the submit button, I display a loading overlay. The bug occured as I fixed this loadingOverlay. Before I had a copy&paste error, where I referenced the wrong loading overlay, which was not present in the current page. But the bug only occured when I used the correct overlay or I even deleted the reference to this overlay completely. When I put in the wrong id of the overlay again, it worked...
Now, when I removed the appendTo attribute, everything seems to work fine, even the correct loading overlay. I don't even know anymore why I used the appendTo attribute. I assume the dialog was in another place before and I had to use it, but meanwhile the dialog is placed directly in the body of the document, outside of any layout or form, so I understand that I don't need it.
Does someone has a more educated idea why this error happened like it did? I understand that appendTo can have side effects when you don't need to use it, but the general behaviour I could observe here is beyond any reason to me.

Primefaces Autocomplete returning list is based on the values of other fields. How do I do?

Pictures speak better than words...
I want to return the street address but for that I have to pass city and state. I've tried many ways and I could not, because I'm not understanding but I think when I make an ajax autocomplete and set the values ​of city and state, the values don't are going through to the managedbeans. They pass only when I advance in wizard, perhaps because the autocomplete process and just update itself (even me trying to update the other fields through other ajax requests within autocomplete tags).
The following code:
<p:autoComplete global="true" id="autoCompleteEnderecoCidadesLogradouro"
completeMethod="#{cidadeMB.autoCompletaEnderecoPojo}"
value="#{cidadeMB.enderecoTemp}" var="e" itemValue="#{e}"
itemLabel="#{cidadeMB.cidade.endereco.logradouro}" converter="enderecoConverter"
forceSelection="true" minQueryLength="5" maxResults="15" queryDelay="800"
styleClass="textoLogradouro">
<p:ajax process=":tabViewSistema:tabViewCadastro:formAddCidades:testeCidadeInput"
update=":tabViewSistema:tabViewCadastro:formAddCidades:testeCidadeInput"/>
<p:column>#{e.logradouro}</p:column>
<p:column>#{e.cep}</p:column>
<p:column>#{e.uf}</p:column>
<p:ajax event="itemSelect" listener="#{cidadeMB.handleSelecaoDeEndereco}"
update=":tabViewSistema:tabViewCadastro:formAddCidades:panelAddCidadesT3"/>
</p:autoComplete>
Can anyone give me a hint how to do it?
Other people had the same problem, and PrimeFaces addressed this:
Issue 3593: Add process option to autocomplete
Summary: Add process option to autocomplete
Labels: TargetVersion-3.2
[...]
We'll add process option which you can use to add other component
on page to decide what to process during search request.
[...]
Done, you now can do process="otherComponents" while entering data.
So you can now add attribute process="otherComponent1 otherComponent2" to the <p:autocomplete>. JSF should then set the values from those componentes in the backing bean on every autocomplete callback, just like during a regular submit, and your autocomplete callback can refer to the values.

Basic ajax functionality in RichFaces

I have a lot of trouble finding proper, full examples on how to send values using Ajax when some event happens on the client side.
A basic example could be a list of cars. Each car has a name and an id. When a car is clicked, the id of the car is sent to the server.
What is the idiom for this in RichFaces using a4j? Or should I try this with plain jsf2?
I find easily parts of what I need but always there's some missing part that glues the thing together. Of course, there's always workarounds but I need a solid, robust way.
Just bind the value of the component,
value="#{bean.selectedCar}"
and had to your component an a4j:support event :
E.g. for comboBox
<a4j:support event="onchange" action="#{bean.method}" />
E.g. for dataTable
<a4j:support event="onselectionchange" action="#{bean.method}" />
In the method on the bean public void method() you can read data of bean.selectedCar.
I hope this help you.
This may help: http://mkblog.exadel.com/2009/02/how-to-delete-a-row-in-jsf/

JSF f:event preRenderView is triggered by f:ajax calls and partial renders, something else?

So we have an f:event:
<f:metadata>
<f:event type="preRenderView" listener="#{dashboardBacking.loadProjectListFromDB}"/>
</f:metadata>
Which is triggered as desired on initial page load (render).
However this preRenderView event is also triggered by an ajax partial page render, which re-renders an h:panelgroup with the id projectListing, as below.
<h:commandButton action="#{mrBean.addProject}" value="Create Project"
title="Start a new project">
<f:ajax render="projectListing" />
</h:commandButton>
I only want the dashboardBacking.loadProjectListFromDB to be called for the initial page render, but not when there is an ajax partial render. Is there a more appropriate event or method I could be using?
I had the same need not too long ago. I ended up using something suggested by BalusC.
There is a method in the FacesContext class that lets you know if you're dealing with a full blown request or a partial processing of some sort:
FacesContext.getCurrentInstance().isPostback()
This way you can still use the preRenderView technique and check if it's a postback in the listener. I found that particularly useful because I needed a session bean as the user had to navigate to another page and come back. If I used view scoped beans (like suggested above by Brian), I'd lose the info I had before navigating away.
Another option would be to put your preRenderView functionality in a #PostConstruct method of a ViewScoped managed bean. This logic would be executed when the bean is initialized, and you you maintain the same instance of the bean for all your ajax requests until you change views.
Another possibility is to check if the request is an ajax one or not in the preRenderView method. You can also perform the load conditionally considering other factors such as if the request is a GET or not and if validation failed or not (view params validation can fail on GET page).
boolean getMethod = ((HttpServletRequest) fc.getExternalContext().getRequest()).getMethod().equals("GET") ? true : false;
boolean ajaxRequest = fc.getPartialViewContext().isAjaxRequest();
boolean validationFailed = fc.isValidationFailed();
The "new-age" way of handling this is described here:
http://www.coderanch.com/t/509746/JSF/java/duplicate-call-preRenderView-event#2634752
You could try attaching the preRenderView event listener to an individual component, rather than the page. Choose a component that is not rendered during an Ajax request.
One small problem is that the view parameters have not been set when the #PostConstruct method is called so I had to get them explicitly:
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("theParam");
update: actually, I ended up doing the #PostConstruct thing, its much cleaner.
I had exactly the same issue today with a session-scoped backing bean. Namely, I had registered an event-listener method on the backing session-scoped bean that was registered with the preRenderView. But I found that it was also fired on some Ajax sorting operations on a PrimeFaces 3 dataTable component. So, what I ended up doing was using a boolean instance variable on the session-scoped backing bean to make sure the body of the event-listener method executed only the first time (the boolean acted as a flag). I am sure it's rather naive and probably broken on certain cases so I would be interested to know why and how this simplistic approach might fail.

Resources