Friday, November 16, 2012

AJAX pattern for SharePoint

Introduction

In the Web 2.0 world heavy use is made of client side AJAX to pull in and refresh data without doing a page postback. When applied to SharePoint the benefits are twofold

·         Background asynchronous postbacks and rending give a better user experience by not requiring a full page refresh

·         The ability to call some specific code for updated data and not having to repopulate the whole page provides for better server side performance

Ideally updating page data with AJAX would involve calling a webservice (ASMX/WCF) but there is a very useful and popular ASP.Net control called UpdatePanel. As you may be aware an UpdatePanel  has some drawbacks that nullify the benefits of AJAX. Specifically there is no such thing as a partial page postback, the whole page will go through its full server-side lifecycle, loading all web parts and data. From the server’s point of view it is equivalent to a full page refresh but only the user interface elements in the AJAX control actually get updated. Depending on the server load and performance requirements this may be a serious issue for AJAX controls that continually poll the server.

 Is there a better way? Read on.

AJAX Pattern

To demonstrate a pattern I have a simple AJAX enabled control using an UpdatePanel that returns the current time from the server (see Figure 1). The UpdatePane is  hosted in a user control (ascx).

Figure 1 - Get Server Time AJAX web part

If we break out the components that make up the web part (Figure 2) we can see the pieces that make up the pattern. The basis of the pattern is to host the control in its own dedicated web page and host this page in an iframe on the main content page. This iframe is encapsulated in a custom control called ‘Rightpoint Viewer’ web part. The only non-reusable element is the AJAX control itself and the Rightpoint Viewer web part can be used for any control, AJAX or not.

Figure 2 - AJAX web part components

The sample webapart Rightpoint.ServerTimeAJAXWP returns the current time from the server but can be replaced by any webcontrol containing an update panel. The Rightpoint Viewer webpart is configured so that it loads a custom page containing the AJAX control and has some properties to help rendering.

 Name

Type

Description

RightpointViewerWP

Web part

iFrame web part with custom properties and javascript for rendering

RP_Empty.master

Master page

A very minimal master page with only the ribbon and main content area

BlankWebPartPage_RP.aspx

Page layout

Single web part zone layout

RightpointServerTimeAJAXWP

Web part

Test AJAX web part that retrieves the current server time

 

Resizing the iFrame

One issue when dealing with an iFrame is its height. If the iframe content is known we can simply set its height of accordingly. For other cases where the content is either unknown or dynamic in nature a different approach is required. 

·         When the iframe page is loaded pass its frame ID and content height to the parent page

·         The parent page and the iframe page are loaded by different threads so we don’t know which will complete first. We must allow for this in code.

·         In the parent page set the iframe height to the height variable plus any offset

·         The iframe page code must fire on each async postback.

To address these inconveniences there needs to be some client-side code. The code must address the fact that iframes are loaded asynchronously and therefore may load before its parent page has completed loading. To solve this client-side code must check the parent and iframe and resize only when both are completed. The code is available in the Rightpoint.Blogs/WebParts/RPFramesUtility.js  file for your perusing pleasure but is actually rendered (registered script) by the Rightpoint Viewer web part and embedded in the RightpointServerTimeAJAXWPUserCControl.ascx file.

To handle the page load for the first time and on each AJAX postback I use a feature of ASP.Net AJAX namely the pageLoad method. This method has the following characteristics

·         The AJAX Sys.Application framework initializes all client side components and events the first time the page loads

·         Every time the page subsequently loads it initializes any subscribed events and then calls any pageLoad methods if they exist

·         Sys.Application parses the code automatically looking for pageLoad, it does not have to be registered.

·         The pageLoad method is called after all other client side initialization is complete which is not true for jQuery $(document).ready() for example

For completeness I also illustrated some of the other AJAX initialization methods for use if needed in the  RPFramesUtility.js  file.

The logical client-side flow is displayed in Figure 3 below

Figure 3 –iFrame to Parent page communications

 

Hosting the AJAX web part

A SharePoint page contains an awful lot of client-side code that may or not be used. If we use the default master page and web part page layout to host our webpart then all this code will run even though we don’t need it. In fact we may need to add code to hide ribbons, menus and navigation components. What a waste of electrons. Since performance is a very desirable trait let’s do the following:

·         Create a custom ‘blank’ master page devoid of the extraneous controls and code that we do not need. RP_Empty.master  is included in the solution files.

·         Create a basic page layout with a single web part zone where we can drop in the AJAX web part through the UI. BlankWebPartPage_RP.aspx is included in the solution files.

·         Add some code so that in Edit mode we can use the ribbon for the above but otherwise don’t load the server-side components that handle this and navigation etc.

In order to have the page layout avail of the SharePoint UI in edit mode it contains a code behind file BlankWebPartPage_RP.cs.  The code looks for a QueryString parameter and value (EditPage=1) and if not detected loads the RP_Empty.master page. If the ‘EditPage=1’ is detected the default master page loads and this we have the ribbon. So for this to happen you the user must manually add ‘?EditPage=1’ to the page URL when you want to edit the page.

private const string PAGE_EMPTY_MASTER = "RP_Empty.master";

private const string QUERY_STRING_EDIT = "EditPage";

        

protected override void OnPreInit(EventArgs e)

{

         base.OnPreInit(e);

 

 

         if(Request.QueryString[QUERY_STRING_EDIT] == null 

    || !Request.QueryString[QUERY_STRING_EDIT].ToString().Equals("1"))

                this.MasterPageFile = PAGE_EMPTY_MASTER;

              

}

 

So that&rsquo