Click here to Skip to main content
Click here to Skip to main content

Using Ajax.NET with the Dojo Toolkit

By , 8 Jun 2009
Rate this:
Please Sign up or sign in to vote.

Introduction

For ASP.NET developers, Ajax.NET is the tool of choice for creating AJAX enabled Web applications. The Dojo Toolkit is a popular Open Source JavaScript framework that provides some really powerful user interface features. Using the two frameworks side by side can present a couple of challenges, in particular when trying to embed Dojo Widgets inside of an Ajax.NET UpdatePanel. This article walks-through the process of creating a re-usable patch for getting around those problems.

Background

Ajax.NET is fully integrated with Visual Studio, and it is built on paradigms that ASP.NET developers are used to. So, it makes a lot of sense to use the Ajax.NET Framework when developing ASP.NET websites.

But, the Dojo Toolkit provides (among other things) a library of widgets and animation tools that help you build flashy client side controls. It even has a lot of nice user controls like a tab strip and an accordion control right out of the box, and it is easy to use and popular.

Now, Dojo also has its own AJAX framework that you could use to completely replace the Ajax.NET framework. But, like I said, the Ajax.NET framework is so easy to use and so well integrated with ASP.NET that it's hard to give up.

So, there is a lot of motivation to use Ajax.NET and the Dojo toolkit side by side. The problem is that they do not always play nice with each other.

In this article, I will discuss one of the problems with embedding Dojo Widgets inside of an Ajax.NET UpdatePanel, and provide a re-usable patch to get around it.

Using the Code

First, we will set up a simple form with a couple of Dojo input fields inside of an UpdatePanel.

<asp:UpdatePanel ID="UpdatePanel1" runat="server"
        ChildrenAsTriggers="true" UpdateMode="Conditional" >
    <ContentTemplate>
    <div>
        <asp:TextBox runat="server"  id="txtDate"
            dojoType="dijit.form.DateTextBox"
            constraints="{datePattern: 'MMM dd, yyyy'}"  />
        +
        <asp:TextBox runat="server"  id="txtAddDays"
             dojoType="dijit.form.NumberSpinner"
            constraints="{min:0,places:0}" />
          days  =
         <asp:Label ID="lblDateResult" runat="server" Text="" />
        </div>
        <br />
        <asp:Button  runat="server" ID="btnSubmit" Text="Get Date"
        OnCommand="btnSumit_Command"  />
    </ContentTemplate>
    </asp:UpdatePanel>

You can see that on our TextBox controls, we add the attribute "dojoType". This tells the Dojo framework to assign certain behaviors to those controls; in this case, the dijit.form.NumberSpinner and dijit.form.DateTextBox (dijit is the Dojo namespace that contains most of the form widgets).

We also have to include the Dojo source files. You are better off including the Dojo source files directly via script tags rather than using the .NET ScriptManager.

<script type="text/javascript" src="jscripts/dojo/dojo.js"
        djConfig="parseOnLoad:true"></script>
<script type="text/javascript">
    dojo.require("dijit.form.DateTextBox");
    dojo.require("dijit.form.NumberSpinner");
</script>

And, we'll add a server-side Command event handler so our app actually does something:

protected void btnSumit_Command(object sender, CommandEventArgs args)
{
    DateTime date = DateTime.Parse(this.txtDate.Text);
    int days = Convert.ToInt32(this.txtAddDays.Text);
    DateTime newDate = date.AddDays(days);
    this.lblDateResult.Text = newDate.ToLongDateString();
}

Now we have a simple application that takes a date and a number and adds the number of days to the date. The controls are inside the UpdatePanel so that portion of our page will be refreshed when we click the Get Date button, but the rest of the web page will remain unchanged. And, we are relying on two Dojo Widgets, the NumberSpinner and the DateTextBox, for our UI.

Here's a screenshot of the app. Everything below the horizontal line is in the UpdatePanel.

widgets.jpg

So let's try it and see what happens. When I click on "Get Date", the lower panel refreshes, and here's what I see:

no_widgets.jpg

Our date calculation worked, but our nice Dojo Widgets are gone, and we just see regular old HTML input controls. What happened?

The problem is with the way Dojo attaches itself to our markup. When we first load our page, we declare our input text boxes to be dojoType="..". What Dojo does is when the body of the web page finishes loading, it parses the loaded HTML and looks for tags that are declared with dojoTypes, and adds the widget behaviors to them. That happens by default when the page body finishes loading. When we refresh our UpdatePanels, only a portion of the HTML on the page is replaced, and Dojo never gets a chance to attach its widget behaviors. Thus, the browser renders our controls as plain old HTML form controls.

To fix this, we need tell Dojo to parse this new HTML after the refresh. Fortunately, Dojo provides a method for doing this with the dojo.parser object. And, Ajax.NET gives us a pageLoaded event to know when an UpdatePanel has finished a refresh.

So, we will attach a subscriber to the PageLoaded event of the Ajax.NET PageRequestManager. This code below goes in a file called AjaxDojoPatch.js.

Sys.Application.add_load(ApplicationLoadHandler);
function ApplicationLoadHandler(sender, args) {
    if (!args.get_isPartialLoad()) {
    	Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageLoaded);
    }
}

 function pageLoaded(sender, args) {
    var updatedPanels = args.get_panelsUpdated();
    if (typeof (updatedPanels) === "undefined") {
        return;
    }
    //call the dojo parser on the newly loaded html
    //in each panel so the new elements are instantiated
    for (i = 0; i < updatedPanels.length; i++) {
        dojo.parser.parse(updatedPanels[i]);
    }
}

... and we include this .js file with the Ajax.NET ScriptManager.

<asp:ScriptManager ID="ScriptManager1" runat="server">
    <Scripts>
        <asp:ScriptReference Path="jscripts/AjaxDojoPatch.js" />
    </Scripts>
</asp:ScriptManager>

Now, when Ajax.NET finishes loading our UpdatePanel, it will run our pageLoaded script. And our pageLoaded script loops through all our updated panels (just one for now), and calls the dojo.parser.parse method on them. That gives Dojo a chance to add its widget behaviors to our form controls.

Let's see if it works, here's what happens when I click on Get Date now ...

widget_already_registered.jpg

Another error! Dojo tells us "Tried to register widget with id==txtDate but that ID is already registered. What now?

The problem here is that we have just re-rendered widgets with the same ID as the widgets that Dojo already loaded the first time it parsed the page. Because the rest of our web page remains unchanged, Dojo still has a reference to those widgets that we are replacing. As far as Dojo knows, those old widgets are still there. That's part of the disconnect between Ajax.NET and Dojo. Ajax.NET replaced the HTML without telling Dojo.

To fix this, we just have to notify Dojo that we are replacing those Widgets. To do this, we'll go back to the Ajax.NET PageRequestManager.

Sys.Application.add_load(ApplicationLoadHandler);
function ApplicationLoadHandler(sender, args) {
    if (!args.get_isPartialLoad()) {
    	Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageLoaded);
    	Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(pageLoading);
    }
}

function pageLoading(sender, args) {
    var updatedPanels = args.get_panelsUpdating();
    if (typeof (updatedPanels) === "undefined") {
        return;
    }
    //remove all the widgets in the outgoing panel
    //so the dojo parser doesn't throw
    //an error when it reloads them.
    for (i = 0; i < updatedPanels.length; i++) {
        var unloadPanel = dojo.byId(updatedPanels[i].id);
        if (!unloadPanel) {
            continue;
        }

        var nodeList = dojo.query('[widgetId]', unloadPanel);
        dojo.forEach(nodeList, function(widget) { destroyWidget(widget) });
    }
}

function destroyWidget(widget) {

    var widgetId = dojo.attr(widget, 'widgetId');
    if (dijit.byId(widgetId)) {
        dijit.byId(widgetId).destroy(true);
    }
}

The pageLoading event occurs right before an UpdatePanel's markup is replaced, so we will still have a reference to the old widgets.

In this method, we loop through all the updating panels and tell Dojo to destroy the widgets inside them (because when the page finishes loading, we will have new widgets to replace them).

We have to do a little more work here than we did when we told the parser to find the widgets. This time we have to find them ourselves.

First, we use the dojo.query method to get a list of all of the widgets under this panel:

var nodeList = dojo.query('[widgetId]', unloadPanel); 

This finds all nodes in the UpdatePanel that have a value specified for the "widgetId" attribute. That's an attribute that Dojo adds to all of its widgets. Then, we use the dojo.forEach method to run our destroyWidget method on each widget in the list.

dojo.forEach(nodeList, function(widget) { destroyWidget(widget) }); 

And, the destroyWidget method has a bit of a gotcha in it too.

First, we get the value of the widgetId attribute using dojo.attr(). Then, we have to get a reference to the widget itself using dijit.byId(). It seems redundant to have to get the widget when we already have a reference to the node (the widget variable). But that reference was supplied by the Dojo query method. In order to get the full reference to the dijit Widget, we have to use dijit.byId(). And, don't get that confused with dojo.byId() which can be used to get other Dojo objects! Try replacing dijit.byId with dojo.byId and see what you get. I am only harping on this because it was a painfully acquired lesson!

Anyways, now that we have destroyed all of the widgets, we are done and Ajax.NET will complete the postback and will refresh the UpdatePanel with our new HTML. After the new HTML is in place, the PageRequestManage.pageLoaded method fires and we tell Dojo to parse all of our new HTML into widgets, and .. voila!

widgets.jpg

Our Ajax.NET UpdatePanel is asynchronously updated with new HTML, and our Dojo widgets are fully functional and intact!

And, the real beauty of this solution is that all you have to do is include the file AjaxDojoPatch.js in any page that mixes UpdatePanels with Dojo widgets, and it will fix the disconnect between the two frameworks without any more hassle.

I have used this solution in a couple of production applications and it has worked like a champ. I don't claim to be a Dojo wizard, so I can't guarantee that it will work in all scenarios, but it has served me well.

I hope you have find this article useful, I would love to hear about anyone else's experiences using Dojo with the .NET Framework.

And, if this is interesting enough to anyone else, I have some ideas for some other articles regarding things I have learned about Dojo and Ajax.NET ... like using Dojo animations with UpdateProgressPanels and the Dojo modal dialog with .NET controls.

Let me know if you think that would be valuable.

Other Options

It is worth noting here that when I started using the Dojo Toolkit with Ajax.NET, Microsoft's AJAX Control Toolkit did not seem to do what I needed. I have been checking that out again and it looks like it has a lot of really nice user interface controls, and presumably, those controls should work seamlessly with Ajax.NET. So, if you are not already vested in Dojo, you might want to check that out too.

History

  • 06/05/2009 - Fixed bug where pageLoaded and pageLoading handlers fired multiple times when there were multiple async postbacks (see forum posting)

References

Disclaimer

The download solution for this article includes a full unaltered copy of the Dojo Toolkit v.1.2.3. This is just for demo purposes, if you are going to be doing any real work with Dojo, get the latest version straight from the official Dojo site (see References).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Seth Dingwell
Software Developer Top Side Software
United States United States
Seth Dingwell is the Owner and Principal Architect of Top Side Software in Charlotte, NC.
 
One of Seth's passions is building cutting edge software with C# and whatever the latest and greatest technologies are.
 
When he's not developing software, Seth enjoys hiking, building wooden boats, and spending time with his wife Andrea and daughters Sienna and Annabelle.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberAmol_Joshi29-Dec-11 4:16 
GeneralMy vote of 5 PinmvpMd. Marufuzzaman9-Jul-11 7:53 
GeneralMy vote of 5 Pinmemberandro196612-Apr-11 3:30 
AnswerSlight Modification Pinmemberjohnsonphil11-May-09 5:11 
GeneralRe: Slight Modification PinmemberSeth Dingwell5-Jun-09 18:19 
GeneralRe: Slight Modification PinmemberSeth Dingwell8-Jun-09 6:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 8 Jun 2009
Article Copyright 2009 by Seth Dingwell
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid