Cancel JSF ajax call - ajax

I have an f:ajax tag inside an h:inputText tag, making ajax calls on keyup events :
<h:inputText id="searchinput" value="#{tvShowForm.name}">
<f:ajax event="keyup" render="results" listener="#{tvShowForm.search}" />
</h:inputText>
Each call takes enough time that the user has typed several characters before the first call is finished.
Is there a way to cancel the current ajax call (and the queued up ones), so that the last keyup event executes an ajax call immediately?

It sounds like you want to coalesce the events, for example this will wait
half a second before firing an ajax request, and any input typed at that
point will be included. But you won't fire an ajax request for each character
typed.
<h:inputText onkeyup="keyupHandler();"/>
...
<script>
var keystrokeTimeout;
keyupHandler = function(event) {
var minChars = 4;
var len = $(this).val().length;
if((len != 0) && (len < minChars)) {
return;
}
var ajaxRequest = function() {
jsf.ajax.request('results', null, {
execute: 'results',
render: 'results'
});
}
clearTimeout(keystrokeTimeout);
keystrokeTimeout = setTimeout(ajaxRequest, 500); // millisecs
}
</script>
Is this remotely like what you want to do?
EDIT: Another suggestion is that you check out the Richfaces 4 a4j:queue functionality.
This allows for combining events, for example in the keyup scenario if you've been
leaning on your keyboard, when the current ajax request completes only one further
request will be sent. It's also possible to specify a request delay and ignore stale
responses. The big mistake Richfaces doesn't make (that primefaces does make) is that
RF uses the same underlying queue as the JSF implementation, so you don't have the risk
of out-of-order processing.
I appreciate that if you're not already using this library it's not a small step to take.

Related

Aborting JSF Ajax Request from jsf.ajax.addOnEvent()

I would like to have a central place where I monitor ajax requests and in certain situations abort them.
The only thing I don't know to do is to actually abort the ajax request from one central function.
I Imagine that the solution would look something like this:
jsf.ajax.addOnEvent(function(data) {
if (data.status === 'begin') {
// abort the ajax request
}
});
But I might be mistaken.
Thanks!
This is not possible by the standard JSF JS API. Your best bet is to add an onclick or onchange handler on the calling JSF component which returns false if the desired condition is met to abort the ajax request.
<h:commandButton onclick="return mayFireAjax(this)">
<f:ajax />
</h:commandButton>
You can if necessary abstract this away with jQuery.on() (or jQuery.delegate() or jQuery.live() depending on the jQuery version used) on a common selector so that you don't need to repeat it on every desired component.
$(someSelector).on("click", function() {
return mayFireAjax(this);
});
If you have control the source, you can always attach a onClick to the UICommand component. But in some situation, you do not have access to source. For example, the ajax is provided by some third-party component. You do not want to mess up with their components.
First, I have a small js library.
var FxJSFBegin = "JSFBegin";
if (jsf) {
var originalRequest = jsf.ajax.request;
jsf.ajax.request = function(source, oevent, options) {
var event = $.Event(FxJSFBegin);
event.options = options;
event.originalEvent = oevent;
$(source).trigger(event);
if (event.isDefaultPrevented()) {
return;
} else {
originalRequest.apply(null, arguments);
}
};
}
This piece code proxies original JSF ajax call. It uses jQuery to fire a "JSFBegin" event. Integration code can listen this event using jQuery mechanism. Listener can cancel the jsf call using event.preventDefault().
Requirement:
jQuery
This piece code should be placed after jsf.js is loaded.
Using global="false" will help you prevent calling the ajax status for an ajax call. Please refer to the following link.
Different Ajax statuses for different components in PrimeFaces

Abort previous AJAX call when a new one made?

I'm updating search results as the user types the search term.
When 2 ajax calls happen, sometimes the last one data_response is brought back first. I need to make sure this doesn't happen.
This is my code:
function filterCities(search) {
$.ajax({type:'GET',url:'/ventas/theme/citiesContainer.php',data: "search=" + search,
success:function(data_response){
results.innerHTML = data_response;
}});
}
How do I cancell previous instances of the same request when I make a new one?
This is a solution with a simple counter:
<script>
var counter = 0;
function filterCities(search,c) {
$.ajax({type:'GET',url:'/results.php',data: "search=" + search,
success:function(data_response){
if(c == counter) { // Only update if it's data_response from last call
results.innerHTML = data_response;
}
}});
}
</script>
<input type="text" onkeyup="counter++; filterCities(this.value,counter);">
You could transmit a counter in the URL, and send the counter back, and just ignore the results if they aren't from the most current iteration. Much like a sequence number in UDP packets.
You need to look for an ajax queue plugin (or write one)
How do I go about getting the Ajax Queue plugin working in jQuery 1.3?
just search some more here on SO and google for "jquery ajax queue"

Show loading progress when making JSF Ajax request

How can I show some loading message when making request using <f:ajax>?
If you're not already using a 3rd party component library which could already have a ready-made component for that, such as PrimeFaces with <p:ajaxStatus>, then you can use the JSF-provided JavaScript jsf.ajax.addOnEvent() function (and eventually also jsf.ajax.addOnError()) to hook a function on ajax events.
Here's a basic kickoff example:
<script>
jsf.ajax.addOnEvent(function(data) {
var ajaxstatus = data.status; // Can be "begin", "complete" and "success"
var ajaxloader = document.getElementById("ajaxloader");
switch (ajaxstatus) {
case "begin": // This is called right before ajax request is been sent.
ajaxloader.style.display = 'block';
break;
case "complete": // This is called right after ajax response is received.
ajaxloader.style.display = 'none';
break;
case "success": // This is called when ajax response is successfully processed.
// NOOP.
break;
}
});
</script>
<img id="ajaxloader" src="ajaxloader.gif" style="display: none;" />
See also chapter 13.3.5.2 of the JSF 2.0 specification:
13.3.5.2 Monitoring Events For All Ajax Requests
The JavaScript API provides the jsf.ajax.addOnEvent function that can be used to register a JavaScript function
that will be notified when any Ajax request/response event occurs. Refer to Section 14.4 “Registering Callback
Functions” for more details. The jsf.ajax.addOnEvent function accepts a JavaScript function argument that will be
notified when events occur during any Ajax request/response event cycle. The implementation must
ensure the JavaScript function that is registered must be called in accordance with the events outlined in
Section TABLE 14-3 “Events”.
You can grab some cool ajax loader gifs for free from http://www.ajaxload.info, by the way.
richfaces has a very easy to use component that I use like this:
<a4j:status startText="" stopText="" onstart="showAjaxActive();" onstop="hideAjaxActive();"/>

Can I make an Ajax request inside an ongoing Ajax request (e.g. on the success callback function)?

I have a jQuery application, a shopping cart, that posts back info to the server, if the text inputfield is changed. This is done in an Ajax request. Now, if the Ajaxrequest is a success, I want to reload the shoppingcart asynchronously. The code is as follows:
$(document).ready(function() {
var jInput = $(":input");
jInput.change(function() {
var vareAntal = $(this).val();
var vareID = $(this).siblings("input#vareID").val();
$.ajax({
type: 'POST',
url: 'checkout.aspx',
data: { 'ID': vareID, 'Antal': vareAntal },
success: function() {
$("#newbasket").load(location.href + " #newbasket>*", "");
}
});
});
});
This works, but only once! If I change the text inputfield, after the page is loaded for the first time, the div with the ID of newbasket reloads asynchronously. But if I try to change it again, nothing happens.
I've tried to do some debugging with Firebug, and the first time I change the text inputfield, it fires a POST-event, and afterwards a GET-event, when the POST-event is succesful. But after that, nothing happens when I change the text inputfield again.
So, how do I achieve triggering the .load() method after each text input change?
I've also tried experimenting with the .ajaxComplete() function, but that, of course, resulted in an infinite loop, since the .load() is an ajax-object.
Instead of .change(func), use .live('change', func) here, like this:
jInput.live('change', function() {
This will make the selector work on any new inputs added as well. When you're replacing the elements like you are currently, their event handlers are lost (or rather, not re-created, because you have new elements). .live() is just for this purpose, it listens for events from old and new elements, regardless of when they were added.

Using jQuery Autocomplete with Validator onBlur timing problem

Here's my problem, I have an input element in a form that is implementing jQuery.Autocomplete and jQuery.validate, all working normally except when I click an element in the autocomplete list to select it.
What happens is validation occurs before the autocomplete sets its value. Because validation occurs on onBlur, and you just clicked an item in the autocomplete list, blur fires and validation occurs a split second before the input is filled with its new value.
I wouldn't mind a double-validation if it was client side, but I happen to be executing an expensive remote ajax validation on this field, so I'd really like to solve this the right way.
My first thought is to proxy all validation onBlur events through a function that times out 10ms later, essentially flip flopping the event order. But, I think, that means tearing into the jQuery.Validate.js code, which I'd rather not do.
Any ideas?
I was able to get this working but perhaps not as elegantly as I would have liked. Ideally I would have liked to call the prototype or defaults version of of onfocusout from within a timeout closure but I wasn't able to figure out how to reference it from that scope.
The approach that I took instead was to override the onfocusout method with its code copy/pasted into a timeout closure. The only other tweak was to change references from this to _this to work within the different scope of the timeout closure.
$("#aspnetForm").validate({
success: "valid",
onkeyup: "false",
onfocusout:
function(element) {
//Delay validation so autocomplete can fill field.
var _this = this;
setTimeout(function() {
if (!_this.checkable(element) && (element.name in _this.submitted || !_this.optional(element)))
_this.element(element);
_this = null;
}, 250);
}
});
Feel free to post improvements.

Resources