Recipes by Category

App Distribution (2) Bundle logic, interface and services for distribution. App Logic (37) The Apex programming language, workflow and formulas for logic. Collaboration (6) The Salesforce Chatter collaboration platform. Database (29) Data persistence, reporting and analytics. Integration (33) Web Service APIs and toolkits for integration. Security (9) Platform, application and data security. Tools (4) Force.com tooling User Interface (36) Visualforce MVC and metadata-drive user interfaces. Web Sites (12) Public web sites and apps with optional user registration and login.
Beta Feedback
Cookbook Home » Using actionStatus and JavaScript for More Meaningful Indicators

Using actionStatus and JavaScript for More Meaningful Indicators

Post by alex.berg12152010  (2012-01-23)

Status: Unverified
Level: intermediate

Better Status Indicator

Visualforce is great! One of the things it makes really simple is Ajax and dynamically updating the current page without a full page refresh. What's that? You're not familiar with this part of Visualforce? You, who is otherwise familiar with Visualforce, fear not! Here's a quick introduction to the Ajax parts of Visualforce.

Quick Visualforce Ajax Primer

There are two parts to Ajax in Visualforce, the plumbing and that which uses the plumbing. The plumbing consists of all the Visualforce tags that start with 'action' or 'command'. Namely, actionFunction, actionPoller, actionRegion, actionStatus, actionSupport, commandButton, and commandLink. These are the tags that add the Ajax plumbing, but we also need the other half - that which uses the plumbing. These are any tags that have attributes like 'status' or 'reRender', such as commandButton or actionFunction.

The majority of cases takes the simple road by using Ajax when clicking a button or link. These cases use an actionButton as the plumbing to directly call a method on the page's controller. In the following example, a commandButton calls a method on the controller called 'save'.

<apex:commandButton action="{!save}" value="Save"/>

Sometimes, however, a request like this can take a long time, and the user will sit there, wondering if their button-click didn't register or if the request was lost. This can be solved by providing a visual indicator to the user that an Ajax request is in process. Visualforce makes this easy by using the actionStatus tag. The significant text here is the 'save-status' name in both the 'id' and the 'status' attributes.

<apex:actionStatus startText=" (saving...)" stopText=" (done)" id="save-status"/>
<apex:commandButton action="{!save}" value="Save" status="save-status"/>

Problem

This actionStatus tag will insert a little text string '(saving)' while the Ajax call is executing, and then change it to '(done)' when the Ajax call is complete. This may be enough of an indicator for some cases, but it is not much more difficult to provide a larger, more visually appealing indicator that the page is reloading itself.

The example in the Visualforce documentation shows just the startText and stopText attributes, but there other, more powerful attributes that are available for us: onclick and oncomplete. These attributes accept JavaScript code to execute when the button is clicked and also when the Ajax call completes, which enable us to many interesting things. You can use my code below, or write your own JavaScript code to do something interesting.

Solution

First, we need to write some JavaScript functions that, when executed, will modify the page elements in a way that indicates to the user that a specific section is loading. There are various ways to do this, but here are my functions.

//You must have included the jQuery library above.
j$ = jQuery.noConflict();

function showLoadingDiv() {
  var newHeight = j$("[id$=lead-edit-section] .pbSubsection").css("height");//Just shade the body, not the header
  j$("[id$=loading-curtain-div]").css("background-color", "black").css("opacity", 0.35).css("height", newHeight).css("width", "80%");
}
function hideLoadingDiv() {
  j$("[id$=loading-curtain-div]").css("background-color", "black").css("opacity", "1").css("height", "0px").css("width", "80%");
}

Now we have to hook the button up to the JavaScript that modifies the page, which we wrote above.

<apex:pageBlockSection title="Lead Edit" columns="2" collapsible="false" id="lead-edit-section">
  <div id="loading-curtain-div"/>
  <apex:pageblockSectionItem ></apex:pageblockSectionItem>
  <apex:inputField value="{!lead.FirstName}"/>
  <apex:inputField value="{!lead.LastName}"/>
  <apex:inputField value="{!lead.Street}"/>
  <apex:inputField value="{!lead.City}"/>
  <apex:inputField value="{!lead.State}"/>
</apex:pageBlockSection>

Discussion

I had problems when using vanilla JavaScript, as it often behaves differently in each browser that executes it. This motivated me to use jQuery library to select and modify the page elements. I suggest that you do the same.

Extensions

Here's an idea to get a bit more fancy. Add these CSS styles to the showLoadingDiv function to animate the loading curtain. This should slowly drop the screen over the section you wish to load. The code is below. Note that I was not able to add these values to the loading-curtain-div element by using the CSS function that jQuery provides, since these experimental elements start with a dash '-'. Instead, you'll have to add the styles to the loading-curtain-div element by writing some CSS somewhere on the page.

#loading-curtain-div {
   -webkit-transition: all 0.10s ease-out;
   -moz-transition: all 0.10s ease-out;
}

If you have suggestions, please post below. Improvements, such as alternative JavaScript functions to call to create a status indicator, would be very helpful. This idea can become even more useful when there are drop-in option for other types of status indicators.

Full Working Visualforce Page

<apex:page standardController="Lead">

<apex:includeScript value="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"/>
<script>
    j$ = jQuery.noConflict();
    
    function showLoadingDiv() {
        var newHeight = j$("[id$=lead-edit-section] .pbSubsection").css("height");//Just shade the body, not the header
        j$("[id$=loading-curtain-div]").css("background-color", "black").css("opacity", 0.35).css("height", newHeight).css("width", "80%");
    }
    function hideLoadingDiv() {
        j$("[id$=loading-curtain-div]").css("background-color", "black").css("opacity", "1").css("height", "0px").css("width", "80%");
    }
    
</script>
<style>
    #loading-curtain-div {
        height:0px;
        width:100%;
        position:absolute;
        z-index:5;
        //-webkit-transition: all 0.30s ease-out;
        //-moz-transition: all 0.30s ease-out;
    }
</style>

<apex:form id="page-form">

    <apex:pageMessages />
    <apex:actionRegion >
    <apex:pageBlock id="lead-edit-block">
        <apex:actionStatus id="save-lead-status" onstart="showLoadingDiv();" onstop="hideLoadingDiv();"/>
        <apex:pageBlockButtons >
            <apex:commandButton action="{!save}" value="Save" status="save-lead-status" rerender="page-form"/>
        </apex:pageBlockButtons>
        
        <apex:pageBlockSection title="Lead Edit" columns="2" collapsible="false" id="lead-edit-section">
            <div id="loading-curtain-div"/>
            <apex:pageblockSectionItem ></apex:pageblockSectionItem>
            <apex:inputField value="{!lead.FirstName}"/>
            <apex:inputField value="{!lead.LastName}"/>
            <apex:inputField value="{!lead.Street}"/>
            <apex:inputField value="{!lead.City}"/>
            <apex:inputField value="{!lead.State}"/>
        </apex:pageBlockSection>
    </apex:pageBlock>
    </apex:actionRegion>
</apex:form>
</apex:page>

References

Here are some recipes that will help you get up-to-speed with getting modifying your Visualforce page with JavaScript.

Share

Recipe Activity - Please Log in to write a comment

Be the first to comment.

X

Vote to Verify a Recipe

Verifying a recipe is a way to give feedback to others and broaden your own understanding of the capabilities on Force.com. When you verify a recipe, please make sure the code runs, and the functionality solves the articulated problem as expected.

Please make sure:
  • All the necessary pieces are mentioned
  • You have tested the recipe in practice
  • Have sent any suggestions for improvements to the author

Please Log in to verify a recipe

You have voted to verify this recipe.