I have set up a basic testcase in which I'm experience some (to me) weird behaviour. When using the setup below, the typed value in the editor will only be visible by h:outputText on the second submit. E.g.
Change value in editor to "myvalue"
Send Ajax-request
h:outputText shows "test" (default value from bean)
Change value in editor to "anothervalue"
Send Ajax-request
h:outputText shows "myvalue"
Send Ajax-request
h:outputText shows "anothervalue"
Note: there's a custom composite, please ask for code if needed (it simply creates textarea for TinyMCE and loads .js file from below)
index.xhtml
<h:body>
<h:form>
<mh:editor id="tinymceEditor"
value="#{bean.value}" />
<h:commandButton value="Ajax">
<f:ajax execute="tinymceEditor"
render="show" />
</h:commandButton>
<h:outputText id="show" value="#{bean.value}" />
</h:form>
</h:body>
jsfhandler.js -> included on header in custom composite mh:editor
jsf.ajax.addOnEvent(function(data) {
switch(data.status) {
case "begin":
tinyMCE.execCommand('mceRemoveControl',true,"tinymceEditor");
tinyMCE.triggerSave();
break;
case "complete":
tinyMCE.execCommand('mceAddControl',true,"tinymceEditor");
break;
case "success":
break;
}
});
Bean.java
#Named
#RequestScoped
public class Bean {
private String value = "test";
}
The JSF ajax begin event is too late in order to take changes in form data into account. The ajax request is already prepared based on form data before this event.
Effectively, the sequence is as follows:
User enters input (and leaves field).
HTML DOM "change" event is triggered on input field.
User clicks submit button.
HTML DOM "click" event is triggered on submit button.
JSF prepares ajax request.
JSF ajax "begin" event is triggered on ajax request.
JSF sends ajax request.
...
Basically, you should be doing tinyMCE.triggerSave() during the HTML DOM "click" event.
<h:commandButton ... onclick="tinyMCE.triggerSave()">
Or, better, during the HTML DOM "change" event of the tinyMCE textarea.
Related
I have a JSF front end using Primefaces 5.3 which updates fields dynamically using Ajax. The problem that I am having is that sometimes my Ajax calls fail (ex: server responds with a 500), but my front end is still changing. Essentially, I'm looking to prevent the change of the input field if my ajax fails. Stated differently, I only want the input field to change upon a successful Ajax response.
I'm fairly new to JSF, so I'm not sure how to handle this. In regular HTML/JS, I would have been able to store the value onclick and in my ajax error handler restored the value, but I don't know how to do this using the PF framework.
<div class="Container25">
<p:selectOneRadio id="grid" value="#{cc.attrs.answer.singleAnswer.codeValue}" layout="grid" columns="1" >
<f:selectItems value="#{cc.attrs.menuItems}"
var="item" itemLabel="#{msg[item.code]}" itemValue="#{item.code}" itemLabelEscaped="false"/>
<p:ajax event="change" listener="#{cc.attrs.onChange}" update="#{cc.attrs.update}" disabled="#{cc.attrs.onChange == null }" global="false" />
</p:selectOneRadio>
</div>
I've tried adding the resetValues attribute to the ajax component, but that hasn't helped. Additionally, I've tried adding some custom JS in my onstart handler, but it is undefined.
I figured there must be a simple JSF/PF way of doing this, but can't seem to find it.
How can I either prevent the input value to change until the Ajax call returns successfully (ie: only change the value in the onsuccess handler) or reset my original radio button selection in the event that my Ajax call fails? What do I need to put in my onerror handler to restore the pre-ajax state?
You can use Primefaces RemoteCommand component for an easy solution, just embed it in your form:
<p:remoteCommand name="revertSomeValues"
actionListener="#{relatedBean.revertValuesToDefaults}"
update="componentId" />
And at the bean side you can manipulate the model:
#Named
#ViewScoped
public class relatedBean implements Serializable {
Integer codeValue;
//other model attributes and methods...
public void revertValuesToDefaults() {
setCodeValue(0); //supposing 0 is the default value
//handle other model attributes if needed
}
}
Now you can set the onerror callback alike -> onerror="revertSomeValues()"
You can also update the components wtih Primefaces RequestContext programatically from your bean if needed:
RequestContext context = RequestContext.getCurrentInstance();
context.update("componentId");
PrimeFaces disable submit on pressing enter key.
I’m, running PrimeFaces 5.1 running on WildFly 8.2 Final.
I have dialog, with two inputNumbers and two buttons. And the first inputNumber does some calculation on ajax blur event. Next to it is button which does some calculation in bean. And the problem is that when users press enter while focus is in inputNumber the button’s action gets fired and it’s really annoying. Is there a way to disable submitting with enter key on dialog?
Here is small xhtml dialog which can simulate my behavior:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:pe="http://primefaces.org/ui/extensions" >
<p:dialog id="id_example" header="Test dialog"
widgetVar="exampleDialog" modal="true" closable="true" >
<h:form id="id_example_form">
<p:panelGrid columns="3" styleClass="noBorders">
<h:outputText value="Input 1:" />
<pe:inputNumber id="Input1" value="#{exampleBean.number1}">
<p:ajax event="blur" update="valueInput1" />
</pe:inputNumber>
<p:commandButton value="Check something else" action="#{exampleBean.checkForUsername()}"
update=":growl_form" />
<h:outputText value="Input 1:" />
<p:inputText id="valueInput1" value="#{exampleBean.number1}" />
<p:commandButton value="Save" action="#{exampleBean.save()}" oncomplete="PF('exampleDialog').hide();"
update=":growl_form" />
</p:panelGrid>
</h:form>
</p:dialog>
</ui:composition>
And the bean:
package si.pucko.beans;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import si.pucko.util.Util;
#Named(value = "exampleBean")
#ViewScoped
public class ExampleBean implements Serializable {
private static final long serialVersionUID = 1L;
private BigDecimal number1;
public ExampleBean() {
number1 = new BigDecimal(BigInteger.ONE);
}
public BigDecimal getNumber1() {
return number1;
}
public void setNumber1(BigDecimal number1) {
this.number1 = number1;
}
public void checkForUsername() {
Util.ShowWarning("Just testing");
}
public void save() {
Util.ShowWarning("Saved");
}
}
The catch is i can't disable enter key with:
<h:form onkeypress="if (event.keyCode == 13) { return false; }">
Because client asked for hotkeys support and enter is used for submiting forms, recalculation some other values in some cases etc...
I think you use JavaScript to capture the enter key press and do nothing.
<h:form onkeypress="if (event.keyCode == 13) { return false; }">
Ref: https://stackoverflow.com/a/5486046/201891
return false; cancels an event across browsers if called at the end of an event handler attribute in the HTML. This behaviour is not formally specified anywhere as far as I know.
Ref: https://stackoverflow.com/a/1648854/201891
Update
It sounds like you want to disable the Enter key only when focus is in a particular field. You can write a Javascript method for that too and bind it to onkeypress. Write a Javascript method something like "if the enter key was pressed and the focus is in this field, return false; otherwise, return true".
As the answer referenced by Nimnio says, this is specific to HTML and browsers.
I consider this behavior to be inappropriate when using PrimeFaces.
I prefer to disable it globally, for all forms like this:
$('form').off('keypress.disableAutoSubmitOnEnter').on('keypress.disableAutoSubmitOnEnter', function(event) {
if (event.which === $.ui.keyCode.ENTER && $(event.target).is(':input:not(textarea,:button,:submit,:reset)')) {
event.preventDefault();
}
});
The target check allows the other default behaviors to work, like adding a line break in a textarea by pressing Enter.
To take into account new ajaxically added forms you'll need to call the above script after every AJAX request. There are multiple ways to do that, such as a <script> in a p:outputPanel autoUpdate="true", or calling a function in a p:ajaxStatus's oncomplete callback.
If this solution is not appropriate for some reason then consider the more localized one:
<h:form onsubmit="return false;">
Returning false here disables the non-AJAX default submit.
Putting a dummy/hidden button before the one you want to stop works best for me
<p:commandButton style="visibility: hidden;"/> <!-- dummy hidden button to stop keyPress from firirng submit -->
I used following solution:
<h:form onsubmit="return false;">
This prevents form submit. It works well only in case if you have ajax only behavior on this form.
It is default browser behavior to hunt a form for jQuery(":submit") elements and trigger the first listed on the form, when the enter key is pressed.
this will look strange on debugger, because if you have a function such as onclick="handle(event);".
You go to the text field, hit enter, and you will see an "artifical" onclick event with being passed to your primary submit action for that form.
The surest way to be in-control of what happens, I would say, is not by means of onkeypress as explained above. I found that to not work in all cases. On soame cases the form onkeypress simply does not get triggered, and you do not have then the opportunity to return flase; / event.preventDefault();. I am not 100% sure of all cases that justfied the onkeypress not getting triggered, but I suspect framework code preventing event bubbling in some instances.
Ultimately, what is really happening is your form is being submitted by your browser default behavior on ENTER withing input text field. It is not the bubling of the event to the form that submits the form. That is the flaw of trying to handle the event on the form and preventing default behavior there. That might just be too late.
And this is very easy to verify. If you tune youe inpu text and onkeypress always to event.preventDefault(). (A) you kill the textbox no text gets written on the texbox (B) The event still bubles up, but the browser does not submit the form anyway.
So the most important element of all is: where is the browser aiming at when it acts with the default behavior?
So what you can decide instead is that what you will take control of where the browser unleashes its default behavior. You can direct it to a dummy button.
This is the one and only one mechanism I have found to be really reliable to be in control of what happens when you hit enter on a text field withing a form is to have on that form a well known artifical button, e.g.:
<input type="submit" onclick"return false;" style="border:none; background-color: #myAppBrackgroundColor;" />
You get the idea, to make sure that the submit event is being routed by the browser to a button under your control.
You have to be careful, IE in particular is quirky.
If not for IE, you could simply style your button as display: none.
Because of IE, the browser will route the default submit action to a visible submit button, if one exists.
Therefore, your button cannot be in display none. You have to make it logically visible, but phsysically invisible. For that you supress the border and give it an appropriate background for your application.
This method is 100% reliable.
onkeydown and onchange()
<h:form id="inputform">
<p:inputText onkeydown="if (event.keyCode === 13) {onchange();return false;}" >
<p:ajax update="updatearea" />
</p:inputText>
</h:form>
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();
}
});
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
}
I want to be able to disable the commandbutton below once it's hit and enable it once the event listener runs and msg1 is rendered.
<h:commandButton value="Submit">
<f:ajax execute="#form" render="msg1" listener="{bean.method}" />
</h:commandButton>
How could I do this?
UPDATE: I found out that I can attach onclick event to the commandButton element itself to disable it. How can I detect the listener method has returned so I can enable the button again?
You could do it with help of the onevent attribute of <f:ajax> which points to a JavaScript function which handles the JSF Ajax events.
E.g.:
<h:commandButton value="Submit">
<f:ajax execute="#form" render="msg1" listener="#{bean.method}" onevent="handleDisableButton" />
</h:commandButton>
(note that I fixed the wrong EL in listener as well)
with this JS:
function handleDisableButton(data) {
var buttonElement = data.source; // The HTML DOM element which invoked the ajax event.
var ajaxStatus = data.status; // Can be "begin", "complete" and "success".
switch (ajaxStatus) {
case "begin": // This is called right before ajax request is been sent.
buttonElement.disabled = true;
break;
case "complete": // This is called right after ajax response is received.
// We don't want to enable it yet here, right?
break;
case "success": // This is called when ajax response is successfully processed.
buttonElement.disabled = false;
break;
}
}
If you use richfaces, the a4j:commandButton has the status attribute which prevent this.