The Client-Side Runtime

A fundamental principle of AJAX is to perform as much processing as possible on the client. This includes rendering, data retrieval, and loading multiple applications throughout the life cycle of the page. Where the life cycle of classic ASP.NET pages could be very short, the page life cycle can be very long with an AJAX application. Your users might not reload the page all day because the data can be constantly refreshed without reloading the page.

More Information

More Information

ASP.NET AJAX 3.5 builds on the JavaScript framework included with ASP.NET 2.0 AJAX Extensions but includes support for browser history (the Back button) through the Sys.Application object and support for dynamic data services with the ADO.NET Entity Framework. It is also integrated with Microsoft Silverlight controls, which can be used to implement the AJAX user interface.

The ASP.NET AJAX client runtime is based on the Microsoft AJAX script library, which is defined in MicrosoftAjax.js. Additional components and special-purpose libraries designed for using the ADO.NET Entity Framework and the Silverlight browser runtime are defined in MicrosoftAjaxDataService.js, SilverlightControl.js, SilverlightMedia.js, and SilverlightPlugin.js. You can view the script library for reference from the installation directory at %ProgramFiles%Microsoft ASP.NET 3.5 ExtensionsMicrosoftAjaxLibrary. These scripts are compiled into the System.Web.Extensions assembly and are included through the ScriptManager control. Each script has an associated .debug script that is used when debugging compilation is set in web.config. The debug scripts are much more readable than the release scripts and provide support for JavaScript IntelliSense through Microsoft Visual Studio 2008.

More Information

More Information

Microsoft’s ASP.NET AJAX client-side library is separate from ASP.NET and may be manually added to HTML pages, but in that case you don’t get all the benefits of simple configuration and deployment. For more information about deploying ASP.NET AJAX in non-Windows environments, see http://ajax.asp.net/docs.

To understand the client runtime with ASP.NET AJAX, we’ll first look at script inclusion through the ScriptManager control and then cover basic application management through the JavaScript Sys.Application object. Finally, we’ll look at client-side rendering and asynchronous data operations.

The ASP.NET AJAX ScriptManager Control

The ScriptManager control (System.Web.UI.ScriptManager) is used by ASP.NET AJAX to render script references during initial page rendering. This control is one of the few ASP.NET server controls that you use in a client-centric Web application. When the ScriptManager control is placed on the page, it includes references for the Microsoft AJAX script library. You also use the control to include script references for your own script libraries, including JavaScript proxies for Web services and XML endpoints that enable integration between JavaScript components and back-end services.

Tip

Tip

The ScriptManager control also references the more verbose and readable debug JavaScript files while the application is running in debug mode, which improves your debugging experience. The debug scripts are stricter than the release scripts because you might want to handle exceptions more liberally while in release mode but catch all errors while testing in debug mode.

Page Life Cycle and Sys.Application

The client-side page life cycle is important to know when building ASP.NET AJAX client applications, much as the server-side life cycle was important to know in classic ASP.NET when building server controls. Specifically, you should care most about the life cycle of the ASP.NET AJAX object Sys.Application, which is responsible for the client-side AJAX application runtime. Its methods and events are analogous to the server-side Page and Control life cycle in classic ASP.NET. Because you must load the client-side runtime in the form of script libraries (the ASP.NET AJAX library plus any custom libraries implemented in your code), you need to ensure that these libraries are loaded before calling any dependent scripts. You don’t want to call any client-side functions that aren’t yet loaded within your client-side page load method. To account for this, the Sys.Application object is created when the client page loads, and it manages the application context as the AJAX library loads and fires events during its life cycle. You will often add handlers to these events to initialize your components.

The three main application events to handle are init, load, and navigate. You can also handle the unload and disposing events, although this is less common. To initialize your page with default content, use the init event. The init event is raised after all scripts have been loaded and is used to enable default object creation. The load event is raised after the init event has been handled and the objects in the application have been created and initialized. Ideally, you would initialize your components in init and process initial data loading tasks in the load handler. The navigate event is new in the 3.5 extensions and is used in conjunction with history points, which we’ll talk about later in the book. During the life cycle of the client runtime, you use the navigate event and Sys.Application to manage navigational history as the client loads new application contexts and data streams. Table 1-1 lists the events raised by Sys.Application and describes when and why you should handle these events.

Table 1-1. Sys.Application Life Cycle Events

Event

Description

init

Raised after all initial scripts have been loaded but before custom components and objects have been created. Use the init event to create your custom components.

load

Raised after all the objects in the application have been created and initialized using the init event. Use the load event after creating your components to handle initial data retrieval and other client-side page loading tasks.

unload

Raised when the user navigates off the page, reloads the page, or closes the browser. This event is raised just before disposing of the components in the client application.

disposing

Raised as the application disposes of all resources, just before the end of the browser application instance.

navigate

Raised when the user navigates using the back or forward buttons of the Web browser. Handle this event to implement support of backward and forward navigation in your AJAX application.

To add a handler to Sys.Application events, use the add_ function that corresponds with the event. For example, to add a custom function named onAjaxInit to the application’s init event, use the following command in your script:

Sys.Application.add_init(onAjaxInit);

Likewise, the add_load and add_disposing methods can be used to handle those events:

Sys.Application.add_load(onAjaxLoad);
Sys.Application.add_disposing(onAjaxDisposing);

A complete example of a Hello World Web page using the Sys.Application object is shown in Example 1-1.

Note

Note

In the following examples, I sometimes use inline script in the page to make the sample code readable. For production code I always use separate script files to implement the client runtime. These files are reusable, organized per component, cached by the browser, and loaded as needed during the client lifetime.

Tip

Tip

Code for this book is available online at http://www.microsoft.com/mspress/companion/9780735625914. The code for this chapter is in the file Chapter 1.zip. Where appropriate, I’ll list the file name in the listing heading so that you can locate the sample in the Visual Studio solution. The solutions require Visual Studio 2008 (Express, Professional, or Standard), SQL Server 2005 Express or SQL Server 2008 Express, and the Microsoft .NET Framework 3.5 Service Pack 1.

Example 1-1. The basic page life cycle with ASP.NET AJAX (Web/HelloWorld.aspx).

<%@ Page Language="C#" AutoEventWireup="true" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>ASP.NET AJAX: Fundamentals</title>
</head>
<body>

    <form id="form1" runat="server">
    <asp:ScriptManager ID="AspxScriptManager" runat="server"/>
    <div id="MainContent">
        Loading...
    </div>
    </form>

    <script language="javascript" type="text/javascript">
        function OnAjaxLoad(){
            //   $get(id) is the ASP.NET AJAX equivalent of
            //   document.getElementbyID(id);
            $get('MainContent').innerHTML = 'Hello World';
        }
        function onAjaxInit(){
            $get('MainContent').innerHTML = 'Get ready, here I come!';
        }
        function onAjaxDisposing(){
            alert('Goodbye cruel world!'),
        }

        Sys.Application.add_init(onAjaxInit);
        Sys.Application.add_load(onAjaxLoad);
        Sys.Application.add_disposing(onAjaxDisposing);
    </script>
</body>
</html>

Because the Sys.Application life cycle is event-based, multiple handlers can be added to each event. By using event handlers for component creation and initialization, you can build multiple components and deploy them independently without tying your component to a single page or a single page’s Load method. For example, you might define a group chat component in the JavaScript library chat.js and a stock ticker component in the JavaScript library stockticker.js. Because the application model accepts multiple event handlers for init and load, you can add handlers to the init and load events from both script libraries. As long as components and script libraries use event handlers for initialization, they can be loosely tied to the page. However, a pageLoad method can be used to handle loading a page for page-based scripts and is called after the Sys.Application init and load event handlers are processed.

Tip

Tip

The AJAX Web Form template for Visual Studio 2008 uses the pageLoad method for initialization, which is not a best practice. Avoid using pageLoad. Add event handlers to init and load for more robust, maintainable code.

Web Requests with Sys.Net.WebRequest

One of the main components of the AJAX architecture pattern is client-side data loading with JavaScript Web requests. Handling your own Web requests can be complex—you have to check the browser’s implementation of the XMLHttpRequest object, which might be implemented as a native object or a version of Microsoft’s ActiveX object using MSXML, and you have to handle multiple ready-state changes until the request is complete. It can be quite complex, and the XMLHttpRequest object is not implemented consistently across various browsers. Thankfully, the Microsoft AJAX Library includes the Sys.Net.WebRequest object to perform this task. This JavaScript class is the main component you use for network operations. In its implementation, Sys.Net.WebRequest wraps the XMLHttpRequest object in a cross-browser fashion, but we care only that it can make Web requests for us. That is the beauty of the ASP.NET AJAX library—the gory implementation details are hidden, and you don’t need to worry about browser compatibility. Sys.Net.WebRequest’s interface is similar to the System.Net.WebRequest class in the .NET Framework. With this class you don’t need to handle the implementation details of the AJAX call, you just make a call to a URL and pass in a response handler.

All network calls (Web service methods or simple HTTP requests) made with JavaScript are asynchronous. This means that you have to implement a callback handler whenever you make a Web request or call a Web service. Any time you make an AJAX Web request—either a GET request through Sys.Net.WebRequest or a Web service call using WCF proxies—you need to handle the response with an asynchronous callback.

To perform a Web request using Sys.Net.WebRequest, create a new WebRequest object, set the URL by using the method set_url, add a completed event handler callback method by using add_completed, and call the invoke method. You can also pass in an arbitrary user context object that is passed to the callback method. The user context object can be any object, but typically you will use the user context object to reference a DOM object or include instructions for processing the callback. You can also specify the type of request by using the set_httpVerb method of Sys.Net.WebRequest, usually the verb "GET" or "POST", and a request body by using the set_body method for posting data to the server. The following example creates a simple GET Web request, passing in the ajaxDataCallback event handler (an arbitrarily-named function) as the callback method and setting a user context object through the WebRequest’s set_userContext method. If you do not specify the verb, the "GET" verb is used as the default.

var req = new Sys.Net.WebRequest();
req.set_url('Example.xml'),
req.set_httpVerb('GET'),
var context = { targetElementID : 'MainContent', exampleData : 'Hello, world!'};
req.set_userContext(context);
req.add_completed(ajaxDataCallback);
req.invoke();

To process the response, the callback method accepts the parameters webRequestExecutor and userContext as in the following example. Notice that the user context object is the same object that was passed into the request and is not changed during execution.

function ajaxDataCallback(webRequestExecutor,userContext){
    alert(userContext.exampleData);
}

The webRequestExecutor parameter is an object of the JavaScript type Sys.Net.WebRequestExecutor and contains the server response, including HTTP headers, HTTP status information, and the body of the response. For clarity, I recommend using the term response rather than webRequestExecutor, which will make your code easier to understand. You can also get information about the request by retrieving the request object with the get_webRequest method. In the callback, you can obtain the XML contents of an XML document through the get_xml method, or the text contents of the response through the get_responseData method. If the response type is XML, you get an XML DOM object from get_xml; otherwise get_xml returns null. If the response contains a JSON-formatted string, you can use the get_object method, but get_object will throw an exception if the data is not a JSON-formatted string. Example 1-2 contains a complete code sample for making a simple Web request using the Sys.Net.WebRequest object and an asynchronous callback handler.

Example 1-2. A simple Web request using System.Net.WebRequest (Web/SimpleWebRequest.aspx).

<%@ Page Language="C#" AutoEventWireup="true"  %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>ASP.NET AJAX: Fundamentals: Simple Web Request</title>
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="AspxScriptManager" runat="server"/>
  </form>
  <script language="javascript" type="text/javascript">
        function OnAjaxLoad(){
            var req = new Sys.Net.WebRequest();
            req.set_url('Example.xml'),

            // userContext is an arbitrary object passed to the callback.
            var context = new Object();
            req.set_userContext(context);

            req.add_completed(ajaxDataCallback);
            req.invoke();
        }

        function ajaxDataCallback(response, userContext){
            // Include "debugger" to break into Visual Studio or Firebug:
            debugger;

            // For an XML response, use get_xml()
            var xml = response.get_xml();
            alert(xml);

            // For a text response, use get_responseData()
            var text = response.get_responseData();
            alert(text);


            // If the response is a JSON serialized object, use get_object:
            try{
                // This will work only with a JSON response, not XML:
                var responseObject = response.get_object();
                alert(responseObject);
            }catch(e){ /* This error is expected for non-JSON responses */ }

            // All requests include a status code and description:
            var statusCode = response.get_statusCode();
            var status = response.get_statusText();
            alert(statusCode + ':' + status);
        }

        Sys.Application.add_load(OnAjaxLoad);
    </script>
</body>
</html>

Client-Side Rendering

Because AJAX moves rendering logic to the client, you can load data when you need it, reload portions of your interface when required, and maintain a responsive interface as the user interacts with the page. There are many different ways to render content on the client: you can write HTML through JavaScript, you can use JavaScript templates, or you can use Extensible Stylesheet Language Transformations (XSLT). I’ll use XSLT throughout this book in most client-side controls because it is often the simplest, most flexible rendering technique and also scales well. Alternatively, you can construct your own DOM elements or ASP.NET AJAX client controls through script or even use a combination of all of these techniques.

Note

Note

DOM, or Document Object Model, is the JavaScript model that enables working with an HTML document. A DOM element is any HTML element in the browser’s document and can be accessed through the document JavaScript object.

Throughout this book, you’ll learn how to create data-driven DOM controls by using various rendering methods, but we’ll look at some simple examples here to get you started. Because of the flexibility of transforming standard XML messages with XSLT, this is a favorite technique among AJAX developers. XSLT can be loaded when needed using the ASP.NET AJAX JavaScript class Sys.Net.WebRequest, or it can be stored in a JavaScript variable in simple cases. Because XSLT support isn’t included in the ASP.NET AJAX library, you have to include your own transformation method. A simple method is shown in Example 1-3, which takes a DOM element and applies a transform to the XML.

Example 1-3. A simple XSLT function for JavaScript.

XmlTransform = function (xml, xsl, control){
    if (!window.XSLTProcessor){ // ie
     var content = xml.transformNode(xsl);
     control.innerHTML = content;
  }else{  // MOZZILA
    var processor = new XSLTProcessor();
    processor.importStylesheet(xsl);
    var content = processor.transformToFragment(xml, document);
    control.appendChild(content);
  }
}

Under most circumstances when you use XML with XSLT transforms, you load the XSLT at the same time as you load the XML and you transform it when you have both the XML and XSLT ready, caching the XSLT to use again. You can also load XSLT as the user interacts with the page, which lets you bring in new data-bound AJAX controls. You’ll see examples of wrapping this functionality into DOM-based ASP.NET AJAX controls in Chapter 8, but for now I will simply extend the DOM element with expando properties. Expando properties are arbitrary JavaScript properties that are added to elements at run time through script. In this case, we’ll store the XML and XSLT as properties of the element and perform the transformation when we have the data. Example 1-4 demonstrates this simple technique by loading the data and the XSLT and performing a simple XSLT transform during the client-side page execution in response to a user action. You can do this many times throughout the life cycle of the page to render data as the user requests it or with a client-side timer function. For this simple example, I’ve used static XML files on the server.

Example 1-4. XSLT can be used to render data on the client (Web/SimpleXmlTransform.aspx).

<%@ Page Language="C#" AutoEventWireup="true"  %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>ASP.NET AJAX: Fundamentals: Simple XML Transform</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="AspxScriptManager" runat="server"/>
    <div id="maincontent">
       <a href=javascript:loadXml();>
          Click to load data.
      </a>
    </div>
    </form>
    <script language="javascript" type="text/javascript">
        function loadXml(){
            var context = $get('MainContent'),
            var xmlReq = new Sys.Net.WebRequest();
            xmlReq.set_url('SampleRSS.xml'),
            xmlReq.add_completed(xmlCallback);
            xmlReq.set_userContext(context);
            xmlReq.invoke();

            var xsltReq = new Sys.Net.WebRequest();
            xsltReq.set_url('SampleRSS.xslt'),
            xsltReq.add_completed(xsltCallback);
            xsltReq.set_userContext(context);
            xsltReq.invoke();
        }

        xmlCallback = function(executor, context, args1, args2, args3){
            var control = executor.get_webRequest().get_userContext();
            var xml = executor.get_xml();
            // control.xml is an expando property, storing the xml for latter use
            control.xml = xml;
            if (control.xslt != null)
                XmlTransform(control.xml, control.xslt, control);
        }

        xsltCallback = function(executor, context, args1, args2, args3){
            var control = executor.get_webRequest().get_userContext();
            var xslt = executor.get_xml();
            // control.xslt is an expando property, storing the xslt for latter use
            control.xslt = xslt;
            if (control.xml != null)
                XmlTransform(control.xml, control.xslt, control);
        }
        XmlTransform = function (xml, xsl, control){
            for(var i=0; i< control.childNodes.length; i++){
                control.removeChild(control.childNodes[i]);
            }control.innerHTML = '';
            if (!window.XSLTProcessor){ // ie
             var content = xml.transformNode(xsl);
             control.innerHTML = content;
          }else{  // MOZZILA
            var processor = new XSLTProcessor();
            processor.importStylesheet(xsl);
            var content = processor.transformToFragment(xml, document);
            control.appendChild(content);
          }
        }
    </script>
</body>
</html>

With the Web request in Example 1-4, a simple XSLT file is used to render the page elements. Example 1-5 displays a simple XSLT file for transforming an RSS formatted XML document. You’ll learn more about RSS and other syndication formats in Chapter 3, as syndicated formats are an ideal data scheme for loosely coupled systems.

Example 1-5. A simple XSLT file for rendering RSS items (Web/sampleRSS.xslt).

<xsl:stylesheet
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     version="1.0">
  <xsl:strip-space elements="true"/>
  <xsl:output omit-xml-declaration="yes" method="html" />
  <xsl:template match="/rss/channel">
    <h3>
      <xsl:value-of select="title" disable-output-escaping="yes" />
    </h3>
    <xsl:apply-templates select="item" />
  </xsl:template>
  <xsl:template match="item">
    <div>
      <xsl:value-of select="description" disable-output-escaping="yes"  />
    </div>
  </xsl:template>
</xsl:stylesheet>

In the XSLT in Example 1-5, the rendering logic creates an H3 element for the XPath item /rss/channel and then creates a DIV element for each item in the channel. Example 1-6 defines a simplified RSS document that could be used as the data source for the AJAX component. In Chapter 3 we’ll look at built-in support for RSS generation in the .NET 3.5 Framework.

Example 1-6. A simplified RSS document. RSS is an ideal format for loosely coupled systems.

<rss version="2.0">
  <channel>
    <title>Over-simplified RSS channel</title>
    <item>
      <title>An example RSS item</title>
      <description>
        RSS is an ideal format for loosely coupled systems.
      </description>
    </item>
  </channel>
</rss>

Note

Note

As an alternative to XSLT, you might want to explore JavaScript template frameworks, such as the prototype JavaScript library (http://www.prototypejs.org/api/template). JavaScript templates will be an integrated part of ASP.NET AJAX in future releases and can be downloaded from the ASP.NET CodePlex site (http://codeplex.com/aspnet).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset