jsf.ajax.addOnEvent(callback) jsf.ajax.addOnError(callback)Let me extend PrimeFaces AjaxStatus, register new global callbacks and show an example in action. At first we have to add the standard JS library for Ajax and our extended script ajaxstatus.js.
import javax.faces.application.ResourceDependencies; import javax.faces.application.ResourceDependency; @ResourceDependencies({ @ResourceDependency(library="primefaces", name="jquery/jquery.js"), @ResourceDependency(library="primefaces", name="core/core.js"), @ResourceDependency(library="primefaces", name="ajaxstatus/ajaxstatus.js"), @ResourceDependency(library="javax.faces", name="jsf.js"), @ResourceDependency(library="js", name="ajaxstatus.js") }) public class AjaxStatus extends org.primefaces.component.ajaxstatus.AjaxStatus { }Renderer class is simple
import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import java.io.IOException; public class AjaxStatusRenderer extends org.primefaces.component.ajaxstatus.AjaxStatusRenderer { protected void encodeScript(FacesContext context, org.primefaces.component.ajaxstatus.AjaxStatus st) throws IOException { AjaxStatus status = (AjaxStatus)st; ResponseWriter writer = context.getResponseWriter(); String clientId = status.getClientId(context); String widgetVar = status.resolveWidgetVar(); writer.startElement("script", null); writer.writeAttribute("type", "text/javascript", null); writer.write(widgetVar + " = new PrimeFaces.widget.ExtendedAjaxStatus('" + clientId + "');"); encodeCallback(context, status, widgetVar, "ajaxSend", "onprestart", AjaxStatus.PRESTART_FACET); encodeCallback(context, status, widgetVar, "ajaxStart", "onstart", AjaxStatus.START_FACET); encodeCallback(context, status, widgetVar, "ajaxError", "onerror", AjaxStatus.ERROR_FACET); encodeCallback(context, status, widgetVar, "ajaxSuccess", "onsuccess", AjaxStatus.SUCCESS_FACET); encodeCallback(context, status, widgetVar, "ajaxComplete", "oncomplete", AjaxStatus.COMPLETE_FACET); writer.endElement("script"); } }Note: Prestart event "ajaxSend" is not supported in the standard Ajax, but it's rarely used anyway. "ajaxStart" is normally enough. JavaScript ajaxstatus.js is a little bit complicate. I register there global handlers for Ajax events by jsf.ajax.addOnError / jsf.ajax.addOnEvent.
PrimeFaces.widget.ExtendedAjaxStatus = function(id) { this.id = id; this.jqId = PrimeFaces.escapeClientId(this.id); } PrimeFaces.widget.ExtendedAjaxStatus.prototype.bindFacet = function(eventName, facetToShow) { var _self = this; // jQuery jQuery(document).bind(eventName, function() { _self.showFacet(facetToShow); }); // Standard if (eventName == "ajaxError") { jsf.ajax.addOnError(this.processAjaxOnErrorFacet(facetToShow)); } else { jsf.ajax.addOnEvent(this.processAjaxOnEventFacet(eventName, facetToShow)); } } PrimeFaces.widget.ExtendedAjaxStatus.prototype.bindCallback = function(eventName, fn) { // jQuery jQuery(document).bind(eventName, fn); // Standard if (eventName == "ajaxError") { jsf.ajax.addOnError(this.processAjaxOnErrorCallback(fn)); } else { jsf.ajax.addOnEvent(this.processAjaxOnEventCallback(eventName, fn)); } } PrimeFaces.widget.ExtendedAjaxStatus.prototype.processAjaxOnEventFacet = function(eventName, facetToShow) { var _self = this; function processEvent(data) { if (eventName == "ajaxStart" && data.status == "begin") { _self.showFacet(facetToShow); } else if (eventName == "ajaxComplete" && data.status == "complete") { _self.showFacet(facetToShow); } else if (eventName == "ajaxSuccess" && data.status == "success") { _self.showFacet(facetToShow); } } return processEvent; } PrimeFaces.widget.ExtendedAjaxStatus.prototype.processAjaxOnErrorFacet = function(facetToShow) { var _self = this; function processEvent() { _self.showFacet(facetToShow); } return processEvent; } PrimeFaces.widget.ExtendedAjaxStatus.prototype.processAjaxOnEventCallback = function(eventName, fn) { function processEvent(data) { if (eventName == "ajaxStart" && data.status == "begin") { fn(); } else if (eventName == "ajaxComplete" && data.status == "complete") { fn(); } else if (eventName == "ajaxSuccess" && data.status == "success") { fn(); } } return processEvent; } PrimeFaces.widget.ExtendedAjaxStatus.prototype.processAjaxOnErrorCallback = function(fn) { function processEvent() { fn(); } return processEvent; } PrimeFaces.widget.ExtendedAjaxStatus.prototype.showFacet = function(facetToShow) { jQuery(this.jqId).children().hide(); jQuery(this.jqId + '_' + facetToShow).show(); }Using on the page along with JS handling could be
<p:ajaxStatus onstart="ajaxOnStartIndicator()" onerror="ajaxOnErrorIndicator()" onsuccess="ajaxOnSuccessIndicator()"/> <h:graphicImage id="ajaxIndicatorActive" library="img" name="connect_active.gif" style="display: none;" styleClass="ajaxIndicator"/> <h:graphicImage id="ajaxIndicatorCaution" library="img" name="connect_caution.gif" style="display: none;" styleClass="ajaxIndicator" title="Connection problem" alt="Connection problem"/>
function ajaxOnStartIndicator() { document.body.style.cursor = 'wait'; jQuery("#ajaxIndicatorCaution").css("display", "none"); jQuery("#ajaxIndicatorActive").css("display", "block"); // get the current counter var jqDoc = jQuery(document); var requestCount = jqDoc.data("ajaxStatus.requestCount"); if (typeof requestCount === 'undefined') { requestCount = 0; } // increase the counter jqDoc.data("ajaxStatus.requestCount", requestCount+1); } function ajaxOnSuccessIndicator() { // get the current counter var jqDoc = jQuery(document); var requestCount = jqDoc.data("ajaxStatus.requestCount"); // check the counter if (typeof requestCount !== 'undefined') { if (requestCount == 1) { // hide indicators document.body.style.cursor = 'auto'; jQuery("#ajaxIndicatorActive").css("display", "none"); jQuery("#ajaxIndicatorCaution").css("display", "none"); jqDoc.data("ajaxStatus.requestCount", 0); } else if (requestCount > 1) { // only decrease the counter jqDoc.data("ajaxStatus.requestCount", requestCount-1); } } } function ajaxOnErrorIndicator() { document.body.style.cursor = 'auto'; jQuery("#ajaxIndicatorActive").css("display", "none"); jQuery("#ajaxIndicatorCaution").css("display", "block"); // reset counter jQuery(document).data("ajaxStatus.requestCount", 0); }The implementation is smart enough and avoid collisions with parallel running Ajax requests (s. counters). Furthermore, the cursor is set to 'wait' if an Ajax request is running. It's how desktop applications behaves.
I took connection icons from ICEFaces showcase.
This comment has been removed by the author.
ReplyDeleteHi Oleg. I am trying to recreate this blog entry. However, I am a bit confused on this JSF code of yours:
ReplyDelete[h:graphicImage id="ajaxIndicatorActive" library="img" name="connect_active.gif"
style="display: none;" styleClass="ajaxIndicator"/]
As far as I know h:graphicImage does not have attribute "library" and "name". Where are these come from, Oleg?
(How do I post code in the comment box)
Sure, it has these attributes in JSF 2. http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/h/graphicImage.html
Delete