Jsf 2.1 Ajax Autocomplete + Server Search Only After User Stops Typing
Solution 1:
Since you are not on JSF 2.2 or up (yet) and using PrimeFaces (purely for its p:ajax
) is not an option, you indeed need some 'workaround' (effectively it is not a workaround but for JSF 2.1 a good solution).
Fortunately, and many so called 'modern' javascript UI fans forget this, the JSF client side is just html, javascript and ajax. And the JSF ajax is, just like any other ajax in any other framework, handled with javascript and its basic ajax handling can be overridden in a basic javascript way as can be seen in Aborting JSF Ajax Request from jsf.ajax.addOnEvent().
I took this as a basis and combined this with some creative basic javascript timeout handling as you already suspected would be needed.
It makes use of the option to pass the original event arguments on to a not-anonymous javascript function via timeout. Like in How can I pass a parameter to a setTimeout() callback?. The hardest part was to find out how the 'arguments' would be passed. It turned out you can even pass multiple individual arguments to the setTimeout
instead of one 'arguments' argument
It looks like a lot of code but a lot is comment/logging (which you can remove) and some variables can be reduced.
if (jsf) { // test if the 'jsf' javascript is availablevar originalRequest = jsf.ajax.request; // 'save' the original ajax handlingvar timeouts ={}; // keep an 'hash' of timeouts so it can be used for lots of// different elements independently based on element id
jsf.ajax.request = function(source, oevent, options) {
// check for a 'delayedEvent' class on an input and only apply delay to those that have one
delayed = document.getElementById(oevent.target.id).classList.contains("delayedEvent");
if(delayed) {
// check if an existing timer for this element is already running and clear it// so it will never fire but will be 'superseded' by a new onevar timeout = timeouts[oevent.target.id];
if (timeout != undefined) {
console.log('Delayed event cleared: ' , arguments, timeout);
clearTimeout(timeout);
}
// create a new timeout with a 350 ms delay. Making the delay 'dynamic' could // be done by creating 'composite' classnames like 'delayEvent500' and delayEvent250'. // Or putting both via html 5 data attributes on a container elelement... // Be creative
timeout = setTimeout(applyOriginalEvent, 350, source, oevent, options);
console.log("Delayed event created: ", arguments, timeout);
timeouts[oevent.target.id]=timeout;
} else {
console.log('Direct event fired: ' , arguments);
originalRequest.apply(null, arguments); // apply the request direcyly
}
};
functionapplyOriginalEvent(source, oevent, options) {
var timeout = timeouts[oevent.target.id];
console.log('Delayed event fired: ' , arguments, timeout);
// fire an ajax request with the original arguments;
originalRequest.apply(null, arguments);
// remove the timeout from the listdelete timeouts[oevent.target.id];
}
};
Just make sure this is loaded after jsf.js is loaded. I tested this locally and on the Omnifaces showcase via a browser developer console.
What I did not test it with is your existing 'length' checks but these could be left untouched.
Solution 2:
To solve this problem IMHO you need to think outside of the JSF stack. In Angular this is a common situation solved by using the debounceTime operator from RxJS. For example:
import { AbstractControl } from'@angular/forms';
import { debounceTime, switchMap, map, first } from'rxjs/operators';
@Injectable({ providedIn: 'root'})
exportclassUserNotTakenValidatorService {
constructor(private signUpService: SignUpService) {
}
checkUserNameTaken() {
return(control: AbstractControl) => {
return control.valueChanges
.pipe(debounceTime(300))
.pipe(switchMap(userName =>this.signUpService.checkUserNameTaken(userName)))
.pipe(map(isTaken => isTaken ? {userNameTaken: true} : null ))
.pipe(first());
}
}
}
In this case, the service will be called only after the user stops typing for 300 milliseconds.
Researching a little I found an integration of RxJS and Java. Check it out: https://www.baeldung.com/rx-java. If this integration for some reason doesn't help, I would try to import the RxJS library into the project.
Post a Comment for "Jsf 2.1 Ajax Autocomplete + Server Search Only After User Stops Typing"