Pages

Thursday, October 2, 2008

A Visualforce Component that Beautifies actionStatus!

I have always liked to be able to have one of those fancy AJAX "loading..." messages for my AJAX postback calls. The background screen grays out, all components (textboxes, buttons, etc) become disabled and a little box says "Working on your request..." or "Running..." with that famous GIF animator of a spinning wheel and hey, I finally did it!

In this article I will show how to design the HTML to place the loading message into the center of the screen and gray out the background and the whole nine yards...

In this example I will first create a Visualforce component so that potentially I could use my beautiful "loading..." message on every page I desire, furthermore by forming it as a Component I can parameterize the look and feel of it and easily deploy it into different pages with different coloring styles.

In order to create a new Component click on the "setup" link on the right top corner of the page and find "Develop" item on the left hand-side and expand it, and click on "Components". Alternatively you can browse:
http://yourinstance.salesforce.ccom/apexcomponent/{YourComponentName}

Below you will be to get the code that goes into the component. The HTML code includes two Div elements, one for the gray background and the other for the actual box that the message is shown in and a javascript peice that adjusts the sizes of the div element based on the client browser's resolution and avialable place.


<apex:component >
<!-- Attribute Definitions -->
<apex:attribute name="BorderColor" type="String" required="true" description=""></apex:attribute>
<apex:attribute name="Width" type="String" required="true" description=""></apex:attribute>
<apex:attribute name="Height" type="String" required="true" description=""></apex:attribute>
<apex:attribute name="BackColor" type="String" required="true" description=""></apex:attribute>
<apex:attribute name="BackColor" type="String" required="true" description=""></apex:attribute>
<apex:attribute name="BorderSize" type="String" required="true" description=""></apex:attribute>
<apex:attribute name="ImageUrl" type="String" required="false" description=""></apex:attribute>
<apex:attribute name="Message" type="String" required="false" description=""></apex:attribute>
<apex:attribute name="messageStyle" type="String" required="false" description="Message inline style"></apex:attribute>
<apex:attribute name="BorderStyle" type="String" required="false" description="Message box border style: solid, outset, inset, etc"></apex:attribute>

<div id="salesforceSource_blurybackground" style="position:absolute; left:1px; top:1px; width:100%; height:100%; text-align:center; vertical-align: middle; background-color: #dcdcdc; opacity:0.6;filter:alpha(opacity=60)">
</div>
<div id="salesFroceSource_StatusBox" style="position:absolute; left:100px; top: 100px;width: {!Width}; height:{!Height}; opacity:1;filter:alpha(opacity=100)">
<table border="{!BorderSize}" cellpadding="0" cellspacing="0" style="border-left-color: {!BorderColor};
border-bottom-color: {!BorderColor}; width: {!Width}; border-top-color: {!BorderColor}; height:{!Height};
border-right-color:{!BorderColor}; border-style:{!BorderStyle}; background-color:{!BackColor};">
<tr>
<td align="left" valign="top">
<table border="0" cellpadding="4" cellspacing="0" style="width: 100%; height: 100%">
<tr>
<td style="border-bottom-color:{!BorderColor}; border-bottom-width:1px; border-bottom-style:solid;vertical-align:middle;">
<img src="{!ImageUrl}"/></td>
<td style="border-bottom-color:{!BorderColor}; border-bottom-width:1px; border-bottom-style:solid;vertical-align:middle;{!messageStyle}">
&nbsp;{!Message}</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<script type="text/javascript">
var AgreementForm = document.getElementById("salesforceSource_blurybackground");
AgreementForm.style.height = window.screen.availHeight + "px";
AgreementForm.style.width = window.screen.availWidth + "px";

var ContainerElem = document.getElementById("salesFroceSource_StatusBox");
//ContainerElem.style.display = "block";
AlignToCenter(ContainerElem);

function AlignToCenter(Element)
{
var availableHeight = 0;
var availableWidth = 0;
if (Element.ownerDocument)
{
var docElement = Element.ownerDocument.documentElement;
availableHeight = parseInt(docElement.clientHeight);
if (availableHeight == "NaN") availableHeight = 0;

availableWidth = parseInt(docElement.clientWidth);
if (availableWidth == "NaN") availableWidth = 0;
}

if (availableHeight == 0 || availableHeight == "NaN")
availableHeight = window.screen.availHeight - 200;
if (availableWidth == 0 || availableWidth == "NaN")
availableWidth = window.screen.availWidth - 100;

var msgBoxTop = parseInt((availableHeight - parseInt(Element.clientHeight))/2);
var msgBoxleft = parseInt((availableWidth - parseInt(Element.style.width))/2);

if (msgBoxTop == "NaN" || msgBoxTop == 0)
msgBoxTop = 100;

Element.style.left = msgBoxleft + "px";
Element.style.top = msgBoxTop + "px";
}
</script>
</apex:component>


Pay a good attention to the Attribute section of the of the component those lines will empower you to change the look of the component entirely!

Ok, Now let's see how we can use this component. In Force.com in order to call your component you will need to call it a prefix tag called "c" and don't ask why because that's the way it is.


<apex:actionStatus id="status">
<apex:facet name="start">
<c:enhancedActionStatus BackColor="#efefef" borderColor="#336699" borderSize="3" height="50px" width="120px" ImageUrl="{!$Resource.AjaxAnimation}" Message="Loading..." messageStyle="color:darkred;font-size:11pt;font-weight:bold;"/>
</apex:facet>
</apex:actionStatus>

9 comments:

  1. awesome!

    have you found a way to eliminate the scrollbars issue?

    ReplyDelete
  2. another gr8 post .. :)

    I'm trying to implement this in my VF page. here is the follow up of the code:

    I've an Apex:pageblock tag and inside it i've placed an Apex:pageBlockTable tag.

    This table has been used to display a list of records. now I've implemented PAGING in it, with the help of your paging article.

    So I've at bottom of display 2 buttons, Previous and Next.

    Issue now is that I want an AJAX message display like the one you implemented in this post on click of Next & Previous. I'm not able to implement it.

    Can you help me here?

    ReplyDelete
  3. Thank you, great reusable component!
    I removed the borders and also darkened the background color which, to me, made it stand out more.

    Any plans to publish it on the Code Share (http://developer.force.com/codeshare)? I think a lot of people could benefit from this.

    Anyway, thanks a lot!

    ReplyDelete
  4. How would you modify this to position the two DIV components based on the outer window scroll bar position. With the current code, when I scroll the window down, the DIV are positioned where you can't see it. I can't seem to get the correct document.getElementById scroll.top. Thanks.

    ReplyDelete
  5. brian - I was having this same issue. I added a body tag to the component with an onscroll event. The event fires this function:

    function reposition(){
    // Reposition the background
    if(navigator.appName == 'Window Internet Explorer')AgreementForm.style.top = document.body.scrollTop;
    else AgreementForm.style.top = window.pageYOffset+"px";
    // Reposition the message
    AlignToCenter(ContainerElem);
    }

    You also need to modify the last line of the aligntocenter function to this:
    Element.style.top = msgBoxTop + window.pageYOffset + "px";

    ReplyDelete
  6. This is a great post..
    Thanks

    ReplyDelete
  7. this is very helpfull. thanks

    ReplyDelete
  8. To make the div which covers the page fixed you can change this line of code of the Component section:

    <_div id="salesforceSource_blurybackground" style="position:fixed; left:1%; top:1%; width:100%; height:100%; text-align:center; vertical-align: middle; background-color: #dcdcdc; opacity:0.6;filter:alpha(opacity=60) ">

    ReplyDelete