Asp.Net Ajax: How do you know all of your Ajax calls have completed?

August 22, 2007

Some Background

Here is a situation I ran into recently: I have an Asp.Net Ajax service (.asmx) which has a method used to create rows in a database similiar to:

[WebMethod]
public int CreateItem(string description, double value)

This method basically created a row in the database and returns the id of the item created. It is called from javascript like this:

function MyButtonClicked() {
    MyNamespace.Service.CreateItem("My Item", 5.3, OnSucceeded, OnFailed);
}

function OnSucceeded(result, userContext, methodName) {
}

function OnFailed(error, userContext, methodName) {
}

If you are not familiar with calling Ajax web services from javascript, you can read more from the Calling Web Services from Client Script in ASP.NET AJAX article on asp.net. Basically, I am just calling my CreateItem method (which is part of a .asmx service) from javascript – how this magic happens isn’t really too important to understand the problem I was facing.

Notice that the last two parameters I passed into my CreateItem call in javascript are references to callback methods that should be called in the event that my call succeeded or failed. A successful call will end up calling back my OnSucceeded method which has 3 parameters:

  • result – this contains any values returned from my method. In this case it will be the id of the item that was inserted.
  • userContext – this is an optional item that can be passed into the original call to the method and will just be passed on to the callback method. I am not using this parameter in this situation
  • methodName – this is the name of the method that was called which resulted in the OnSucceeded method being called. In my case, this will be the string “CreateItem”

Now for the Problem

Ok, now with all that background I can actually spell out the problem I had to solve. In many cases, I make calls to the CreateItem method many times in a row. So if I need to create three items in the database, I may do something like this in javascript:

MyNamespace.Service.CreateItem("My first Item", 1, OnSucceeded, OnFailed);
MyNamespace.Service.CreateItem("My second Item", 2, OnSucceeded, OnFailed);
MyNamespace.Service.CreateItem("My third Item", 3, OnSucceeded, OnFailed);

Notice that all three calls have the same callback on success. Now lets say I want to create these three items and then as soon as all three calls are completed successfully I want to forward the user to another page. At first you may think I can just do something like this:

function OnSucceeded(result, userContext, methodName) {
    if (methodName == "CreateItem") {
        window.location = "NextPage.aspx";
    }
}

This would forward the user to a new page as soon as our call to CreateItem has completed successfully. There is one huge problem though: how do we know which call to CreateItem succeeded? The first one? The Second one? The third one?

Obviously we only want to forward the user in the case that our final call has succeeded. As you can see, this problem only gets worse if we have other calls on the same page that could be pending at the time we want to forward the user to the next page.

My Solution – Count Your Calls

So here is how I solved the problem: I made a variable which I would use to keep track of how many pending calls there were at any given time. At the top of my javascript page I first declare:

var pendingCalls = 0;

Then, each time I make a call, I increment the counter:

++pendingCalls;
MyNamespace.Service.CreateItem("My first Item", 1, OnSucceeded, OnFailed);
++pendingCalls;
MyNamespace.Service.CreateItem("My second Item", 2, OnSucceeded, OnFailed);
++pendingCalls;
MyNamespace.Service.CreateItem("My third Item", 3, OnSucceeded, OnFailed);

Then I need to make sure I decrement the counter each time a call succeeds or fails:

function OnSucceeded(result, userContext, methodName) {
    --pendingCalls;
}

function OnFailed(error, userContext, methodName) {
    --pendingCalls;
}

This way, we know how many pending calls we have at any given time. Then to forward the user to a new page once all calls are completed, I did the following:

function OnSucceeded(result, userContext, methodName) {
    --pendingCalls;
    if (methodName == "CreateItem" && pendingCalls == 0) {
        window.location = "NextPage.aspx";
    }
}

As you can see, asynchronous programming can be very difficult. Has anybody else had to solve this problem? If so, how did you do it?

Advertisement

Using JQuery to Make Asp.Net Play Nice with Asp.Net

August 20, 2007

I have found that developing an Asp.Net application that makes heavy use of javascript is very difficult. One of the major pain points is dealing with the horrendously long ids that Asp.Net server controls generates. For example, the following Asp.Net markup:

<asp:TextBox ID="myTextBox" runat="server" />

can cause the TextBox to be rendered as:

<input name="ctl00$ContentPlaceHolder1$myTextBox" 
    type="text" id="ctl00_ContentPlaceHolder1_myTextBox" />

if the page uses a MasterPage or any moderately complex control hierarchy. Asp.Net does this to minimize naming conflicts. This is the whole concept of a Naming Container. Asp.Net uses the interface INamingContainer to mark controls that can be rendered inside of a naming container. The end result is a really long id that can change based on the items place in the control hierarchy. This is a problem if you want to use the elements rendered by server controls in your client side javascript code.

First, the Hack

One technique for getting around this I have seen, has a definite bad code smell to it. You basically have Asp.Net render the itemsID inside your javascript code using the <% %> syntax like so:

<script type="text/javascript">
    var myValue = document.getElementById('<%= myTextBox.ClientID %>');
</script>

this will render as

<script type="text/javascript">
    var myValue = document.getElementById('ctl00_ContentPlaceHolder1_myTextBox');
</script>

This technique has a couple of huge short commings:

  • It is really hard to maintain
  • It doesn’t support having your javascript in a library or a separte .js file

JQuery to the Rescue…sort of

There are many javascript client libraries out there that help ease the headache of writing javascript code that works across browsers. I am currently working on a project where we have decided to use JQuery which I am very impressed with.

One of the main features of using the JQuery library, is its use of selectors to find elements on the page. I will not get that deeply into this rich DOM querying language here, but you can read all about it from the JQuery Documentation.

JQuery selectors allow me to find items in the page many different ways. For example, I can find the textbox in my example above using the following code:

var myTextBox = $("input:text");

This will actually find every textbox on the page but since we have only on textbox on our page, it works. Alternatively, you can use something like the Css class of the item. Suppose we gave the textbox a css class of ‘textInput’:

<asp:TextBox ID="myTextBox" runat="server" CssClass="textInput" />

Now, the control renders as

<input name="ctl00$ContentPlaceHolder1$myTextBox" 
    type="text" id="ctl00_ContentPlaceHolder1_myTextBox" 
    class="textInput" />

so we can use JQuery to select just this item using the following javascript

var myTextBox = $("input:text[@class=textInput]");

or

var myTextBox = $("input.textInput");

or even simpler

var myTextBox = $(".textInput");

There are many ways to select the same item, but you get the idea.

So does this solve our problem with long IDs? No, not really, we would still have to use the following javascript to select this item by id:

var myTextBox = $("#ctl00_ContentPlaceHolder1_myTextBox");

The Solution

As I was working through this problem in my head, I remembered somethign about javascript: it is more than just the attributes that already exist in DOM elements. You are not restricted to just using ID or Class. So why not just come up with your own attribute to use just for the purpose of selecting DOM elements? So here is a pretty workable solution I came up with:

<asp:TextBox ID="myTextBox" runat="server" ClientSelector="myTextBox" />

I just use a made up attribute called ‘ClientSelector’ (you can use whatever you fancy and it renders like this:

<input name="ctl00$ContentPlaceHolder1$myTextBox" type="text" 
    id="ctl00_ContentPlaceHolder1_myTextBox" 
    ClientSelector="myTextBox" />

so now we can use JQuery to select the item with this statement

var myTextBox = $("input[@ClientSelector=myTextBox]");

So now we can use the same selector for just this one textbox no matter if it is in a MasterPage or not. We don’t have to care how the id renders anymore. I have found this technique very useful. What do you think? Is this also too much of a hack?


Microsoft Ajax Enabled Web Site Organization Patterns

August 1, 2007

Lately I have been writing a lot of code utilizing the Microsoft Ajax framework. This means that I am writing “Ajax Services” to handle my asynchronous callbacks. Ajax Services is a term I heard from Rob Bagby which emphasizes the fact that while these services are generally in a .asmx file and the methods are decorated with the [WebMethod] attribute, they are not really web services. They are a special form of service made just for Ajax callbacks. This additional file has got me thinking lately about how I can organize the files in my web site so that it doesn’t get too cluttered.

Let me start off by saying that this post is just a diary of my thoughts and I don’t really think I have found the best solution yet. That is why I am putting this out there to see how others are approaching this situation.

Too Many Files

File Orginization In extreme cases, I can have four different separate files (6 if you count the code behind file) for just one page. Typically, you have one javascript file and one css file per website but I have found this to just be too cumbersome lately. I find that my css and js files just become too cluttered and hard to maintain. So I end up with:

  • .asmx – Contains the WebMethods for my ajax callbacks
  • .aspx – Contains the layout for my page in typically Asp.Net fasion
  • .css – Contains the style definitions for the page layout
  • .js – Contains the javascript methods which typically respond to events on my page and make callbacks to my ajax services.

So now lets look at a folder on the website which has 3 different pages – a very typical scenario.

Too many files!

This layout quickly becomes unwieldy in its own way. All of the sudden your solution explorer view of your site becomes very hard to navigate and it becomes difficult to maintain the references to all those files. Now if I have a very simple page which has no need for any ajax callbacks or custom css mixed in there, It sort of gets lost in the shuffle

Lost in the Shuffle

See how your eye can easily miss the ‘MyPage2-Simple.aspx’ page?

A Better Way?

Another idea I had was to have one folder per page which would look like:

Is this better?

This is a little better but it is still cluttered. Just for completeness, lets look at our site with only one js, css, and asmx file.

Cleaner?

While this definitely looks a lot cleaner, it also has a code smell of it’s own. Imagine what our common files (js, css, asmx) must look like. They have the code from 3 separate pages all thrown in together.

The last idea I will throw out there is to put each file type in its own folder.

Grouped Together

I also like this better than most but it breaks down pretty quickly when you have your web site organized into many folders. Do you keep the script, service, and style files in the root folders or do you have a Script, Services, and Style folder for each sub folder?

Like I said earlier, I haven’t really found a solution that I think is best. Currently, I am trying to organize my project by having each action in its own folder and within these folders I put all the extra files (style, script, or callbacks).

How do you organize your web sites? How do you non Asp.Net people organize yours?