JSF SelectOneMenu onchange, onsubmit cancel reset value - ajax

On a JSF SelectOneMenu, I have ajax event onchange, and with an onsubmit with a confirm question, when user abort the change, how could I reset the value on SelectOneMenu back to the original value?
<h:selectOneMenu value="#{bean.selectedFruit}">
<f:selectItems value="#{bean.fruits}"/>
<a:support event="onchange" onsubmit="if(!confirm('Are you sure?'))return false;}" action="#{bean.loadFruitStats}" rerender="body"/>
</h:selectOneMenu>
So on the above example, when user click 'No' on the confirm dialog, the action is not called, and rerender is not called, so the SelctOneMenu is on the updated "fruit" name, not the original one (so the fruit name and the fruit stats info doesnt match). What can I do to synchronize them?

h:selectOneMenu can be reset back to the original value with a help of javascript.
First you will need to define an id to h:selectOneMenu. For example
<h:selectOneMenu id="selFruitId"...>
Add following (self-explanatory) java script to jsf page
var currentValue;
function saveCurrentValue(){
//get html select element
var element=document.getElementById("form:selFruitId");
//saves currently selected value
currentValue=element.value;
}
function areYouSure(){
if(!confirm('Are you sure?')){
//if canceled, restores previously saved value
document.getElementById("form:selFruitId").value=currentValue;
return false;
}
}
Then you need to intercept moment when user tries to change selected value using mouse (onclick event) or keyboard (onkeypress event, for example, left-right-up-down cursor keys or some other key) and save currently selected value for later.
<h:selectOneMenu id="selFruitId"
onclick="saveCurrentValue()" onkeypress="saveCurrentValue()"...>
And, finally, before executing ajax, you need to call function areYouSure() which will, if canceled, restore saved (original) value back to h:selectOneMenu.
<h:selectOneMenu value="#{bean.selectedFruit}" id="selFruitId"
onclick="saveCurrentValue()" onkeypress="saveCurrentValue()">
<f:selectItems value="#{bean.fruits}"/>
<a:support event="onchange" onsubmit="areYouSure()" action="#{bean.loadFruitStats}" rerender="body"/>
</h:selectOneMenu>

Related

AjaxBehaviorEvent from f:ajax inside p:selectOneMenu is sometimes null

I am currently observing a really weird behaviour in the server-side callback of an ajax call from a value change event. Basically, I have a list of p:selectOneMenus on my page and want to make some decisions based on what was selected where. This is the jsf:
<p:outputLabel value="Provider:"/>
<p:selectOneMenu id="bacc_ba_check" value="#{bean.baCheck}" style="width:20em" styleClass="bigDefaultEnabled" disabled="#{controller.readOnlyUser}">
<f:selectItem itemLabel="none" itemValue=""/>
<f:selectItems value="#{bean.baCheckStrings}" />
<f:ajax listener="#{controller.providerChanged}" render="providerlist RPPPanel" onevent="reapplyDefaults" immediate="true"/>
</p:selectOneMenu>
And this is the callback in the controller:
public void providerChanged(AjaxBehaviorEvent event) {
// check for value
if ("Someval".equalsIgnoreCase(...)) {
this.setSomething(true);
} else {
this.setSomething(false);
}
The confusing part is the following: The event is always NULL when I select something else than none from the selectOneMenu. If I reselect 'none' after having selected something else before, the event is correctly set. In the callback, I want to check what the source of the event is so I can set some defaults elsewhere. Does anyone have an idea what could cause this strange behaviour? I really need to know which component fired the event at all times, especially when someone select a non-null value in a dropdown.
Thanks for help or any hints.
(JSF 2.1.7 Mojarra with Primefaces 4.0)

SelectOneMenu fire ajax when up or down arrow key is pressed

I need to fire an ajax call when i hit the up arrow key or the down arrow key when im focused on the selectOneMenu.
Here is my code, i tried only with the up key first (Its not working, and i tried several variations)
<h:selectOneMenu id="id" disabled="#!Bean.disabled}" value="#{Bean.value}" styleClass="style" onkeypress="if (event.keyCode == 38) { onchange(); return false; }">
<f:selectItems id="idItem" value="#{Bean.options}" var="option" />
<f:ajax event="change" execute="#this" render="group"/>
</h:selectOneMenu>
Hint: Tab key is firing it and moving the focus (if the value changed). I dont want the tab key firing it.
Just change the event of your f:ajax to keyup, the change event will not send any ajax request unless your selectOneMenu loses focus (that's also confirmed by your hint "Tab key is firing it and moving the focus ").
That will work like this:
<f:ajax event="keyup" execute="#this" render="group"/>
Update:
I updated my answer following BalusC's comment that the you want the ajax request to be fired only if the up Key or down Key are fired, so i am suggesting you to invoke directly the javascript function that f:ajax calls behind the scenes, So you can add a direct call to jsf.ajax.request(source, event, options) function, you can find more informations about that function here.
In order to use that method you must add the JSF’s built-in JavaScript library in your XHTML files like this:
<h:outputScript library="javax.faces" name="jsf.js"/>
Then change your code like this:
<h:selectOneMenu id="id" disabled="#!Bean.disabled}" value="#{Bean.value}" styleClass="style"
onkeyup="if (event.keyCode == 38 || event.keyCode == 40) { jsf.ajax.request(this, event, { render: 'form:group'}); return false;}">
<f:selectItems id="idItem" value="#{Bean.options}" var="option" />
</h:selectOneMenu>
NB: Please notice that in the render option of the jsf.ajax.request() i specified the exact ClientID of the component you want to render and not only the component id (form is here the id of h:form), if you want to use only the id group you must add prependId="false" to your h:form.

f:ajax don't fired with event "change"

The code:
<h:inputText id="date" value="#{holidayRequestController.dates}">
<f:ajax execute="date" event="change"
listener="#{holidayRequestController.generateDates}"
render="generateDate" />
</h:inputText>
<h:panelGroup id="generateDate" class="span8"> ..</h:panelGroup>
With this code when i using date picker to select a date in from h:inputText, and f:ajax will fire event and populate this date to generateDate component.
But when i change to multiDatePicker, f:ajax don't fired with event "change"(although it work with event "blur", but i have some problem to use "blur").
What should i do to use event "change" for multiDatePicker?
One other question: is there support f:ajax for tag h:inputHidden in jsf?
Thanks for support
That can happen if the change event is actually not fired. That can in turn happen if the input element's value is only manipulated via JavaScript and not via enduser interaction.
Basically, when JavaScript manipulates the value via element.value = newValue, and you want the change event to be fired, then the JavaScript should explicitly call element.change() on the input element being manipulated.
According to $.multiDatesPicker documentation, you can use the onSelect option to hook a function on select. In that function you can then just call element.change(). So, given an input as follows:
<h:inputText ... styleClass="multi-dates" />
then you can achieve it as follows:
$(".multi-dates").multiDatesPicker({
onSelect: function() {
$(this).change();
}
});

simulate onclick event picklist primefaces

I know that the primefaces picklist only alows transfer event like
<p:ajax event="transfer" listener="#{bean.onTransfer}" />
But I am looking for an onTargetSelected event. Is there a way to simulate it?
I thought about a JQuery function bound with a click event but I don't know on which element. I saw that when I select a line in the target list, the class of the li is transforming to ui-state-highlight. Is there a way to detect class changing with JQuery?
To call a bean method when the event will be fired, I thought about primefaces remoteCommand to send the ID of my object.
Do you have an idea about this event?
Note: I saw that there is a select with the target values in the source code but the selected value is 'selected' for each item and I don't know if there is something to do with this.
Thanks for your help
I have a trick. I am using this JQuery function :
$(document).ready(function(){
$('.ui-picklist-target .ui-picklist-item td').click(function() {
var id = $(this).closest("li").attr("data-item-value");
$('[id$=selectedItemId]').val(id); // Setting the value of my hidden input
updateSelectedTarget(); // Calling the remoteCommand function
});
});
I have added this to my xhtml page
<h:form>
...
<p:pickList ...>
</p:pickList>
<h:inputHidden id="selectedItemId" value="#{modifierUOBean.selectedTargetId}"/>
<p:remoteCommand name="updateSelectedTarget" actionListener="#{modifierUOBean.onSelectedTarget}"/>
</h:form>
And the bean:
private int selectedTargetId; // and getters and setters
public void onSelectedTarget() {
// Do what you want with selectedTargetId which contains the id of selected item
}

When to use valueChangeListener or f:ajax listener?

What's the difference between the following two pieces of code - with regards to listener placement?
<h:selectOneMenu ...>
<f:selectItems ... />
<f:ajax listener="#{bean.listener}" />
</h:selectOneMenu>
and
<h:selectOneMenu ... valueChangeListener="#{bean.listener}">
<f:selectItems ... />
</h:selectOneMenu>
The valueChangeListener will only be invoked when the form is submitted and the submitted value is different from the initial value. It's thus not invoked when only the HTML DOM change event is fired. If you would like to submit the form during the HTML DOM change event, then you'd need to add another <f:ajax/> without a listener(!) to the input component. It will cause a form submit which processes only the current component (as in execute="#this").
<h:selectOneMenu value="#{bean.value}" valueChangeListener="#{bean.changeListener}">
<f:selectItems ... />
<f:ajax />
</h:selectOneMenu>
When using <f:ajax listener> instead of valueChangeListener, it would by default executed during the HTML DOM change event already. Inside UICommand components and input components representing a checkbox or radiobutton, it would be by default executed during the HTML DOM click event only.
<h:selectOneMenu value="#{bean.value}">
<f:selectItems ... />
<f:ajax listener="#{bean.ajaxListener}" />
</h:selectOneMenu>
Another major difference is that the valueChangeListener method is invoked during the end of the PROCESS_VALIDATIONS phase. At that moment, the submitted value is not been updated in the model yet. So you cannot get it by just accessing the bean property which is bound to the input component's value. You need to get it by ValueChangeEvent#getNewValue(). The old value is by the way also available by ValueChangeEvent#getOldValue().
public void changeListener(ValueChangeEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
// ...
}
The <f:ajax listener> method is invoked during INVOKE_APPLICATION phase. At that moment, the submitted value is already been updated in the model. You can just get it by directly accessing the bean property which is bound to the input component's value.
private Object value; // +getter+setter.
public void ajaxListener(AjaxBehaviorEvent event) {
System.out.println(value); // Look, (new) value is already set.
}
Also, if you would need to update another property based on the submitted value, then it would fail when you're using valueChangeListener as the updated property can be overridden by the submitted value during the subsequent UPDATE_MODEL_VALUES phase. That's exactly why you see in old JSF 1.x applications/tutorials/resources that a valueChangeListener is in such construct been used in combination with immediate="true" and FacesContext#renderResponse() to prevent that from happening. After all, using the valueChangeListener to execute business actions has actually always been a hack/workaround.
Summarized: Use the valueChangeListener only if you need to intercept on the actual value change itself. I.e. you're actually interested in both the old and the new value (e.g. to log them).
public void changeListener(ValueChangeEvent event) {
changeLogger.log(event.getOldValue(), event.getNewValue());
}
Use the <f:ajax listener> only if you need to execute a business action on the newly changed value. I.e. you're actually interested in only the new value (e.g. to populate a second dropdown).
public void ajaxListener(AjaxBehaviorEvent event) {
selectItemsOfSecondDropdown = populateItBasedOn(selectedValueOfFirstDropdown);
}
If you're actually also interested in the old value while executing a business action, then fall back to valueChangeListener, but queue it to the INVOKE_APPLICATION phase.
public void changeListener(ValueChangeEvent event) {
if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
return;
}
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
System.out.println(newValue.equals(value)); // true
// ...
}
for the first fragment (ajax listener attribute):
The "listener" attribute of an ajax tag is a method that is called on the server side every time the ajax function happens on the client side. For instance, you could use this attribute to specify a server side function to call every time the user pressed a key
but the second fragment (valueChangeListener) :
The ValueChangeListener will only be called when the form is submitted, not when the value of the input is changed
*you might like to view this handy answer

Resources