Search code examples
ajaxjsf-2

How Can I Use Ajax To Update A GraphicImage Without Blinking?


Please have a look at the following form. In particular, take note of the ajax and graphicImage elements. The ajax element is embedded in a selectOneMenu. When the menu selection is changed the image is correspondingly updated. The update will sometimes cause a "blink" (old image disappears, form resizes (shrinks), new image appears, form is back to original size). I am guessing that the not blink (blink) results from the speed (or lack thereof) with which the new image is fetched from the url (a server elsewhere on the web). Short of caching all the images locally, is there a way to address this? Cause old image to remain in place until the new image is ready? At least prevent the form from resizing?

<h:form rendered="#{orderController.addingNew}" styleClass="demo">
    <fieldset>
        <legend>New Order</legend>

        <h:panelGrid columns="2">
            <h:outputText value="Patient"></h:outputText>
            <h:selectOneMenu validatorMessage="required" value="#{orderController.patientId}"  onchange="submit()">
                <f:selectItems value="#{orderController.patients}" />
            </h:selectOneMenu>

            <h:outputText value="Product"></h:outputText>
            <h:selectOneMenu value="#{orderController.productId}">
                <f:selectItems value="#{orderController.products}" var="product" itemValue="#{product.id}" itemLabel="#{product.name}" />
                <f:ajax render="productImage" listener="#{orderController.productSelectionChanged}"/>
            </h:selectOneMenu>

            <h:graphicImage url="#{orderController.productImageUrl}" id="productImage"/>
        </h:panelGrid>

        <h:panelGroup>
            <h:commandButton value="Save" action="#{orderController.save}" accesskey="s" styleClass="demo"/>
            <h:commandButton value="Cancel" action="#{orderController.cancel}" accesskey="c" immediate="true" styleClass="demo"/>
        </h:panelGroup>
    </fieldset>
    <h:outputText value="&#160;" />
</h:form>

Solution

  • You could bind an onevent attribute to your ajax tag:

    <f:ajax render="productImage" listener="#{orderController.productSelectionChanged}" 
        onevent="ajaxEventListener"/>
    

    Then, you get notified whenever some phase of the ajax process happens. You might be interested in implementing something like this with jQuery:

    function ajaxEventListener(data) {
        var status = data.status; // Can be "begin", "complete" or "success".
        var source = data.source; // The parent HTML DOM element.
        //Give your form an id in order to access the image in JS
        var $image = $(document.getElementById("yourFormId:productImage"));  //Grab your image
    
        switch (status) {
            case "begin": // Before the ajax request is sent.
                // Fade out your image
                $image.fadeOut( "slow" );
                break;
    
            case "success": // After update of HTML DOM based on ajax response.
                // You've got the url properly updated, start fading in the image again
                $image.fadeIn( "slow" );
                break;
        }
    }
    

    In order to improve loading times, you should read some papers about how to cache resources in the web browser. That's not JSF specific, though, it's something you could do with a web filter.

    See also: