Saturday, July 28, 2012

ASP.NET Spiced: AJAX

Introduction

Since the start of Web programming, numerous tradeoffs have existed between Web applications and desktop applications. For example, it has been generally accepted that Web applications don't provide the same type of rich user interface as desktop applications. On the flip side, Web applications are platform independent and provide an easier development mechanism. One area that's been a continuous battleground for Web developers has been the seemingly simple task of providing more responsive applications.

Traditionally, a response to a user's input could only be retrieved by submitting a new request to the Web server. In some cases, developers could load all responses on the client (using JavaScript) and provide a better user experience. A common example of this technique is to dynamically load a list of states or provinces, based on a selected country. Unfortunately, in many cases, neither posting back nor loading everything into JavaScript felt correct. Either posting back created too much of a UI disconnect, or an unmanageable amount of data was required on the client (which often resulted in less-than-readable JavaScript). AJAX provides a new in-between alternative, capable of leveraging the server-based application while maintaining a responsive and slick feel.

What Is AJAX?

AJAX, short for Asynchronous JavaScript And XML, isn't a technology but rather a grouping of technologies. AJAX uses a communication technology (typically SOAP and XML) to send and receive an asynchronous request/response to the server, and then leverages presentation technologies (JavaScript, DOM, HTML, and CSS) to process the response. Applications using AJAX are legitimate today, because most browsers support the necessary technology. For a more detailed definition of AJAX, visit the AJAX Wikipedia entry.

What does AJAX really mean? It lets you execute a server-side method through a JavaScript call, without requiring a browser refresh. Think of it as a mini request/response that happens behind the scenes from the user. If you still aren't clear what AJAX is, take a look at two popular examples from Google: Google Suggests and Google Maps. If you are new to AJAX, the responsiveness of those two applications should make your skin tingle slightly.

AJAX for ASP.NET

A lot of plumbing goes into making AJAX tick. Chances are that you don't want to spend hours or days figuring out the internals of AJAX, but would rather start creating AJAX-enabled applications today, to meet existing demands (and if you do want to know how it works internally, I'm certainly not the person to ask). There are a number of tools developers can use to get started quickly. Specifically, we'll look at the free and open-source Ajax.NET written by Michael Schwarz. Ajax.NET takes care of all the implementation details, is .NET-aware, and can be extended. Microsoft ASP.NET 2.0 introduces its own flavor of asynchronous callbacks through the Client Callback functionality, and it was recently announced that an AJAX implementation, code-named "Atlas," was in the works.

The terminology might be confusing, but when I talk about AJAX, I'm talking about the overall framework for asynchronously calling server-side functions from the client. When I mention Ajax.NET, I'm referring to a specific implementation that helps you build solutions that take advantage of the AJAX framework.

To learn more about the Client Callback functionality of ASP.NET 2.0, make sure to visit Bertrand Le Roy's blog.

AJAX Hands-On

The remainder of this article will mostly be devoted to three meaningful examples that utilize the power of AJAX using Ajax.NET. The guide will contain code in both Microsoft C# and Microsoft Visual Basic .NET, sometimes both will be provided, and sometimes only one or the other. The code to make all this happen is so easy, that C# developers should be able to easily follow the Visual Basic .NET-only code, and vice versa! Sample C# and Visual Basic .NET projects are included with this article for download, and provide working and running code. Before we get to the examples, a primer on setting up and working with Ajax.NET is necessary.

Ajax.NET

The AJAX.NET documentation and Web site do a good job at getting developers up and running. We'll briefly go over the core steps you need to know before looking at some concrete examples of this technology in use.

Start by downloading and unzipping the AJAX file from the AJAX.NET project Web site, creating a new ASP.NET project (in either Visual Basic .NET or C#, whichever you prefer) and adding a reference to the AJAX.dll file. The only configuration step beyond that is to add the following code in the web.config file, inside the <system.web> element.

<configuration>       <system.web>      <httpHandlers>     <!-- Register the ajax handler -->     <add verb="POST,GET" path="ajax/*.ashx"           type="Ajax.PageHandlerFactory, Ajax" />    </httpHandlers>      ...    ...    </system.web>  </configuration>  

In order to make server-side functions available through JavaScript, two things must be done. First, the function or functions in question must be marked with the Ajax.AjaxMethodAttribute. Second, the class containing these functions must be registered through a call to Ajax.Utility.RegisterTypeForAjax during the page load event. Don't worry if it sounds complicated; it's really only two extra lines in your code. Let's look at an example.
//C#

public class Sample : System.Web.UI.Page  {   private void Page_Load(object sender, System.EventArgs e)   {    //Register the class containing the server-side function    //we are interested in    Ajax.Utility.RegisterTypeForAjax(typeof(Sample));   }   [Ajax.AjaxMethod()]   public string GetMessageOfTheDay()   {    return "Experience is the mother of wisdom";   }  }  'VB.NET  Public Class Sample   Inherits System.Web.UI.Page     Private Sub Page_Load(sender AsObject, e As EventArgs)                                            Handles MyBase.Load    'Register the class containing the server-side function    'we are interested in    Ajax.Utility.RegisterTypeForAjax(GetType(Sample))   End Sub   <Ajax.AjaxMethod()> _   Public Function GetMessageOfTheDay() As String    Return "Experience is the mother of wisdom"   End Function  End Class  

The above example first tells Ajax.NET to look at the Sample class for Ajax-friendly methods. This happens to be the same class as the actual page, but it could be any .NET class, or multiple classes could be registered. Ajax.NET will then look through the specific class for any methods that are marked with the AjaxMethodAttribute, of which the Sample class has one, GetMessageOfTheDay.

With that done, the only thing left to do is to make use of it in JavaScript. Ajax.NET automatically creates a JavaScript variable named the same as the registered class (in this example it will be Sample) that exposes functions named the same as the AjaxMethod (in this example, GetMessageOfTheDay). Here's what it looks like.

<script language="javascript">   Sample.GetMessageOfTheDay(GetMessageOfTheDay_CallBack);   function GetMessageOfTheDay_CallBack(response)   {    alert(response.value);   }  </script>  

The JavaScript GetMessageOfTheDay expects the same parameters as its server-side counterpart (in this case, none) in addition to the JavaScript callback function to execute and pass the response to when done. Here we see the asynchronous nature of AJAX at work, since the call to GetMessageOfTheDay doesn't prevent other JavaScript code from executing, nor does it prevent the user from continuing to work on the page. When the server-side processing is done, Ajax.NET calls the specified callback function, GetMessageOfTheDay_CallBack, and passes it the response that comprises the server-side's return value.

The mapping between server-side code and JavaScript code might be confusing. Figure 1 shows the outline of both server-side code and JavaScript code, along with the mapping between the two.

Aa479042.ajaxspiced_fig01(en-us,MSDN.10).gif

Figure 1. Mapping between server-side code and JavaScript code

There's certainly more to Ajax.NET that's of interest, such as the support for .NET types and the richness of the callback response (it's more than just a value). The following examples will hopefully highlight some of the features and help you envision how AJAX might help you create a successful application.

Sample 1: Linked Drop-Down List

The beginning of this article briefly discussed the two traditional approaches used for linking one DropDownList to another. Either the page is posted back when the selected index changes, or all the possible data is loaded into JavaScript arrays and dynamically displayed. Hopefully you can see how AJAX might be used as an alternative to both of these solutions.

First, let's look at our data interface and drive the example from there. Our data access layer will expose two methods: the first will retrieve a list of countries our system supports, while the second will take a country ID and return a list of states/provinces. Since this is pure data access, we'll just look at the method signatures.

//C#  public static DataTable GetShippingCountries();  public static DataView GetCountryStates(int countryId);  'VB.NET  Public Shared Function GetShippingCountries() As DataTable  Public Shared Function GetCountryStates(ByVal countryId As Integer)                                                          As DataView  

Now, let's flip to the opposite layer and create our simple Web form.

<asp:DropDownList ID="countries" Runat="server" />   <asp:DropDownList ID="states" Runat="server" />  <asp:Button ID="submit" Runat="server" Text="Submit" />  

The Page_Load event is equally straightforward, and is as common as the preceding Web form. We use our data access layer to retrieve the available countries and bind that to our countries DropDownList.

//C#  if (!Page.IsPostBack)  {   countries.DataSource = DAL.GetShippingCountries();   countries.DataTextField = "Country";   countries.DataValueField = "Id";   countries.DataBind();   countries.Items.Insert(0, new ListItem("Please Select", "0"));  }  

This is where our code stops being typical. First, we'll create our server-side function that we want to call from JavaScript.

'VB.NET  <Ajax.AjaxMethod()> _  Public Function GetStates (ByVal countryId As Integer) As DataView   Return DAL.GetCountryStates(countryId)  End Function  

This is like any other function you'd normally have: it expects the ID of the country we want to get, and passes the request to the DAL. The only difference is that we've marked the method with the AjaxMethodAttribute. The last server-side step left to do is register our class containing the above method (in this case it's our code-behind) with Ajax.NET through a call to RegisterTypeForAjax.

//C#  Ajax.Utility.RegisterTypeForAjax(typeof(Sample));  'VB.NET  Ajax.Utility.RegisterTypeForAjax(GetType(Sample))  

We're almost done; all that's left is to call the GetStates method from JavaScript and handle the response. We logically want to call GetStates when the user selects a new item from the list of countries. To do so, we'll hook into the JavaScript onChange event. Our Web form code thus changes slightly.

<asp:DropDownList onChange="LoadStates(this)"                     ID="countries" Runat="server" />  

The JavaScript LoadStates function will be responsible for issuing the asynchronous request through the proxy created by Ajax.NET. Remember that, by default, the proxy Ajax.NET created is of the form <RegisteredTypeName>.<ServerSideMethodName>. In our case, that'll be Sample.GetStates. We'll also want to pass in our country ID parameter and the callback function that Ajax.NET should call once the server-side function is done.

//JavaScript  function LoadStates(countries)  {   var countryId = countries.options[countries.selectedIndex].value;   Sample.GetStates(countryId, LoadStates_CallBack);  }  

Finally, the last step is to handle the response in our LoadStates_CallBack function. Probably the most productive feature of Ajax.NET is its support for a number of .NET types (I've mentioned this a few times already). Recall that our server-side function returned a DataView. What does JavaScript know of DataViews? Nothing, but JavaScript is an object-oriented language, and Ajax.NET takes care of not only creating an object similar to the .NET DataView, but also maps the return value from the function to the JavaScript clone. You should keep in mind that the JavaScript DataView is merely a replica of the actual DataView, and doesn't (currently) support much more functionality than looping through the rows and accessing the column values (functionality such as setting the RowFilter or Sort properties).

function LoadStates_CallBack(response)  {   //if the server-side code threw an exception   if (response.error != null)   {    //we should probably do better than this    alert(response.error);     return;   }     var states = response.value;     //if the response wasn't what we expected     if (states == null || typeof(states) != "object")   {    return;   }   //Get the states drop down   var statesList = document.getElementById("states");   statesList.options.length = 0; //reset the states dropdown     //Remember, its length not Length in JavaScript   for (var i = 0; i < states.length; ++i)   {    //the columns of our rows are exposed like named properties    statesList.options[statesList.options.length] =            new Option(states[i].State, states[i].Id);   }  }  

After a bit of error checking, the preceding JavaScript gets the states drop-down list, loops through the response's value, and dynamically adds options to the drop-down list. The code is clean, straightforward, and eerily similar to C# and Visual Basic .NET. Personally, as a developer who's created JavaScript arrays based on server-side variables and linked them together, I still have a hard time believing it actually works.

There is one major issue that might not be obvious. Since the DropDownList was dynamically created in JavaScript, its items aren't part of the ViewState and won't be maintained. That means that our button's OnClick event handler needs to do some extra work.

'VB.NET  Private Sub submit_Click(sender As Object, e As EventArgs)    Dim selectedStateId As String = Request.Form(states.UniqueID)      'should do some user validation...    states.DataSource =        DAL.GetCountryStates(Convert.ToInt32(countries.SelectedIndex))    states.DataTextField = "State"    states.DataValueField = "Id"    states.DataBind()    states.SelectedIndex =       states.Items.IndexOf(states.Items.FindByValue(selectedStateId))  End Sub  

First, we can't use the states.SelectedValue property and must use Request.Form. Second, if we want to redisplay the list to the user, we need to bind the states DropDownList, thankfully reusing the same data access method. Finally, the selected value has to be programmatically set.

Sample 2: Document Locker

For our next example, we'll take a more complete feature and improve it with AJAX. This example will be of a simple document management system. Like any decent document management system, we have to provide concurrency management. That is, we need a way of handling two users trying to edit the same document. We'll do so by creating some type of locking mechanism that prevents one user from editing a document already being edited. We'll use AJAX to make the user's experience with our locking mechanism more pleasant. First, we'll create a queue of documents the user tried to edit but couldn't (because it was already being edited), and automatically notify him or her when they become available. Second, we'll make sure to unlock a document if the user closes his or her browser or navigates away. This last feature helps ensure that documents don't stay forever locked. For the purpose of this guide, we'll skip the functionality not related specifically to the AJAX implementation; however, the downloadable project contains everything.

First, when a user tries to edit a document, we'll try to acquire an exclusive lock on it and, failing that, we'll add the document to the user's queue and send him or her back to the main page. There's nothing specific to AJAX here, but we'll look at the code in order to give the example the necessary context. In the OnLoad event of the Page used for editing, the following is added.

//C#  if (!Page.IsPostBack)  {   //should validate user input   Document document = GetDocument(Request.QueryString["id"]);   //We have the document, but can't edit it!   if (!Locker.AcquireLock(document))   {    //let's add it to the user's list of docs to watch    User.CurrentUser.AddDocumentToQueue(document.DocumentId);    Response.Redirect("DocumentList.aspx");   }   //ok, we have the document and CAN edit it   //...  }  

The key line is where the document is added to the current user's queue, which adds it to the session. Next, we'll create a user control, which can be placed on any page, used to notify the user when a queued document has become available. This user control will contain a single AJAX method, along with the code necessary to register the class with AJAX.

'VB.NET  Private Sub Page_Load(s As Object, e As EventArgs)                                      Handles MyBase.Load   Ajax.Utility.RegisterTypeForAjax(GetType(UnlockNotifier))  End Sub    'Loops through the queued documents and checks if they're available  <Ajax.AjaxMethod()> _  Public Function GetUnlockedDocuments() As DocumentCollection   'Get all the queued document ids belonging to the user   Dim queuedDocument As ArrayList = User.CurrentUser.DocumentQueue   Dim unlocked As DocumentCollection = New DocumentCollection     For Each documentId As Integer In queuedDocumentIds   'If the queued document is no longer locked    If Not Locker.IsLocked(documentId) Then     unlocked.Add(Document.GetDocumentById(documentId))    End If   Next     Return unlockedDocuments  End Function  

All that's needed now is some JavaScript to make the request and handle the response. We'll place the released document information, if any, inside a table that we'll dynamically build, based on the response. To do so we'll start off with our HTML.

<div id="notifyBox" style="display:none;">   <b>The following queued documents can now be edited</b>   <table cellpadding="5" cellspacing="0"          border="0" style="border:1px solid #EEE;"          id="notifyTable">    </table>  </div>  

We use the DIV tag to hide everything when no documents are available (or maybe none are queued for the user), and the TABLE tag to display the results. We'll use a polling system to see if any queued documents are available. Basically, this means we'll keep calling the server-side method, with a delay, and display the results. The first call will simply occur when the page loads, and subsequent calls will be timed to happen every X seconds.

<script language="javascript">  window.setTimeout("PollQueue();", 2000);  //fires every 2 seconds to check if a queued document was released  //in a real system with a lot of users, 2 seconds might put too high  //a load on the server. We could even check first to see if the user  //even has anything queued, but we'll certainly need to do some  //performance testing  function PollQueue()  {   //UnlockNotifier is the type we registered with Ajax.NET   //GetUnlockedDocuments is a method within that type marked with   //the AjaxMethod attribute   UnlockNotifier.GetUnlockedDocuments(PollQueue_CallBack);   //calls itself every 2 seconds   window.setTimeout("PollQueue();", 2000);  }  </script>  

All that's left is to handle the response. This is similar to the code from the previous example. First, check for any errors, get the response, loop through the available documents, and dynamically build our HTML, in this case adding rows and columns to our table.

function PollQueue_CallBack(response)  {    var notifyBox = document.getElementById("notifyBox");    var notifyTable = document.getElementById("notifyTable");    //if we couldn't find our table notification box    if (notifyBox == null || notifyTable == null)    {      return;    }    //if the server-side code threw an exception    if (response.error != null)    {       notifyBox.style.display = "none";       alert(response.error); //we should probably do better than this      return;    }            var documents = response.value;    //if the response wasn't what we expected        if (documents == null || typeof(documents) != "object")    {      notifyBox.style.display = "none";      return;      }      for (var i = 0; i < notifyTable.rows.length; ++i)    {     notifyTable.deleteRow(i);    }    for(var i = 0; i < documents.length; ++i)    {          var row = notifyTable.insertRow(0);      row.className = "Row" + i%2;      var cell = row.insertCell(0);      cell.innerHTML = documents[i].Title;      cell = row.insertCell(1);      var date = documents[i].Created;      cell.innerHTML = date.getDay() + "/" + date.getMonth()                        + "/" + date.getYear();      cell = row.insertCell(2);      cell.innerHTML = "<a href='DocumentEdit.aspx?id="                        + documents[i].DocumentId + "'>edit</a>";    }     notifyBox.style.display = "block";   }  

The last quick improvement we'll look at is to automatically unlock a document being edited if the user closes his or her browser, navigates to another link, or clicks the back button. Typically, this is achieved by hooking into the JavaScript OnBeforeUnLoad event or OnUnload event, opening a new small popup that does some cleanup on page load, and then closes itself down. While your own use of popups might be legitimate, others don't play so nice, resulting in popup blocking and documents that stay locked forever. To solve this problem, we'll still rely on the two JavaScript events, but instead of launching a popup, we'll execute a server-side method through AJAX. On our page used to edit a document, the one that places a lock, we add some simple JavaScript.

<script language="javascript">  //Makes sure the document is unlocked if the user closes the   //browser or hits the back button  window.onbeforeunload = ReleaseLock;  function ReleaseLock() {   Locker.ReleaseDocument(<%=DocumentID%>);  }  </script>  

Here, DocumentId is a variable defined and set in code behind. Alternatively we could store DocumentId in the session and access that in the server-side ReleaseDocument. ReleaseDocument basically removes the document from the list of locked documents.

Sample 3: Forums Subject Search

The last example we'll look at will be the modification of an existing app. I heard this idea first proposed by Josh Ledgard as a feature being played with for the MSDN forums. The goal of it is to try to help users with questions help themselves, as well as to curb the number of duplicate posts. Basically, when a user goes to ask a new question in the forum, he or she enters a subject and a question, all too often not doing a search to see whether the question was already asked and answered. Enter AJAX. After the user finishes entering the subject (and tabs out of the field), we'll asynchronously search the forums, based on the subject, and non-obtrusively present the results to the user. Sometimes the results will be helpful, sometimes not.

To accomplish this, we'll modify the asp.NETPRO Reader's Choice Award for Best Forum Application, CommunityServer. The downloadable samples doesn't include the code in this section (or the forums), but you can learn more about CommunityServer at http://communityserver.org/ and apply the following code snippets to it.

With CommunityServer installed and Ajax.NET configured (references and handler added to web.config), we need only make a few changes to get the desired functionality. First, we'll go to the CreateEditPost.cs file in the CommunityServerForums project. Think of this as the code-behind for the page where users go to add a new post. Here we'll add our AJAX-enabled function.

//C#  [Ajax.AjaxMethod()]  public static ArrayList Search(string search)  {   SearchQuery query = new SearchQuery();   query.PageIndex = 0; //get the first 10 results   query.PageSize = 10;   query.UserID = Users.GetUser().UserID;   query.SearchTerms = search;   return new ForumSearch().GetSearchResults(query).Posts;  }  

We are able to leverage the search functionality already built into CommunityServer and simply have our function wrap around it. As always, the type must be registered with Ajax.NET. We'll do this in the InitializeSkin function of the same file (think of it as Page_Load).

//C#  Ajax.Utility.RegisterTypeForAjax(typeof(CreateEditPost));  

Before we can jump into JavaScript, we need to make one final server-side change. Custom classes returned to Ajax.NET, such as the ForumPost contained in the ArrayList we are returning, must be marked with the Serializable attribute. All we do is go into the Components/ForumPost.cs file of the CommunityServerForums project, and add the attribute.

//C#  [Serializable]  public class ForumPost : Post  {   ...  }  

At the presentation side, we need only modify Themes/default/Skins/View-EditCreatePost.cs in the CommunityServerWeb project. First, we'll hook into the onBlur event of the subject's textbox.

<asp:textbox onBlur="Search(this.value);"                id="PostSubject" runat="server" ... />  

Next, we write the JavaScript Search method so that it calls our server-side Search.

var oldValue = '';  function Search(value)  {     //don't search again for something we just searched    //would happen if the user is tabbing back and forth    if (value != oldValue)    {     CreateEditPost.Search(value, Search_CallBack);     oldValue = value;    }  }  

Finally, all that's left is to handle the response. Since the previous example showed a slightly more elegant way to display results in a table, we'll merely create some dynamic HTML and stick it into a fictitious DIV.

function Search_CallBack(response)  {   //since the search functionality automatically redirects if there    //are no results, we can't rely on response.error     var results = response.value;   //if we didn't get a result   if (results == null)   {    return;   }     //a div that we'll use to put our results   var someDiv = document.getElementById("someDiv");     var html = "";   for (var i = 0; i < results.length; ++i)   {    var result = results[i];    html += "<a target=_blank href='" + result.PostID    html += "/ShowPost.aspx'>";       html += result.Subject;    html += "</a><br />"   }   someDiv.innerHTML = html;  }  

By making minor modifications to three files (plus the web.config for configuration) of the CommunityServer application, we were able to add some pretty nifty functionality. However, do be cautious about simply adding AJAX-enabled functionality to an existing application. The preexisting ForumSearch class doing the actual search might not have been designed for the type of use we've introduced. Our code will likely result in a number of extra searches being performed, and the impact could be significant.

AJAX and You

How and where AJAX fits into your applications, and whether they already exist or not, is going to be very situational. Although we've seen how easy it is to create AJAX-enabled solutions with Ajax.NET, there are other considerations to take into account. A serious concern is the impact on the overall architecture and maintainability of your application. AJAX can further blur the line between your system's layers, notably the presentation, presentation logic, and business layers. This isn't an issue with AJAX itself, but rather with how you'll make use of it. As long as you are aware of how easy it might be to cause some bleeding between layers, and only do so calculatingly, all should be good.

Will an application that uses AJAX be harder to maintain? The answer largely depends on how much JavaScript you are already using, and how good you are at organizing and maintaining it. Many developers feel that JavaScript is harder to write, test, and debug (not because of JavaScript itself, but because of tool support and developer knowledge). If you are currently implementing a linked drop-down list using JavaScript, and switch to AJAX, your code will probably get easier to maintain (Ajax.NET's support for .NET types and arrays is a big reason for this). However, if you are going the post-back route, you'll now be introducing a whole new language to your application (JavaScript), and you'll have to deal with having some data not participating in ViewState (as we saw in the button click event).

Another consideration is the impact AJAX will have on the usability of your Web site. Even though the ultimate gift of AJAX is to create more responsive interfaces, developers should keep two things in mind. First, and most obviously, AJAX is dependent on JavaScript. We all know that some users disable JavaScript, and that some standards (such as the Canadian government Common Look and Feel [think Canada's 508]) require that Web sites work with or without JavaScript. So, you shouldn't assume that the AJAX functionality is working, and you should have your application fall back to more normal Web processing if it isn't available. Second, AJAX applications might be unfamiliar (even if it's superior) to the way users are used to using applications. An example of this is that a page that does various things through AJAX might not behave with the Back button, Favorites menu, and other browser features in the way a user thinks it should.



If you are searching life partner. your searching end with kpmarriage.com. now kpmarriage.com offer free matrimonial website which offer free message, free chat, free view contact information. so register here : kpmarriage.com- Free matrimonial website

0 comments:

Post a Comment