Build web parts with AJAX

In this post I’m going to explain how to start using ajax in your web parts.  The goal of this article is to reproduce functionality similar to the KPI and BDC web parts in MOSS 2007.  If you don’t know what ajax is or the basics of how it works this article is probably not for you.

ASP.net 2.0 has a cool new feature called client script call backs.  Script call backs basically allow you to use javascript to execute a server side method in your assembly and then do something with the result. This allows you to dynamically update parts of the page, or in this case the webpart, without refreshing the entire page.  This article (http://msdn.microsoft.com/msdnmag/issues/04/08/CuttingEdge/) has a nice explanation of how script call backs work in .net 2.0 but the details of how to actually use it in your code are outdated, so follow the instructions in this post, not the msdn one.  The fact that the script call back feature is built into .net makes it really easy to implement and use.

For this example we will pretend that we have a web part that takes a long time to generate its display. To solve this problem we are going to have the render method output a blank div tag when the web part is initially loaded and then replace the contents of the div, via ajax, with the actual content for the webpart. This will allow the page to load and let the user look at everything else on the page while our web part is loading. Lets get started….

Go ahead and create a new web part project. Add the following directive at the top of your web part class…

using System.Web.UI;

 

 

To let .net know that your class is using script call backs you have to implement the System.Web.UI.ICallbackEventHandler interface….

public class TreeViewRollUp : System.Web.UI.ICallbackEventHandler

 

 

The ICallBackEventHandler interface has two methods,  public string GetCallbackResult() and public void RaiseCallbackEvent(string eventArgs). We will cover these alittle later on.

Now go ahead and add two class variables..

private string datadiv; //This will hold the name of your div tag
private string ajaxdata; //This will hold the data that is returned via ajax…

 

 

The datadiv variable contains the html id of the div tag we are using to render our contents in. The name of this div tag should be unique per instance of the web part. If it was the same name all the time and you had two instances of the web part on the same page they would replace each others contents.

Now add the following to your render method…

this.datadiv = this.ClientID + “content”;  //Uses the client side id of the web part instance + a name we give it

 

writer.Write(“<div id=\”” + this.datadiv + “\”><img src=\”_layouts/images/kpiprogressbar.GIF\” width\”150\”></div>”);

 

 

You can see in the last line we write the div tag with an image in it. This is just the little progress gif animation that the kpi web parts use. You can replace this with whatever you want and it won’t really affect anything.

Now in your OnLoad handler paste the following code…

protected override void OnLoad(EventArgs e)

        {

            this.datadiv = this.ClientID + “content”;

           

string js = Page.ClientScript.GetCallbackEventReference(this, “‘blah'”, “filldiv”, “‘” + this.datadiv + “‘”, true);

           

string contentloader = “var ajaxcommands=”;  window.onload = ajaxloader; function ajaxloader () { eval(ajaxcommands); } function filldiv(arg, ctx) { var mydiv = document.getElementById(ctx); mydiv.innerHTML = arg; }”;    

            if (Page.ClientScript.IsClientScriptBlockRegistered(“contentloader “) == false)

                Page.ClientScript.RegisterClientScriptBlock(Page.ClientScript.GetType(), “contentloader”, contentloader, true);

            Page.ClientScript.RegisterStartupScript(this.GetType(), “myloader”, ” ajaxcommands = ajaxcommands + \”” + js + “;\”;”, true);

            base.OnLoad(e);

        }

 

 

In this method there are two different blocks of javascript that we are registering. The first string, js, is being set by the GetCallbackEventReference. The GetCallbackEventReference method basically returns the javascript that executes the call back to the server to retrieve the data.  We are passing it a reference to our control (the webpart), “Blah” as some initial data that will get passed back to our assembly when the call back is initiated,  the name of the javascript method to execute once the callback is complete, the name of our div tag as the context, and setting asynchronous to true. The javascript code generated by this will look similar to the following…

WebForm_DoCallback(‘ctl00$m$g_a010b3bd_1a68_40f9_b46b_f87050cf516f’,’blah’,filldiv,’ctl00_m_g_a010b3bd_1a68_40f9_b46b_f87050cf516fcontents’,null,true);

Now we could stop here and just use this javascript in the OnClick event of a button to initiate the callback to populate the div tag. For this example we don’t want the user to have to click anything to continue loading the web part so we are going to call the WebForm_DoCallback method when the browser loads. That’s what the contentloader string is all about.  The contentloader  string declares a javascript variable, ajaxcommands,  that holds the command(s) we would like to execute. It also sets the window.onload event to execute the ajaxloader method.  When the ajaxloader  method runs it will use the eval function to execute all the commands stored in the ajaxcommands variable.  The reason you have to do it this way is because if you have more than once instance of your ajax web part on page both of them will try to use the window.onload event to load their data, which creates a problem because only the last set event handler will execute, not both.  So we set a common method to window.onload event and then each web part just appends its WebForm_DoCallback code to the ajaxcommands variable.  This is kind of funky, and I’m sure there is a better way to do it, but it seems to work for me. Finally the contentloader string also includes the filldiv function that gets executed when the call back is completed. The filldiv method simply sets the innerhtml of the div to the contents returned from the callback.

The final step in the OnLoad method of our class is to register the scripts with the page.  Since we only want the contentloader javascript to be rendered once on each page we need to check to see if it has already been registered with the script manager by another webpart on the page.

Now all we have left to do is implement the two methods required by the ICallbackEventHandler interface…

       public string GetCallbackResult()

        {

            return this.ajaxdata;

        }

        public void RaiseCallbackEvent(string eventArgs)

        {

            this.ajaxdata = “Some crazy message here!”;

        }

 

 

 

RaiseCallbackEvent is invoked when the client call back is started.  It is a void method so it doesn’t actually return anything, just sort of gets the data ready for the GetCallbackResult method to return it.   The RaiseCallbackEvent method is where you would put all the code that typically goes in your web parts Render method. In this example we are just going to be sending a simple message back to the client. Go ahead and compile and test it out.

Well hopefully this will help you get started using script call backs. Please post comments or suggestions if you have any.

Advertisements

2 Responses to Build web parts with AJAX

  1. JP says:

    I like the fact that you kept it simple, good article but I would suggest to get more in deep on it.

    Keep it up!..

  2. Surya says:

    Hi
    I tried your approach and found it of much use in one of my projects.
    The only difficulty am facing is in adding multiple web parts with the async callback feature.
    When I add the second web part, the “WebForm_DoCallback” method doesn’t get registered.

    If you have done some work on that front, please post some info.

    Also I found another approach in “http://www.codeproject.com/KB/aspnet/MultipleClientCallbacks.aspx?display=Print” but haven’t tried it.

    Have a look.

    Thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: