DCSIMG
September 2008 - Posts - Pini Dayan

Pini Dayan

The best thing about a boolean is even if you are wrong, you are only off by a bit.

September 2008 - Posts

Microsoft Ajax, from the bottom up - part 2.

In the previous post, i showed a simple ajax sample using the old traditional way.

Today we are going to improve our sample using JSON.

Before i'll explain what JSON is, lets do a little briefing about JavaScript. In javascript we can write object using this syntax:

<script> 
        var oEmployeeObj = new Object();
        oEmployeeObj.Name = "Yaniv";        
        oEmployeeObj.LastName = "Cohen";        
        oEmployeeObj.Age = "30"; 
        oEmployeeObj.Hobbies = new Array();
        oEmployeeObj.Hobbies[0] = "Playing soccer";      
        oEmployeeObj.Hobbies[1] = "C# 3.0"; 
        alert(oEmployeeObj.Hobbies[1]);
      </script>

As you can see , this is a simple object, representing an employee with some properties and one of them is an array of hobbies.This is by no means a way for creating a template object such in C# where we can instance it every time with a new values for our class member. Actually we can build templates of objects in JavaScript using the javascript prototyping (But this is beyond the scope of this post).

But as it turns out , you can write objects in a different syntax, This is exactly equivalent to the previous employee object.

 <script> 
       var oEmployeeObj = {    "Name":"Yaniv","LastName":"Cohen","Age":"30",
                                "Hobbies"  : ["Playing soccer","C# 3.0"]
                          }
                            
       
      alert(oEmployeeObj.Hobbies[1]);
 </script>
This is a subset of the language that allows this javascript syntax.
JSON 

JSON is Javascript object notation, which means it can be used as a data interchange
format. It is basically a way to define structures and it can use a a format for exchanging
data between Async calls when using ajax in an asp.net applications.
As we will see in later posts , it turns out that even asp.net ajax framework uses JSON as the default serializer.

This means that instead of telling our server to return an xml string when we use ajax to do it different:
1. We can return a JSON string.
2. We can still return an xml string but use parser (which can be downloaded from the web) to convert this xml into a  
   JSNO string.
3. We can even use javascript eval function to take this JSON string and convert it to an object. In this case
   We have a security issue since a malicious code can be injected to this string.

So lets see an example, which is an improvement for the previous one:

1. Here the aspx file code: (nothing new here)

<div dir=rtl>    
   <asp:DropDownList runat=server ID="ddlEmployees" onchange="CheckIfTeacher();">
       <asp:ListItem Text="pini" Value="pini"></asp:ListItem>
       <asp:ListItem Text="zohar" Value="zohar"></asp:ListItem>
       <asp:ListItem Text="eran" Value="eran"></asp:ListItem>    
   </asp:DropDownList>
       
   <br />
   <asp:Label runat=server ID="lblDetails" ></asp:Label><br />                            
   </div>

2.Here the js code:

var oXmlhttp;
       function CheckIfTeacher() 
       {
           //Get the selected name
           var ddlObj = document.getElementById("<%= ddlEmployees.ClientID %>");
           var indexSelected = ddlObj.selectedIndex;
           var sName = ddlObj.options[indexSelected].value;

           //Get details for the given user
           oXmlhttp = new ActiveXObject("Microsoft.XMLHTTP");                        
           var sURL = "http://localhost/AjaxTest/Ajax/GetTeacherDetails.aspx?sName=" + sName;
           oXmlhttp.open("GET", sURL, true);
           oXmlhttp.onreadystatechange = GotAnswer;
           oXmlhttp.send(null);           
       }

       function GotAnswer() 
       {
           // if xmlhttp shows "loaded"
           if (oXmlhttp.readyState == 4) 
           {              
               //if "OK"
               if (oXmlhttp.status == 200)
                {
                   var result = oXmlhttp.responseText;                    
                   
                   //Parse this xml                                        
                   var lblObj = document.getElementById("<%=lblDetails.ClientID %>");    
                   
                   var oJSONObject = xml2json.parser(result);                                                             
                   
                   //alert(oJSONObject);
                   //alert(xml2json.show_json_structure(oJSONObject));
                   
                   var sName = oJSONObject.userdata.name;                            
                   var sAge =  oJSONObject.userdata.age;              
                   var sHobbies = oJSONObject.userdata.hobbies;                   
                 
                   lblObj.innerHTML = "Name:"  + sName + " Age=" + sAge + " Hobbies:" + sHobbies;                    
               }
               else
                {
                   alert("Problem retrieving XML data");
                }
           }
       }    
   </script>

3. And here are the refrences to the JS files that uses the xml2json.parser(result) which converts an xml string into a JSON object:

<script language=javascript src="../Javascript/xml2json.js"></script>
The js file can be loaded from http://www.thomasfrank.se/xml_to_json.html
 
As you can see this the returning string now can be easily parsed using a javascript syntax instead of parsing an xml document which can be a pain in javascript.
 
 

UpdatePanel control, one more limitation

As you probably know by using the UpdatePanel control within the asp.net AJAX framework is that it has a major drawback, it's inefficiency. That's because in every Async call  the entire content of the page is being sent to the server. Imagine that you have a big or huge ViewState hidden field on this page........

 

Another limitation for the UpdatePanel i discovered yesterday is its disability to initiate several Async calls to the server at once.

As it turns out, If you have several UpdatePanel controls on a single page and you make an Async call to the server using one of them (lets say it takes a few seconds) and then you do the sam with another UpdatePanel  on the page, The first one is aborted by the PageRequestManager object that is responsible for the UpdatePanel.

So for example:

<div>
   <asp:ScriptManager runat=server ID="ScriptManager1"></asp:ScriptManager>
   <asp:UpdatePanel runat=server ID="upPanel1">
       <ContentTemplate>
           Some content....
       </ContentTemplate>
       <Triggers>
       <asp:AsyncPostBackTrigger ControlID="btnSomeButton1" />
       </Triggers>
   </asp:UpdatePanel>
   <asp:Button runat=server ID="btnSomeButton1" Text="Do Async call for panel 1" />
   
   <br />
   
   <asp:UpdatePanel runat=server ID="UpdatePanel2">
       <ContentTemplate>
           Some content....
       </ContentTemplate>
       <Triggers>
       <asp:AsyncPostBackTrigger ControlID="btnSomeButton2" />
       </Triggers>
   </asp:UpdatePanel>
   <asp:Button runat=server ID="btnSomeButton2" Text="Do Async call for panel 2" />
   </div>

If we click the first button and lets say it takes some time for his operation on the server, and immediately click button 2, then the first request will be aborted.

Notes:1.  You don't need  to specify the event name for the triggers since the default event for a button is "Click"

2. You can learn this from the given code:

<asp:Button id="btnCancel" Text="Abort Request" runat=server OnClientClick="CancelUpdate();return false;"/>
    .
    .
    .
    .
    
    <script>
    function CancelUpdate()
    {
        var oPageRequestManagerObj = Sys.WebForms.PageRequestManager.getInstance();
        if(oPageRequestManagerObj!= null && oPageRequestManagerObj.get_isInAsyncPostBack())
        {
            oPageRequestManagerObj.abortPostBack();
        }        
    }
    
    </script>
That you have only a single PageRequestManager manager, so there can be only a single partial page update call at once.
 
Lets look at a full sample to demonstrate this fact, here we have 2 UpdatePanel each containing a textbox to be filled with some 
when the action on the server complete, i intentionally did a the action on the server a bit log.
when you click the first button and then the second button you will notice only the second one occurs.
The cs code: 
public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void btnSomeButton1_Click(object sender, EventArgs e)
    {
        for (int i = 1; i < 1000000000; i++)
        {

        }
        txtText1.Text = "Partial 1 finished";
    }

    protected void btnSomeButton2_Click(object sender, EventArgs e)
    {
        for (int i = 1; i < 1000000000; i++)
        {

        }
        txtText2.Text = "Partial 2 finished";
    }    
}

 

The aspx code:

<asp:ScriptManager runat=server ID="ScriptManager1" EnablePartialRendering=true></asp:ScriptManager>
   <asp:UpdatePanel runat=server ID="upPanel1">
       <ContentTemplate>
           Some content....
           <br />
           <asp:TextBox runat=server ID="txtText1"></asp:TextBox>
       </ContentTemplate>
       <Triggers>
       <asp:AsyncPostBackTrigger ControlID="btnSomeButton1" />
       </Triggers>
   </asp:UpdatePanel>
   <asp:Button runat=server ID="btnSomeButton1" Text="Do Async call for panel 1" OnClick="btnSomeButton1_Click" />
   
   <br />
   
   <asp:UpdatePanel runat=server ID="UpdatePanel2">
       <ContentTemplate>
           Some content....
           <br />
           <asp:TextBox runat=server ID="txtText2"></asp:TextBox>
       </ContentTemplate>
       <Triggers>
       <asp:AsyncPostBackTrigger ControlID="btnSomeButton2" />
       </Triggers>
   </asp:UpdatePanel>
   <asp:Button runat=server ID="btnSomeButton2" Text="Do Async call for panel 2" OnClick="btnSomeButton2_Click" />
   

 

 
 
 

Microsoft Ajax, from the bottom up.

Recently i started using Microsoft  Ajax and found it very exiting, I also decided to find out once and for all what is all the fuss about JSON.

So In these series of posts, i will try to explain Microsoft ajax and what is so great about it.I will start by introducing the old way we did ajax first, Improve my sample with JSON and then go over ms ajax.

In the traditional web application, the interaction between the user and the server is something like this:

The user opens some url and access a web application

Some page events occur and the page is first renders from the server.

The user then clicks some links or buttons (which need to get some data that is stored some where in the server, probably a DB server)

The user waits while the server get what ever he was told to do and re-renders the entire page.

The user is waiting for this re-rendering every time this postback happens.

So what if we could let the user continue his work, or at least release him from the tedious and slow re-rendering. This is where ajax comes in.

So what is ajax: ajax stands for Asynchronous JavaScript and XML, lets try to explain in a simpler words:
Ajax is basically a way for writing better web applications.
And when i say better web applications i mean applications that are less slow and less tedious.
Ajax is our way for going back to the server without the user knowing it, and without the page doing a submit ,But actually going "silently" to the server and bringing the data. After this data was brought to the page we can simply change the UI of our page in the client side (e.g JavaScript)

And what we gain from building an ajax driven web application is a less slow and tedious application that needs less time and the page in a very quicker way shows us what ever he we told it to do

For instance try the google map web site and check out how cool is it to click some buttons or scroll some scorlles in the page without having to see the entire page renders itself all over again each time we press some button.

So lets start up by building a simple web page that does ajax the old fashion way:
Out sample will ask the user to select a user name from a select box, and according to his name we will show into the label some details about this person. 

In the old days without ajax we set the AutoPostBack of the DropDownList to true, and in the event handler of the selected index change event we checked this user criteria and set the label on the server side.
This is obviously a burden for a user waiting for this post back to end.

Lets show the "old" ajax way, To do so we will do the following:
1. Open vs.net 2005/2008 and create a new web site/web application containing a single web page : SimpleAjaxSample.aspx .Our solution will something like this:

1

2. Add the following code to the aspx file to create a simple drop down list and to handle its onchange event:

<body>
    <form id="form1" runat="server">
    <div dir="rtl">    
    <asp:DropDownList runat="server" ID="ddlEmployees" 
                                onchange="CheckIfTeacher();">
        <asp:ListItem Text="pini" Value="pini"></asp:ListItem>
        <asp:ListItem Text="zohar" Value="zohar"></asp:ListItem>
        <asp:ListItem Text="eran" Value="eran"></asp:ListItem>    
    </asp:DropDownList>
        
    <br />
    <asp:Label runat="server" ID="lblDetails" ></asp:Label><br />                            
    </div>
    </form>
</body>

3. Add the following code to the aspx file in the head section to handle the onchange event using BLOCKED SCRIPT

<script>
       var oXmlhttp;
       function CheckIfTeacher() 
       {
           //Get the selected name
           var ddlObj = 
               document.getElementById("<%= ddlEmployees.ClientID %>");
           var indexSelected = ddlObj.selectedIndex;
           var sName = ddlObj.options[indexSelected].value;

           //Get details for the given user
           oXmlhttp = new ActiveXObject("Microsoft.XMLHTTP");                        
           var sURL = 
           "http://localhost/AjaxTest/GetTeacherDetails.aspx?sName=" + sName;
           oXmlhttp.open("GET", sURL, true);
           oXmlhttp.onreadystatechange = GotAnswer;
           oXmlhttp.send(null);           
       }

       function GotAnswer() 
       {
           // if xmlhttp shows "loaded"
           if (oXmlhttp.readyState == 4) 
           {              
               //if "OK"
               if (oXmlhttp.status == 200)
                {
                   var result = oXmlhttp.responseText;
                   
                   //Parse this xml                                        
                   var lblObj = 
                    document.getElementById("<%=lblDetails.ClientID %>");    
                   var oXML = new ActiveXObject("Microsoft.XMLDOM");      
                   oXML.async = false;
                   oXML.loadXML(result);                         
                   
                   var sName = oXML.selectSingleNode("//UserData/Name").text;                                         
                   var sAge = oXML.selectSingleNode("//UserData/Age").text;                    
                   var sHobbies = oXML.selectSingleNode("//UserData/Hobbies").text;                    
                 
                   lblObj.innerHTML =
                      "Name:"  + sName + " Age=" + sAge + " Hobbies:" + sHobbies;                    
               }
               else
                {
                   alert("Problem retrieving XML data");
               }
           }
       }    
   </script>
   

Notes:

  •  The method CheckIfTeacher gets the name from the select box
  • creates a new Microsoft.XMLHTTP object to handle the web request we are about to perform.
  • Setting the url to an aspx page and add a querystring paramater to send to it.(This page will be explained soon)
  • define the onreadystatechange property for the Microsoft.XMLHTTP object.This property will define the  callback function that will be activated when the web request has finished.In our sample it is the GotAnswer function
  • The GotAnswer will check if the web request succeeded and if so load the xml result to a client side parser(using Microsoft.XMLDOM).
  • Then parse the result and place it in some label.
  • Note that a better solution that will be cross browsing solution is to check in which browser we are at, such as: 
// code for Mozilla, etc.
if (window.XMLHttpRequest)
{
  oXmlhttp = new XMLHttpRequest()
}
//code for IE
else if(window.ActiveXObject)
{
  oXmlhttp = new ActiveXObject("Microsoft.XMLHTTP")
}    
  • 3. Create a page called GetTeacherDetails.aspx which will simulate our http request call (in real life we will usually use an asmx file)
This page will have no content (so delete everything but the page directive). and his CS will like like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class GetTeacherDetails : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Request["sName"] == null)
        {
            Response.Write("");
        }
        else
        {
            Response.Write(GetDetails(Request["sName"].ToString())
                                                       .ToString());
        }
    }
    private string GetDetails(string sName)
    {
        string sDetails = "<UserData><Name>{0}</Name><Age>{1}
</Age><Hobbies>{2}</Hobbies></UserData>"
; switch (sName) { case "pini": return string.Format(sDetails,"pini","30","Soccer,Dancing"); break; case "zohar": return string.Format(sDetails, "zohar", "25", "Ballete,Tennis"); break; case "eran": return string.Format(sDetails, "eran", "15", "Programming,Dancing"); break; default: return string.Empty; } } }
Well, This is about it.What we got is a page looks like this: 
image 
and when the user pick some option from the select box: 
image 
with no flickers or some heavy waiting.
On the next post we will show how to improve our sample using JSON.