Using AJAX with ASP.NET (Part 1) Introduction ASP.NET page processing model is post back based. That means in order to handle any server side event you must post the form back to the server. This event driven model is certainly powerful and rich but it has drawbacks of its own. Now a days many browsers support client side JavaScript and DHTML. AJAX model is all about making intelligent use of browser's capabilities to give better user experience. In Part 1 of this series I will explain what AJAX is with a simple example. What is AJAX? AJAX stands for Asynchronous JavaScript And XML. AJAX by no way is a new technology. The parts of AJAX i.e. HTML, XML, DOM, XMLHTTP and JavaScript are being used for years. AJAX refers to use of these individual pieces together. Imagine a web form with a TextBox, a Label and a Button. The TextBox is supposed to accept a CustomerID and on the click of the button you are supposed to retrieve the total orders placed by the customer and display in the Label. How will you do it in normal way? You will write Click event handler for the Button and inside the event handler you will write all the code to retrieve the total orders depending on the CustomerID specified in the TextBox. That means for each and every CustomerID you will cause a post back to the server. Think what will happen if your page contains lots of other controls or heavy images. Naturally the overall network traffic will be much more resulting in a poor performance. AJAX can solve such problems. Using AJAX you don't cause frequent post backs to the server. Instead you give requests to server side resources (web forms for example) from the client browser itself. Once the request returns the data you update the controls accordingly. On one hand AJAX can give much better user experience and performance but on the other hand you also need to worry about things such as browsers not supporting JavaScript and cross browser JavaScript. Simple Example To get a feel of how AJAX works we are going to develop a simple example. Our example consists of two web forms. One will have a Button and a Label. This web form will call the other web form via AJAX. The other web form will not have any UI as it is not directly displayed to the user. It will simply return a string "hello world" in it's Page_Load method. This string is displayed in the previous form when user clicks on the Button.
•
Create a new Web Application in VS.NET
•
Add two web forms to it - AJAXMain.aspx and HelloWorld.aspx
•
Drag and drop a Button and a Label on AJAXMain.aspx
•
Write following code in the Page_Load event handler of AJAXMain.aspx Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Button2.Attributes.Add("OnClick", "return GetDataViaAJAX();") If Page.IsPostBack Then Label1.Text = "Server Date Time :" & DateTime.Now End If End Sub
•
Here, we added client side OnClick event handler to the Button
•
The GetDataViaAJAX() function is a client side JavaScript function that does the AJAX trick for us.
•
We then set Label's text to date time stamp. This value will never be displayed because in AJAX model post back is not happening. We are putting this code just to prove this point.
•
In the .aspx file add following JavaScript function to the section of the page
var obj; function GetDataViaAJAX() { try { obj = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try { obj = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e1) { obj = null; } } if(obj!=null) { obj.onreadystatechange = ProcessResponse; obj.open("GET", "http://localhost/AJAXDemo/helloworld.aspx", true); obj.send(null); } return false; }
•
Here, we created an instance of XMLHTTP class. This class is the key to the overall AJAX model. It is this class that makes asynchronous requests to the server resources. This class is a part of MSXML parser that ships with IE.
•
We have used try...catch blocks to take care of version differences between various versions of MSXML.
•
Once we create the instance of XMLHTTP we set its onreadystatechange property to another function called ProcessResponse.
•
The ProcessResponse function is supposed to process the return data from the page being requested.
•
We then call open() method of XMLHTTP and pass the request type (GET or POST), the url to be accessed and whether the request is to processed asynchronously or no.
•
Finally we call send() method of XMLHTTP class. In case of POST requests you may have some content which you can pass as parameter to the send() method. Since ours is a GET request we pass it as null.
•
Add following function after the above function
function ProcessResponse() { if(obj.readyState == 4) { if(obj.status == 200) { var retval=obj.responseText; alert(retval); } else { alert("Error retrieving data!" ); } } }
•
Here, we created a function called ProcessResponse which will be called when the helloworld.aspx returns the response.
•
Inside we check readyState property. The value of 4 indicates that the request is complete.
•
We further check the status code of the request. Status code of 200 indicates ok.
•
Finally we get the data by calling responseText method of XMLHTTP. There is one more property called responseXML that is used with XML data.
•
Now, add following code to the Page_Load event of helloworld.aspx
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Response.Clear() Response.Write("hello world") Response.End() End Sub
•
Here, we first clear any response and send a simple string "hello world" using Response.Write method
•
Finally we call Response.End method to stop the further sending of response.
•
Set the AJAXMain.aspx as the startup page and run the application.
•
Click on the button and you should see a message box with "hello world". Notice that this is happening without any post back.
Code Download The complete source code of above sample is available for download along with the article. Please see top of the article for download link. Summary In this article we saw what AJAX is and how it can help you build more responsive and performance rich web applications. We also saw how to use XMLHTTP component that ships with IE. In the next article I will show you how to deal with bit complex requirement tat includes XML data.
Using AJAX with ASP.NET - Working with DataSet (Part 2) Introduction In the previous article I explained the overall concept of AJAX and how it can save you server post backs. Our last example was a simple "hello world" kind of example. In this part I will explain how to work with data from a DataSet to populate controls. Simple Example In this example we will see how to populate controls with data from DataSet using client side script. We will develop two web forms - DataSupplier.aspx and DataConsumer.aspx. The former populates a DataSet consisting of Orders for a specific Customer. The later consists of a DropDownList and a ListBox. The DropDownList displays list of all the Customers from Customers table. When user selects a specific CustomerID we call DataSupplier.aspx through AJAX and populate the ListBox with the DataSet returned •
.Create a new Web Application in VS.NET
•
Add two web forms to it - DataConsumer.aspx and DataSupplier.aspx
•
Drag and drop a DropDownList and a ListBox on DataConsumer.aspx
•
Write following code DataConsumer.aspx
in
the
Page_Load
event
handler
of
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load If Not IsPostBack Then Dim ds As New DataSet Dim da As New SqlDataAdapter ("select customerid,companyname from customers", "data source=.\vsdotnet;initial catalog=northwind;user id=sa") da.Fill(ds, "custoemrs") DropDownList1.DataSource = ds DropDownList1.DataValueField = "customerid" DropDownList1.DataTextField = "companyname" DropDownList1.DataBind() End If DropDownList1.Attributes.Add("onchange", "return GetDataViaAJAX();") End Sub
•
Here, we populated a DataSet with all the customer records from Customers table. We then bind this DataSet to the DropDownList
•
We also attach a client side event handler for "onchange" event of the DropDownList with GetDataViaAJAX() function.
•
The GetDataViaAJAX() function is a client side JavaScript function that calls DataSupplier.aspx via AJAX.
•
In the .aspx file add following JavaScript function to the section of the page
var obj; function GetDataViaAJAX() { try { obj = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try { obj = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e1) { obj = null; } } if(obj!=null)
{ obj.onreadystatechange = ProcessResponse; obj.open("GET", "http://localhost/AJAXDemo/datasupplier.aspx? customerid=" + document.getElementById("DropDownList1").value, true); obj.send(null); } return false; } }
•
This function is similar to what we wrote in the previous article.
•
Note the part marked in bold. This time we call open() method by passing the URL of datasupplier.aspx and pass the Customer ID selected in the DropDOwnList as a query string parameter.
•
Now let's write ProcessResponse() function that deals with the returned data.
•
Add following function after the above function
function ProcessResponse() { if(obj.readyState == 4) { if(obj.status == 200) { var dsRoot=obj.responseXML.documentElement; var ddlOrders = document.getElementById("ListBox1"); for (var count = ddlOrders.options.length-1; count >-1; count--) { ddlOrders.options[count] = null; } var orders = dsRoot.getElementsByTagName('orderid'); var text; var listItem; for (var count = 0; count < orders.length; count++) { text = (orders[count].textContent || orders[count].innerText || orders[count].text); listItem = new Option(text, text, false, false); ddlOrders.options[ddlOrders.length] = listItem; } } else { alert("Error retrieving data!" ); } } }
•
Here, we created a function called ProcessResponse which will be called when the datasupplier.aspx returns the response.
•
This time we used responseXML property of the XMLHTTP instance because our data is going to be in XML format.
•
The documentElement property returns a reference to the root element of the returned XML
•
First we remove all the items from the ListBox by iterating through it and setting individual item to null
•
Then we retrieve all the OrderIDs by calling getElementsByTagName() method of DOM. This method returns array of all the nodes with tag name of orderid.
•
We then iterate through all the elements of this array and add the Order IDs to the ListBox.
•
Now, add following code to the Page_Load event of datasupplier.aspx
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load If Not IsPostBack Then Dim ds As New DataSet Dim da As New SqlDataAdapter("select orderid from orders where customerid='" & Request.QueryString("customerid") & "'", "data source=.\vsdotnet;initial catalog=northwind;user id=sa") da.Fill(ds, "custoemrs") Response.Clear() Response.ContentType = "text/xml" Response.Write(ds.GetXml()) Response.End() End If End Sub
•
Here, we fill a DataSet with all the Orders for the CustomerID specified in the query string
•
We set content type of the response to "text/xml" as our data is XML.
•
We retrieve the data in XML format by using GetXml() method of DataSet.
•
Finally we send this XML data back to the client using Response.Write() methiod.
•
Set the DataConsumer.aspx as the startup page and run the application.
•
Select any Customer from the DropDownList and you should get the relevant Order IDs.in the ListBox.
Code Download The complete source code of above sample is available for download along with the article. Please see top of the article for download link.
Summary In this article we saw how to work with data from a DataSet. The GetXml() method of DataSet class comes handy for generating XML version of your data instead of manually formatting it. In the next article I will show how the values addeded from client side JavaScript can be accessed on the server.
Using AJAX with ASP.NET-Retrieving values on the server (Part 3) Introduction In the Part 2 of this series we saw how to deal with DataSet and populate controls such as ListBox. Since we are populating the control values from the client side script; they will not be accessible on the server side automatically. In this part we will elaborate on how to access these AJAX populated control values on the server. Simple Example We are going to use the same web forms (DataConsumer.aspx and DataSupplier.aspx) of the previous example. We will add a Button and a Label at the bottom of the DataConsumer.aspx. When you click the Button the Label should display the selected Order ID from the ListBox. Normally, to achieve above requirement you would have written something like this: Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Label3.Text = "Your selected Order ID :" & ListBox1.SelectedValue End Sub
If you try doing the same for our ListBox, you will get an error - "Object reference not set to an instance of object." We have populated the ListBox from client side JavaScript code. These values are not stored in the page ViewState. As a result they are not available on the server in the normal way. There are two ways to resolve the issue: •
Use Request.Form collection directly on the server and get the value
•
Use hidden form field
Let's see each method. Using Request.Form collection You can access all the posted data via Request.Form collection. Classic ASP developers will be familiar with this collection. This collection allows you to specify the key (control name) and access the corresponding value (control value). Following code will make its use clear:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Label3.Text = "Your selected Order ID :" & Request.Form("ListBox1") End Sub
Out ListBox ID is ListBox1 and after post back its value can be accessed as Request.Form("ListBox1"). Using Hidden Field You can also achieve the same effect using a hidden form field. You need to add a Hidden HTML element from the HTML t ab of the toolbox. Right click on it and select "Run as server control". This will add runat="server" attribute to the HTML element and we will be able to access it on the server. Now add following line at the end of Page_Load event handler. Button1.Attributes.Add("OnClick", "return SetValue();")
Here, we are attaching a client side function called SetValue() to the OnClick client side event of the Button control. The SetValue() function looks as follows: function SetValue() { var selectedvalue=document.getElementById("ListBox1").value; document.getElementById("Hidden1").value=selectedvalue; return true; }
This function simply retrieves the selected value from the ListBox and assign it to the hidden form field. You can access this value on the server as shown below: Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Label3.Text = "Your selected Order ID :" & Hidden1.Value End Sub
Here, we simple used Value property of hidden form field to retrieve the value we set via javascript. Code Download The complete source code of above sample is available for download along with the article. Please see top of the article for download link. Summary With AJAX you populate controls via client side JavaScript. These values are not persisted in the control's ViewState. As a result you can retrieve it back on the server in normal way. You can either use Request.Form collection or hidden form field to grab this value.
Using AJAX with ASP.NET - Calling ASP.NET Web Service (Part 4) Introduction In Part 2 we discussed how to work with DataSet returned by the web form. The disadvantage of using web forms as data suppliers is that one web form typically returns only one DataSet. You need to put all the code in the Page_Load. It would be much more organized and useful if you can call Web Service using AJAX. You can then neatly organize your code in various web methods and call the required one from the JavaScript code. In this article I will illustrate how web services can be called from client side JavaScript using HTTP POST method. Simple Example As an example we will first build a web service called Service1.asmx and add the following web method to it: <WebMethod()> _ Public Function GetOrders(ByVal custid As String) As DataSet Dim ds As New DataSet Dim da As New SqlDataAdapter ("select orderid from orders where customerid='" & custid & "'", "data source=.\vsdotnet; initial catalog=northwind;user id=sa") da.Fill(ds, "custoemrs") Return ds End Function
This web method accepts the Customer ID and returns a DataSet containing the relevant OrderIDs. We are going to use the same web form that we created in Part 2. The web form has a DropDownList and a ListBox. The DropDownList displays list of all the Customers from Customers table. When user selects a specific CustomerID we call the web method through AJAX and populate the ListBox with the DataSet returned. Below is the client side JavaScript code that you need to place in the section of the web form: var obj; function GetDataViaAJAX() { try { obj = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try
{ obj = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e1) { obj = null; } } if(obj!=null) { obj.onreadystatechange = ProcessResponse; obj.open("POST", "http://localhost/Service1.asmx/GetOrders"); obj.setRequestHeader("Host","localhost"); obj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); obj.setRequestHeader("Content-Length","12"); obj.send("custid=" + document.getElementById("DropDownList1").value); } return false; } function ProcessResponse() { if(obj.readyState == 4) { if(obj.status == 200) { var dsRoot=obj.responseXML.documentElement; var ddlOrders = document.getElementById("ListBox1"); for (var count = ddlOrders.options.length-1; count >-1; count--) { ddlOrders.options[count] = null; } var orders = dsRoot.getElementsByTagName('orderid'); var text; var listItem; for (var count = 0; count < orders.length; count++) { text = (orders[count].textContent || orders[count].innerText || orders[count].text); listItem = new Option(text, text, false, false); ddlOrders.options[ddlOrders.length] = listItem; } } else { alert("Error retrieving data!" + obj.status); } } } function SetValue() { var selectedvalue=document.getElementById("ListBox1").value; document.getElementById("Hidden1").value=selectedvalue; return true; }
The above code is almost the same as in Part 2 except the one marked in bold. Here, we will be sending a POST request to the web service. You also need to send HTTP headers using setRequestHeaders() method. We are hardcoding the content length header but you may calculate it depending on your actual content. This step is necessary while calling a web service via POST method. Note the URL of the web service. The web method that you want to call is included after the web service file name. This is how you call a web method via POST method. When you call a web service using POST method the web method parameters need to be sent as request body. Hence we created key-value pair custid=<selected_cust_id>. Code Download The complete source code of above sample is available for download along with the article. Please see top of the article for download link. Summary Using AJAX one can call any web resource including web forms and web services. Using web forms, though simple, makes your code bit unorganized and you may end having many web forms supplying data to various AJAX functions. Web service on the other hand is a nice way to keep all your data supplier functions together. It also makes maintaining the application easy.
Client Callbacks in ASP.NET 2.0 Introduction Over the last few weeks I have been writing about AJAX and ASP.NET (Part 1, Part 2, Part 3 and Part 4). AJAX model reduces server post backs using client script and XMLHTTP. Now that ASP.NET 2.0 is released, let's see what it has to offer to do the same thing. ASP.NET 2.0 allows you to execute server side code from your client browser via a feature called as "Client Callback". In this article I will explain what Client Callbacks are and how to program them in your web forms. What are Client Callbacks? The default page processing model in ASP.NET is post back oriented. That means when you click a post back causing control (such as button) your form gets submitted back to the server. Server side events are raised and the entire page output is sent back to the client. This can cause significant network overhead. The Client Callback feature allows you execute code from a web form without posting the form back to the server. You just retrieve the required value and update a small part of the overall UI. This way you save the amount of data passed over the network. It also improves the user experience because of quicker response time.
Classes and JavaScript functions involved The main used while working with client callbacks is ClientScriptManager. This class is used to manage client side script emitted by your page. The Page.ClientScript property returns an instance of this class for you which is attached to the current web form by ASP.NET framework. In a typical usage you are required to write three JavaScript functions to implement client callbacks: •
One that calls the server (CallServer in the example below)
•
One that calls the above function when you click on a post back causing control (SendCustomerID in the example below)
•
One that receives the data returned (ReceiveServerData in the example below)
when
the
call
is
over
As an example we will develop a web form with a DropDownList and a ListBox. The DropDownList is filled with CustomerIDs from Customers table of Northwind database. The ListBox displays the OrderIDs for a selected customer. Registering the client side functions with the page framework In order to register the above functions with the page framework you need to write the following code in the Page_Load event: ClientScriptManager m = Page.ClientScript; string str = m.GetCallbackEventReference (this, "args", "ReceiveServerData", "'this is context from server'"); string strCallback = "function CallServer(args,context) {" + str + ";alert(context);}"; Page.ClientScript.RegisterClientScriptBlock (this.GetType(), "CallServer", strCallback, true);
Here, •
We got the reference of ClientScriptManager instance associated with the current page using Page.ClientScript property
•
We then called GetCallbackEventReference method. This method generates the required markup to call the server from the client. You need to pass name of the client function parameter that acts as arguments, Name of the client function that receives the server data and a context string.
•
The GetCallbackEventReference function returns the required script as a string. You need to wrap this string in a client side function (CallServer in above example).
•
Finally you need to emit this function using RegisterClientScriptBlock
method You also need to write a client side function that actually calls the CallServer function when user changes CustomerID from the DropDownList. You do this using Attributes collection. DropDownList1.Attributes.Add("onchange", "return SendCustomerID();");
Here, we attach a client side function called SendCustomerID to the client side OnChange event of the DropDownList. Implementing ICallbackEventHandler interface In addition to registering the client side functions, you also need to implement an interface called ICallbackEventHandler on your web form class. This interface contains two methods - RaiseCallbackEvent and GetCallbackResult. The former gets called when the client calls the server and the later is called when the server returns the data back to the client. The following code illustrates how these functions can be used. public partial class _Default : System.Web.UI.Page,ICallbackEventHandler { public string GetCallbackResult() { return str; } public void RaiseCallbackEvent(string eventArgument) { SqlDataAdapter da = new SqlDataAdapter ("select orderid from orders where customerid='" + eventArgument + "'", @"data source=.\sqlexpress;initial catalog=northwind; integrated security=True"); DataSet ds=new DataSet(); da.Fill(ds); str=ds.GetXml(); }
In the RaiseCallbackEvent method we simply filled a DataSet with records matching the selected CustomerID and assign the XML representation of it to a string variable str. This variable is returned to the client in the GetCallbackResult method. Client Side Functions The client side functions that you need to write are shown below: function SendCustomerID() { var id=document.getElementById("DropDownList1").value; CallServer(id,"This is context from client"); return false; }
function ReceiveServerData(args, context) { alert(context); var obj = new ActiveXObject("MsXml2.DOMDocument"); obj.loadXML(args); var dsRoot=obj.documentElement; var ddlOrders = document.getElementById("ListBox1"); for (var count = ddlOrders.options.length-1; count >-1; count--) { ddlOrders.options[count] = null; } var orders = dsRoot.getElementsByTagName('orderid'); var text; var listItem; for (var count = 0; count < orders.length; count++) { text = (orders[count].textContent || orders[count].innerText || orders[count].text); listItem = new Option(text, text, false, false); ddlOrders.options[ddlOrders.length] = listItem; } }
In the SendCustomerID function we get the selected CustomerID and invoke the CallServer function with it. In the ReceiveServerData function we receive the XML representation of DataSet (that we returned from GetCallbackResult method. We then populate the ListBox from that data. Download The complete source code of the above example is available for download. Please click on the Download Source Code button at the top of this article. Summary ASP.NET 2.0 client callback feature allows you to call server side code without making any post back. This reduces the network traffic and results in a quicker response time. •
Write it in a .js file and use it in the SRC attribute of SCRIPT tag
•
Manually emit the entire JavaScript code in the OnPreRender overridden method of the control
In our example I will use the second approach. Here is the client side function: function ValidateCreditCard(val) { var cardNumber=
document.getElementById(val.controltovalidate).value; var cardType=val.cardtype; var isValid = false; var ccCheckRegExp = /[^\d ]/; isValid = !ccCheckRegExp.test(cardNumber); if (isValid) { var cardNumbersOnly = cardNumber.replace(/ /g,""); var cardNumberLength = cardNumbersOnly.length; var lengthIsValid = false; var prefixIsValid = false; var prefixRegExp; switch(cardType) { case "MASTERCARD": lengthIsValid = (cardNumberLength == 16); prefixRegExp = /^5[1-5]/; break; case "VISA": lengthIsValid = (cardNumberLength == 16 || cardNumberLength == 13); prefixRegExp = /^4/; break; case "AMERICANEXPRESS": lengthIsValid = (cardNumberLength == 15); prefixRegExp = /^3(4|7)/; break; default: prefixRegExp = /^$/; alert("Card type not found"); } prefixIsValid = prefixRegExp.test(cardNumbersOnly); isValid = prefixIsValid && lengthIsValid; } if (isValid) { var numberProduct; var numberProductDigitIndex; var checkSumTotal = 0; for (digitCounter = cardNumberLength - 1; digitCounter >= 0; digitCounter--) { checkSumTotal += parseInt (cardNumbersOnly.charAt(digitCounter)); digitCounter--; numberProduct = String((cardNumbersOnly.charAt(digitCounter) * 2)); for (var productDigitCounter = 0;
productDigitCounter < numberProduct.length; productDigitCounter++) { checkSumTotal += parseInt(numberProduct.charAt(productDigitCounter)); } } isValid = (checkSumTotal % 10 == 0); } return isValid; }
We will not discuss this function in detail here but it should be fairly easy to follow if you have followed the server side code. Note how the parameter val of this function provides access to the emitted attributes in object based manner. That's it. Now, compile the control project and you are ready to use it on any of your web forms. Developing a test web form In order to test the control create a new ASP.NET web application and add a markup like this to its .aspx file: <%@ Register TagPrefix="cc1" Namespace="CreditCardValidatorVB" Assembly="CreditCardValidatorVB" %> ... ... <script src="ccvalidation.js"> This is error text
Note that I have put the client validation function in a file called ccvalidation.js. If you set EnableClientScript property to True then the validation will be performed on the server side else post back will happen and server side validation will occur. Now run the test web form and see your control in action! Summary Credit card validation is a commonly required on almost all the e-commerce sites. Using ASP.NET validation control framework you can develop your own validation control that validates the credit card on client side as well as on the server side.