Building AJAX Applications with ASP.NET
Overview
The primary purpose of ASP.NET AJAX is making the user’s experience as rich as possible by providing a breakthrough programming environment to developers so that they can code what was impossible or impractical before. Partial page refresh and remote method calls are the key features of an AJAX-powered application. Together, these features enable developers to build mash-up applications, display real-time data, and update the user interface promptly and smoothly. All this is darned cool and effective, but it’s also a little bit confusing when, having recovered from the initial hype, you think it over with a cold and fresh mind.
Where’s the confusion? Partial rendering (covered in Chapter 4, “Partial Page Rendering”) and remote method calls (covered in Chapter 7, “Remote Method Calls with ASP.NET AJAX”) herald two neatly distinct approaches to AJAX. Which one is best, and when? Would one complement the other, and to what extent? What are the costs and benefits of bringing each into an existing application? What kind of work is required on the back end? What if several different user interfaces are expected to work, and what if the AJAX client is just one possible client? As you can see, a number of architectural issues arise as you move past the starting line of AJAX and make your way toward the finish.
What’s the best way to move your application to AJAX? Should you just go with low-level ASP.NET AJAX controls such as the UpdatePanel control, or would you be better off adopting a third-party suite of controls? If you’re building an AJAX application, or adding a new feature, from scratch, how should you design and code the client and server infrastructure?
From a pragmatic standpoint, you should clearly distinguish situations in which you’re just migrating an existing application to AJAX from situations in which you need to design a brand-new system for AJAX. In the former case, ASP.NET AJAX has a number of shortcuts, some of which are really smart to use. In the latter case, you should spend some time developing a deep understanding of the client and server boundaries of your system and be sure to keep the client decoupled from the server. When writing new code using AJAX technology, you can expose server-side capabilities through contract-bound services, and you can consume services using JavaScript proxies. Clearly, creating and using contract-bound services and JavaScript proxies is not necessarily like building a classic ASP.NET application. AJAX is a new paradigm for Web development; it’s not simply a new application programming interface (API) for writing applications.
In this final chapter, I’ll first spend some time illustrating the pros and cons of the AJAX paradigm from an application perspective. Next, I’ll move on to architectural considerations by separating partial rendering from true AJAX architectures. Finally, I’ll take one of the most popular ASP.NET starter kits and update it to an ASP.NET AJAX starter kit.
AJAX in Perspective
AJAX propounds a pattern for applications that is fundamentally centered around the user. The two main approaches to AJAX development are often referred to as AJAX Lite and AJAX Deluxe. These two approaches define the extremes, and most applications fall somewhere in between.
In AJAX Deluxe, you use the set of AJAX features to the fullest. Your final goal is making the application feel nearly identical to a classic desktop Microsoft Windows application. The browser drives the interaction and goes to the server only when it needs to perform operations that require more processing than just JavaScript and collecting input data on the client can do. AJAX Lite, on the other hand, propounds a programming style in which applications look like conventional Web applications with some out-of-band calls sprinkled here and there. AJAX Lite is the ideal approach to migrate and upgrade existing applications; AJAX Deluxe is the philosophy you might want to embrace when you build a new application. However, not all applications fall so neatly into one approach or the other. It’s mostly a matter of costs and time to develop. A “lite” approach doesn’t require a full redesign, but still some subsystems may need a deep refactoring. In this case, you’re just in the middle of the two approaches. Personally, I tend to use the “lite” approach and fall back to more complex AJAX when I have to.
Whatever approach, or combination of approaches, you take, though, it is essential that you understand which logical tasks around the application might take advantage of AJAX and which are the proper architectural choices and design patterns to adopt.
The Benefits of AJAX
In this book, I already mentioned that the term AJAX was coined in 2005, but I didn’t mention why the term was invented. Jesse James Garrett was the first to refer to AJAX in the form we know it today, and he did so in a paper you can read at http://www.adaptivepath.com/publications/essays/archives/000385.php. Jesse argues that the typical responsiveness of desktop applications is out of reach for Web applications. The key factor to the rapid proliferation of Web pages is a sort of common denominator approach: you can reach zillions of people if you don’t pose strict requirements on hardware and software. At the same time, the less you require, the smaller the set of features will be that you can build in pages.
The ultimate goal of AJAX is demonstrating that by using the proper set of (existing) tools, and without loss of simplicity and reach, you can fill (most of) the gap between Web and desktop applications as far as the user interface is concerned.
The last sentence contains three strategic qualifications, I believe, that were key in establishing the goals of the AJAX platform. First, fill the gap between Web and desktop applications. Second, fill most of this gap but not all. Third, fill most of the gap as far as the user interface is concerned. Let’s see how the benefits AJAX provides allow us to target these strategic tenets.
AJAX Is a Real Breakthrough
AJAX is an important achievement for the Web software industry. It makes possible implementing features and functionalities that were impossible or impractical before. AJAX is neither a single technology, nor does it require an external plug-in. AJAX is not akin to Adobe Flash, Adobe Flex, or similar tools, including the Microsoft code-named WPF/E product based on the Windows Presentation Foundation (WPF) API. Rather, AJAX is a recipe of design patterns. You get to use AJAX if you need Web applications that do more things or do things better than technologies that are generally available today.
What are the fields in which AJAX can make a difference? AJAX is mostly concerned about the user interface and a user’s perception of performance. What are examples of areas that can prove AJAX is a breakthrough? Here’s a quick list: displaying live data, monitoring remote tasks, establishing zero page flickering, making smarter use of page real estate, making dynamic changes, and aggregating data from multiple feeds.
The Continuous Feel
In a traditional Web application, users click a button and submit a form. Then they hang out for a few seconds until the page redraws. When all is done, they can finally take a look at the new content. However, if they scrolled down a half page to find a button, they lose their position and have to rescroll. Although this behavior might be acceptable if you turn to a new page, or to a significantly modified version of the same page, it ends up being bothersome if you clicked just to page through grid data or flip through a photo album, only to lose your place when the page refreshes.
With the content-heavy pages of today’s sites, going through a multistep operation such as a registration or an electronic purchase is like being stuck in traffic. In the end, it’s a kind of Web stop-and-go. With AJAX, you have no full-page refreshes. The user experiences continuous interaction with the same page; she sends requests and receives responses, and it all happens smoothly. It’s an easy ride all the way through-as long as nothing bad happens on the server. One thing AJAX can’t do, in fact, is shield you from server failures and server-side scalability or performance issues. The responsibility to provide sound Web-based architectures is still a requirement.
The User’s Experience and User Interface
AJAX is about the user interface and the user’s experience. With AJAX, you can develop (or buy) powerful controls that incorporate out-of-band calls to expand or collapse views, page silently, detect ongoing changes, and refresh portions of the screen. If asked to justify their interest in AJAX, a large share of developers and managers would answer that they want or need to provide attractive and friendly user interfaces to customers. For example, expanding and collapsing views is much smoother with AJAX than without it. This is because ASP.NET AJAX and similar commercially available AJAX frameworks provide built-in objects and a client infrastructure to leverage for just this purpose.
More often than not, though, behind a cool and attractive user interface there’s the need to build a rich user experience-which is somewhat different. A user’s experience is the sum of many factors, including a gorgeous user interface. In addition to a rich set of user interface (UI) gadgets, you find data aggregation, prompt interaction with the server, personalizable graphics, information at the user’s fingertips, and the possibility of building personalized views of pages using drag-and-drop functionality and other facilities. Giving the user a better experience is much easier with AJAX than without.
The Downsides of AJAX
AJAX has a number of positive aspects, but it also has some weaknesses. In the end, choosing AJAX should be the result of a careful and thoughtful process of evaluating the trade-offs. You weigh what’s good and what’s bad and decide the percentage of AJAX you need in your system and, more importantly, how to build it. In general, spicing up Web applications with AJAX is always a good thing. The recipe that works for you, though, might not be that obvious.
Comparing AJAX to Other Rich Clients
AJAX is the paradigm to use for building Web-rich clients. ASP.NET AJAX is a concrete framework you use to apply AJAX to your applications. How does AJAX compare to rich clients such as Windows applications deployed via ClickOnce or WPF browser applications in .NET 3.0? Let’s see.
ClickOnce is a deployment technology that facilitates the deployment of a Windows Forms application. With ClickOnce, installing a Windows application is as easy as deploying a Web application; all that users have to do is click a link in a provided Web page. For administrators, installing or updating an application is only a matter of updating files on a server; there’s no need to touch individual clients. (For further information, visit http://msdn2.microsoft.com/en-us/netframework/aa497348.aspx.)
WPF browser applications are essentially WPF applications that can be hosted inside a browser such as Internet Explorer. These applications are partially trusted and are not given full access to the local computer’s resources. For example, they can’t do any I/O. Although you get the perception of the application running in the browser, it is actually hosted outside the browser process. From a graphical point of view, though, it looks like a real WPF application with animation, media, and advanced graphics.
If ClickOnce and WPF browser applications are new to you, they probably sound very appealing at first glance, and they are compelling technologies. What about the differences between ClickOnce, WPF browser applications, and AJAX? We’re basically talking about apples and oranges. AJAX clients are Web applications. ClickOnce clients are Windows Forms, .NET 2.0 applications with a number of nice features as far as deployment is concerned. Finally, WPF browser applications are .NET 3.0 WPF-powered applications that run in the sandbox within the graphical context of a browser.
All are valid options for adding a smart client to your enterprise systems. But each has its own role and application. AJAX (and ASP.NET AJAX as a concrete platform) is for pure, traditional Web applications.
Note |
A browser plug-in currently codenamed WPF/E might be released by the time you read this. It is a product that, functionally speaking, is really close to Adobe Flash. It provides media capabilities without requiring Windows Media Player, vector graphics, and a rich user interface a là WPF in Web applications on Windows and Mac platforms. Based on XAML files, it comes with built-in controls for data binding and represents the natural way of extending AJAX with rich components fully scriptable through JavaScript. |
Not Ready for Miracles as Yet
ASP.NET AJAX applications certainly let developers do things over the Web they never imagined possible in days past. However, there are still substantial restrictions to what one can achieve with AJAX and Windows applications. Want some examples? Multimedia capabilities, data storage, advanced graphics, hardware control. Plug-ins such as Adobe Flash and the upcoming Microsoft WPF/E can help developers to mitigate some of these limitations. However, a key fact shouldn’t be forgotten or misconstrued: AJAX is not the desktop.
Note |
Realistic AJAX applications do require good JavaScript skills. JavaScript is certainly not the most complex programming language you can learn, but it can be far less trivial than many suspect. More importantly, there’s no alternative to JavaScript for the client portion of an AJAX application. ASP.NET AJAX does a good job of abstracting JavaScript intricacies as much as possible, but it’s still script. And you shouldn’t embark on a big AJAX project without a good working knowledge of JavaScript. |
Continuous Connection
AJAX applications are virtually useless without an Internet connection. In other words, they still follow the classic Web application model and must remain connected to the network to function properly.
Note |
You can still run AJAX applications on the local host, as long as you have a local Web server that hosts the application. Which doesn’t mean that AJAX applications, like regular Web applications, can sometimes work offline. |
The major advantage of AJAX-the ability to connect to the server while bypassing the browser-can become its biggest drawback if abused. AJAX applications that ping servers almost constantly, multiplied by the order of magnitude of clients at peak time, certainly could create a boundary condition that can’t be ignored. Such constant interaction between the browser and server can make an application appear slow or unresponsive.
Applying caching techniques and fine-tuning the frequency of updates might work in most cases, and it could even work in the case of high-demand applications such as stock-trading and live-score applications. This said, AJAX is obviously not a reliable fit for real-time processing and machine control applications.
One-Page Application
The life cycle of an AJAX application is amazingly more akin to that of a traditional desktop application than a classic Web site. In desktop applications, controls collect the user’s input and developers establish a given behavior by handling their events; in AJAX applications, Document Object Model (DOM) elements play exactly the same role. Client event handlers command actions; and often they are actions that are then forwarded to the server. When this happens, though, the page remains up and running while waiting for the response.
When the server responds and the response is downloaded to the client, a request callback updates the DOM accordingly, much like a C# or Microsoft Visual Basic .NET handler updates a desktop-based form.
In light of this, can we consider AJAX applications as one-page applications where the presentation layer just does three-card tricks with user-interface panels? I remember that a similar “potential” problem arose when Microsoft shipped SQL Server 2005 with support for common language runtime (CLR) types. Quite a few developers, more or less seriously, wondered whether they were supposed to abandon database normalization and relational principles to work with one-table, one-column databases.
Of course, AJAX applications will be made of a number of pages linked together through a menu or a tree-view. Within the page, then, related microfunctions will be implemented to increase friendliness and time-to-last-byte, while limiting flickering, scrolling, redraws, and whatever else that can make a user unhappy.
Patterns, Practices, and Services
Building an AJAX application can be challenging. Don’t be fooled by the apparent simplicity of the API involved with it-which includes the UpdatePanel control, Web service calls, Web methods, and so on. It is essential that you see the clear difference between AJAX and ASP.NET AJAX Extensions.
An AJAX application is a Web application architected and built according to AJAX concepts-such as the use of out-of-band calls, direct access to the DOM, regarding the GUI as a dynamically changing element, and the logical separation of the client and server APIs.
An ASP.NET AJAX application is an ASP.NET Web application that leverages the AJAX framework (whether that framework is ASP.NET AJAX or another third-party version). The framework has a client and server infrastructure and abstracts most of the AJAX principles by exposing it through concrete programming tools and techniques. If you opt for ASP.NET AJAX or any other framework, you opt for the AJAX architecture envisioned by the authors of the framework. You just get pragmatic and use whatever comes out of the box.
Despite the fact that building an ASP.NET AJAX application is not a complicated task, architecting one is definitely challenging. Why challenging? Mostly because you have to rethink functionalities and page structure and plan the allocation of functionalities to pages, the exposure of remote services, the format of data being exchanged, the level of coupling between client and server API, and testing.
Although this book is entirely dedicated to ASP.NET AJAX Extensions, it is good to stop and think a moment about common patterns and practices that lie behind AJAX programming. Then it should be easy to recognize many of them in the practical solutions offered by the ASP.NET AJAX Extensions framework.
Services and Web Services
For years, it has been natural for .NET developers to associate Web services with Simple Object Access Protocol (SOAP) and Web Services Description Language (WSDL). Now with ASP.NET AJAX support for ASP.NET Web services, one wonders why on earth the communication between the client and the Web service has to use JSON. And why there’s no use of WSDL, neither explicit nor buried into the folds of the ASP.NET AJAX Extensions framework.
As mentioned in Chapter 7, you should think of the remote API exposed to JavaScript clients as plain services, possibly with a contract. Put in this way, the fact that ASP.NET AJAX lets you use ASP.NET Web services is a mere implementation detail. So you have a service with a contract.
A service with a contract is often referred to as an SOA service, where SOA stands for Service-Oriented Architecture. An SOA service can be like a classic Web service as long as we use SOAP and WSDL to implement it. On the other hand, a classic Web service can be an SOA service as long as it exposes its programming interface via a contract. Be pragmatic, think of functionality as a contract-that is, an interface-and expose it through a service. And try not to care too much about whether it is a Web service or an SOA service. What matters is that it works and can have its contract known and checked.
In the context of AJAX, you should use this principle to build the server-side, client-callable interfaces, as we demonstrated in Chapter 7.
Note |
Services in an SOA environment are resources available as independent services that can be accessed without knowledge of the underlying platform implementation. SOA services are generally available and not targeted to the application in question and only that application. Clearly, services like those described in Chapter 7 and recalled here are specific to just one application. So, in this sense, they’re not SOA at all. Again, be pragmatic: in AJAX all that you need is a service-oriented back end and services that are publicly exposed and have a well-defined contract. |
REST vs. SOAP
Representational State Transfer (REST) is an architectural pattern used in the design of services. REST deals with the way in which you expose and make available to callers the contract of the service. REST is an alternative service implementation mechanism to SOAP.
A SOAP-based SOA or Web service uses a single URL to expose all functionalities and documents the service methods through WSDL or a contract name. Behind the URL, a single handler receives the request, examines the body, and forwards the call to the internal machinery for individual method processing.
A REST SOA or Web service uses distinct URLs to point to distinct functionalities. A REST service documents functionalities through links and HTTP headers. Each operation has a unique URL (plus, optionally, HTTP headers), and this URL is enough for the server-side handler to identify the requested resource and process it.
REST relies on HTTP verbs and URLs to express what operation is requested and on what data. SOAP, instead, relies on WSDL metadata to define the contract and data types. REST is more flexible and promotes decoupling of clients from the server. SOAP through WSDL defines a fixed contract. If this WSDL-based contract is extended or modified, all clients must be notified and adapted. A REST service can start supporting a new feature without the need of notifying all clients. Clients that become aware of this new feature (method) can then easily take advantage of it without changing existing code. And distinct clients can get data in different formats-JSON, XML, RSS, CSV, plain text, and so on-just by examining the HTTP header they use. This is not possible with SOAP, as SOAP is designed to convey all data types as XML, with consumer-specific type conversions taking place on the consumer’s end.
Services consumed by AJAX applications should adhere to REST principles. The term RESTful is commonly used to indicate this.
Note |
REST, SOAP, WSDL, remote procedure call (RPC), and flavors of services are all broad topics where personal opinions and attitudes blur and often generate either crude and sharp statements or long, tedious, semiphilosophical discussions. My advice is that you read as much as possible, but if you reach the point of being confused, stop. When every statement looks the same and you can no longer distinguish clearly where you are or who you are, it’s about time to get pragmatic and start writing code! For your convenience, I’ll recommend a few resources that I’ve found useful to clarify details and doubts. A good book is Ajax and REST Recipes: A Problem-Solution Approach by Christian Gross (Apress, 2006). Keep in mind that this is not an ASP.NET AJAX book; it’s an AJAX book. A couple of good online articles can be found at http://ajaxpatterns.org/RESTful_Service and http://webservices.xml.com/pub/a/ws/2002/02/06/rest.html. |
REST in ASP.NET AJAX
How much REST is in ASP.NET AJAX Extensions? There’s some. All in all, I’d say that the services you can consume from ASP.NET AJAX clients are RPC-style services with some REST principles implemented. Because of the outermost RPC style, you-the programmer endpoint-don’t get many of the benefits of REST in terms of flexibility, loosely coupled client and server, and testing capabilities. These benefits are reserved for the framework for further extensions.
Hold on. This is not necessarily bad news. Once again, be pragmatic and look at what you get in return. ASP.NET AJAX Extensions gives you a familiar programming interface and, with Visual Studio “Orcas,” ad hoc tools for testing. In addition, you can still use little-known classes in the JavaScript Microsoft Client Library for AJAX (discussed in Chapter 2, “The Microsoft Client Library for AJAX”) to prepare and issue a Web request yourself, by modifying URLs and headers to make special requests to RESTful services you might have.
In the end, there’s quite a few REST principles in ASP.NET AJAX even though they’re not patently exposed to developers.
Quick Tour of AJAX Patterns
RESTful services is one of the key AJAX design patterns, but others are defined as well-many of which are silently implemented in the ASP.NET AJAX framework. Let’s take a quick look at some of them in Table 8-1.
Pattern |
Category |
Description |
---|---|---|
RESTful services |
Services |
Provides guidance on the most effective ways of exposing services to AJAX clients, and thereby minimizing the impact on testing, extensibility, client/server coupling, and data formats. |
Cross-domain proxy |
Services |
Provides guidance on how to connect to Web services available remotely outside the boundaries of the current application. |
Page rearrangement Display morphing |
User interface |
Related partterns that summarize the most common techniques to update the page structure and styles of DOM elements. |
HTTP streaming |
User interface |
Provides guidance on how to push data from the server to the client. |
Periodic refresh Progress indicator |
Browser/Server communication |
Related patterns that show common ways to implement periodic refresh of the page and monitor server-side operations. |
Heartbeat Timeout |
Browser/Server communication |
Related patterns that show common ways to determine whether a user is still actively working with an application and give guidance on what to do once you know it. |
On-demand JavaScript |
Performance |
Provides guidance on how to apply lazy-loading to JavaScript files and download them only when required. |
Submission throttling |
Performance |
Provides guidance on how to retain posted data on the client and submit it at fixed intervals. |
Predictive fetch |
Performance |
Provides guidance on how to make the browser more responsive by anticipating user actions and call the server in advance. |
Table 8-1 lists only 10 patterns from a few areas. Each pattern brings with it a number of real-world applications, issues, and scenarios as well as multiple solutions and techniques. Building AJAX applications might not be easy, as I mentioned at the beginning of the chapter, if you want the application to be a deluxe one. Thankfully, a few patterns are taken care of by the ASP.NET AJAX framework and the components in the AJAX Control Toolkit. (See Chapter 5, “The AJAX Control Toolkit.”) But for the most part, deciding on a pattern is left up to you.
For more information, you can visit http://www.ajaxpatterns.org. If you prefer a book, my suggestion is Ajax Patterns and Best Practices by Christian Gross (Apress, 2006).
Revisiting ASP NET Starter Kits
When a new programming paradigm is offered to the community, good and fully fledged examples are essential for a comprehensive understanding of the key facts and to get started on using it. ASP.NET Starter Kits play exactly this role in the context of classic ASP.NET 2.0 programming. You can find a number of them available for download at http://www.asp.net/starterkits.
To illustrate the application of AJAX techniques in a realistic scenario, I’ll take one of them and upgrade some of the pages to ASP.NET AJAX Extensions. The result will be a partial rewriting of the starter kit, with some pages enhanced with partial rendering and remote calls.
The Jobs Site Starter Kit at a Glance
Adding AJAX capabilities to an existing application is seemingly a simple task. You just loop through the pages and add updatable panels where appropriate and where you want to limit page flickering and postbacks. The AJAX Lite approach seems to be lightweight and powerful overall.
As we’ll see in a moment, though, not all that shines is necessarily gold. And not all that sounds easy on paper is as seamless in practice. The main point is the mechanics of the application. Their implementation is relatively cheap, adding AJAX to pages with a simple structure in which server controls generate the markup and event handlers dictate the behavior. In this case, you simply wrap blocks of server controls with updatable panels and go.
Fully templated pages and pages with a dynamically changing structure don’t lend themselves well to AJAX. For example, porting to AJAX a page made of interchangeable templates or panels, which are shown and hidden via code, is challenging. These pages, therefore, already use the Page Rearrangement pattern, but they do it entirely on the server. In these cases, you have two options: either wrap the whole page in a single updatable panel or redesign the page from scratch.
For the purpose of this book, I picked up a relatively boilerplate sample site-the Jobs Site Starter Kit (JSSK). You can get it at http://www.asp.net/downloads/starterkits/default.aspx?tabid=62#jobsite.
Overview of JSSK
JSSK provides a Web platform for job seekers and employers to meet and share respective needs. After registering with the site, job seekers can post and edit their resume, search for job postings, and maintain a list of favorite searches. Registered employers, on the other hand, are allowed to enter a profile of their company, post and edit information about available positions, and search the resume database. Figure 8-1 shows the home page of the site.
Figure 8-1: The home page of a site built using the JSSK template
Completely based on a three-tier architecture, the site employs forms authentication, role-based security, and user profiles. Users and roles are managed through membership and role providers. All pages are based on a master page. The master page defines a left sidebar with a navigation tree and a right sidebar with statistical information that reflects the current state of databases.
Log-in and navigation are managed using the ASP.NET Login and TreeView controls. Data binding is extensively used through the ObjectDataSource control and the site’s middle tier.
Setting up for AJAX
The first change to be made is editing the web.config file to make sure that ASP.NET AJAX Extensions assembly is successfully located and loaded. You can proceed in either of two ways. You can create a new ASP.NET AJAX-enabled Web site and merge the contents of the JSSK web.config file in the site’s configuration. Alternatively, you can edit the JSSK web.config file to support AJAX if you are familiar with the configuration settings ASP.NET AJAX require. Either way, you must ensure that the configuration file contains the following information:
- A link to the system.web.extensions assembly in the section
- Declaration of the new sections specific to ASP.NET AJAX
- In the section, an association that is defined between the asp tag and all controls in the system.web.extensions assembly
- Registration information for the ASMX handler and script module if you want to use remote calls and page methods
- Any scripting settings (compression, caching) that you might need
The latter two points are optional, and the information is required only if ASP.NET AJAX will use the features they refer to. Let’s apply some changes to the JSSK site.
Reducing Page Flickering
The primary benefit of adding AJAX capabilities is reducing page flickering by limiting the number of full postbacks and subsequent page redraws. You conduct a page-by-page analysis and determine which portions of each page can be updated independently. Each partially updatable region will then be wrapped by an UpdatePanel control. To use the UpdatePanel control and any other ASP.NET AJAX controls, though, you need a script manager to be available to the page. For pages based on a master, though, you are better off adding the script manager directly to the master and using the script manager proxy in child pages.
Adding the Script Manager
For a site based on master pages, the first-and to some extent a step you are compelled to take-is adding a script manager to the master. All content pages will take advantage of the services the manager provides and, therefore, in all of them you can take for granted that a script manager exists.
In the master page, you typically give the script manager the configuration that fits the largest number of pages. For example, you commonly don’t need to enable page methods in all pages. Will it really be a problem if you enable page methods globally for all pages? It’s not a problem per se, but in this case all pages will get some script that only a few of them will actually use and need. So, in the end, you shouldn’t set EnablePageMethods on a master page script manager.
Keep in mind that sometimes you might still want to register Web services and scripts through the master’s script manager because Web service proxies and script files are downloaded once and then cached. However, a relatively large script that is only used by a very small percentage of pages is perhaps better managed if bound to the script manager of the specific page.
To override the settings of a master’s script manager in a content page, you either use the ScriptManagerProxy control (discussed in Chapter 3, “The Pulsing Heart of ASP.NET AJAX”) or programmatically invoke the static GetCurrent method on the ScriptManager class. The GetCurrent method returns the current instance of the script manager and provides full access to all of its properties and methods.
Adjusting the Log-in Process
In most ASP.NET 2.0 applications, the log-in process is implemented through a separate log-in page that collects credentials and posts back to the server to validate them. If the validation is successful, the user is redirected to the originally requested page with a valid authentication cookie attached to the request.
From an ASP.NET 2.0 perspective, the log-in process requires two page redirects and one post-back to validate credentials, as shown in Figure 8-2.
Figure 8-2: The typical log-in process of an ASP.NET 2.0 application
Usually, the user clicks to jump to a log-in page or to reach a protected page directly. In the latter case, the user is automatically redirected to the log-in page. After that, the user types in her credentials and clicks to validate. During the postback, if the user is recognized a page redirect is made to the return URL. In this process, there’s not much that you can improve with AJAX. You can’t control redirects and hyperlinking. As a result, two full-page redraws occur, and there’s no way to prevent that. By wrapping the user interface of the log-in page in an UpdatePanel, you can save the page redraw in case an error occurs. Here’s an example:
New user? Register here!
This code can be further improved by wrapping only the Login control in the updatable panel. The final effect is the same, but you move a bit less markup around.
Refactoring the Log-in Process
To take advantage of AJAX during the log-in process, you should design pages with a dual interface-for anonymous and registered users. In the web.config file, you don’t associate the page with a tag that rules out anonymous users. The page is always accessible, but it hides or disables portions of its user interface reserved to logged-in users.
The log-in form can be embedded in the home page or embedded directly in any such reserved pages. When the user logs in, the user interface of the page changes to reflect the user’s preferences and enabled functions. You can wrap these user interface elements in an UpdatePanel control and use the log-in button as a trigger. No page redirection occurs, and the postback to validate and update the user interface can be easily managed by ASP.NET AJAX.
When dual pages are used, you can also consider a further level of integration with ASP.NET AJAX, as we demonstrated in Chapter 6, “Built-in Application Services.” In this case, you replace the log-in server button of the log-in form with a client button. Next, you use JavaScript to command a remote authentication and refresh the page using script code. The Page Rearrangement and Display Morphing patterns provide guidance on the most effective ways to achieve this.
Warning |
A log-in form directly hosted in the target page (or the home page) provides a smoother user experience, but it raises a security issue. In general, to be sure that nobody can intercept the user’s credentials, the log-in form should be placed in an HTTPS page. However, simply posting to an HTTPS-based URL doesn’t fully protect you from possible DNS-poisoning attacks. To preserve security-and at least for certain categories of applications (for example, banking)-this is key: you should force users to navigate to an HTTPS page before they can enter credentials. Realistically, the log-in process is not the best scenario to consider for AJAX enhancements. |
Users Registration
A common solution to letting users register with a site entails using a homemade wizard or an instance of the built-in CreateUserWizard control. The JSSK site employs the following code:
... ...
By simply wrapping this code with an UpdatePanel control, you prevent full page refreshes without changing any other aspect of the code. The final effect is definitely worth the effort.
Searching for Jobs
The job search page allows logged-in users to search for positions. The page contains an input form to restrict the query-to city, skills, or country. Once you click the Search button, the page posts back, runs a database query, and returns the jobs it finds, listing them in a grid. This is the perfect scenario for setting up an updatable region with triggers and progress support. Here’s an example:
Job Search Site in action looking for a good job for you... It may take a while.
You wrap the grid in an updatable region and use triggers to bind it to the user’s click on the Search button. Finally, the UpdateProgress control adds some animation during the search. (See Figure 8-3.)
Figure 8-3: The job-search function enhanced with AJAX capabilities
Tip |
The preceding code snippet for the UpdateProgress control has been edited for clarity and is missing the style information necessary to make the progress template appear at the center of the page as in the figure. To obtain the same results as in Figure 8-3, you should style the outermost tag in the progress template as follows:width: 350px; height: 140px; top: 40%; left: 35%; position: absolute; border: solid 1px black; |
The grid contains a button column that can be clicked to view details of a given job. In the JSSK implementation, when the user clicks, the page posts back and then redirects to a distinct page, as shown here:
void GridView1_RowCommand(object sender, GridViewCommandEventArgs e) { if (e.CommandName == "details") { Response.Redirect("~/viewjobposting.aspx?last-para">In this case, there’s not much that ASP.NET AJAX can do to save you a full page loading. A more AJAX-friendly design of the page entails that you populate and display a user control with any job information. You embed the user control in the same page, manage its setup and visibility through server code, and let the UpdatePanel control silently download any related markup. Of course, doing so represents a major page design overhaul, so we’ll stick with the current design and redirect to the job posting in formation page.
Posting a Resume
Posting a resume is a function that can be quickly adjusted to support the AJAX paradigm. The function is implemented as a set of input fields and a couple of buttons to save or cancel. By wrapping everything in an updatable panel, you prevent full page reloads in case of errors. In addition, you incur no postback overhead if you display a confirmation message to the user or bring up a little animation while the save operation occurs.
Note |
Only a few of the JSSK pages have been enhanced to support AJAX capabilities. In the new user interface, a double asterisk in the navigation tree indicates pages you can navigate to that have been enhanced with AJAX support. |
Controlling Menus and Navigation
Many ASP.NET pages include a menu or a navigation panel. More often than not, these controls are declaratively bound to a site map; in this case, the navigation that occurs is out of your control. If you employ navigation controls to let users jump to other links, a full page loading is inevitable and by design.
Sometimes, though, a Menu control might have elements that just order a partial redesign of the current page. In this case, AJAX can help to minimize page flickering. You wrap the portion of the interface affected by a menu selection in an UpdatePanel and add a trigger:
...
Whenever any menu items are clicked, the panel is refreshed. If the menu item is bound to an external URL, a full page transition is performed.
Unfortunately, the MenuItemClick event fires regardless of whatever selection is made. Is there a way to refresh only when a particular item is clicked? The only possibility I see entails creating a custom, application-specific menu control and firing ad hoc events-for example, Item1Clicked. Next, you use just this specialized event to trigger a panel update.
Note |
AJAX-oriented changes to the Menu and TreeView controls are expected to ship with the “Orcas” release of ASP.NET. |
Periodic Screen Refresh
Nearly all Web sites have information on the home page that needs be updated frequently. Think, for example, of news Web sites that offer real-time updates on stock exchange markets. For years, many sites used the meta refresh tag to instruct the browser to reload the page periodically. Although it’s easy to code and is effective, the solution doesn’t get along with today’s rich pages. Refreshing too often is boring for users; refreshing too little takes away most of the feature’s value.
Today, a common solution consists of displaying a panel and updating it on each postback. In its home page, JSSK presents a panel with statistics about the current number of jobs, resumes, and companies featured in the site. As you can see, this number is subject to change as soon as concurrent users enter fresh data. In an AJAX-enabled application, that panel would refresh periodically without affecting the rest of the page. Easier said than done.
Periodic screen refresh is a delicate topic with a big potential drawback. Suppose you configure the panel to refresh every five seconds. Can you imagine the effect on the site of thousands clients that operate concurrently at peak times? On the other hand, if the goal of the panel is displaying real-time data, browser-side caching is simply not an option. In the end, you must refresh a piece of user interface periodically, while limiting the impact of frequent updates on the site’s scalability.
Refreshing on Postback
The approach taken by JSSK is not particularly ambitious; instead, it is simple and effective. A user control is embedded in the page whose input fields are set during page loading. Here’s the source code:
protected void Page_Load(object sender, EventArgs e) { lblCompanies.Text = Company.GetCompanyCount().ToString(); lblJobs.Text = JobPosting.GetJobPostingCount().ToString(); lblResumes.Text = Resume.GetResumeCount().ToString(); lblLastUpdate.Text = DateTime.Now.ToString(); }
Added to the master page, the user control is shared by a number of Web site pages. Each time one of these pages is refreshed or loaded, the panel is updated. In the end, in a typical browser session a user gets several updates. I modified the original JSSK source code to add a label with the time of the last update just to let you easily verify how often you get fresh data. (See Figure 8-4.)
Figure 8-4: The statistics panel with up-to-date information on jobs and resumes
Although it might not sound particularly exciting at first, this approach is still one of the most effective in terms of the cost/benefit ratio.
Using Updatable Panels
In its original form, the statistics panel won’t get updated if the host page is partially updated through an AJAX postback. How can you detect partial updates, and what is necessary to programmatically update a panel?
As we saw in Chapter 4, an updatable panel always refreshes when another panel in the page is updated. In light of this built-in behavior, all that you have to do is wrap the statistics panel in an UpdatePanel control. However, an updatable panel defined in a user control has no way to detect partial updates that occur at the page level. Two UpdatePanel controls refresh sympathetically only if they are defined within the same container or if they are nested.
You don’t have to wrap the markup of the statistics panel with an UpdatePanel control; instead, you wrap the user control in an updatable panel within the master page:
In this way, the contents of the statistics user control is updated each time a partial update occurs within any pages based on the master. Is there a way for you to base updates of a region on runtime conditions?
You can use triggers, either programmatically or declaratively, to specify under which conditions the update should occur. Alternatively, you can programmatically command the update of a given region by invoking the Update method on the UpdatePanel control that defines the region. (See Chapter 4.)
Finally, there’s a third option that falls somewhere in between the other two. In the Load event of the page, or user control, that contains the partial-update region, you can check the ID of the postback element and decide whether or not, and even how, to update the user interface. Here’s an example:
void Page_Load(object sender, EventArgs e) { if (ScriptManager.GetCurrent(this.Page).IsInAsyncPostBack) { // Use the AsyncPostBackSourceElementID property on // ScriptManager to check the ID of the postback element ... return; } ... }
To access properties such as IsInAsyncPostBack and AsyncPostBackSourceElementID, any script manager proxy you might have around the page won’t work. You must use the GetCurrent static method on the ScriptManager class.
Using Server-Side Services
As we saw in Chapter 7, it’s often desirable for the client of an AJAX site to call server-side services. Taken as a whole, the server-side services expose an API to the client that a JavaScript proxy will invoke. ASP.NET AJAX Extensions provides a framework that makes this easy. You expose the API either through a special ASP.NET Web service or a set of page methods. In general, you opt for a Web service if more than one page or client application is going to share the same server-side service; you go for page methods if that API serves the purpose of just one page.
Server-side services are generally invoked explicitly; however, you can also set up a client timer to trigger calls at regular intervals. To start out, let’s design a contract for accessing the statistics you see in Figure 8-4:
public interface IStatistics { JobStatistics GetStatistics(); } public class JobStatistics { public int CompanyCount; public int ResumeCount; public int JobCount; public string LastUpdate; }
A Web service that implements the contract might look like the one shown next. Let’s name the service StatService.asmx.
using System; using System.Web.Services; using System.Web.Services.Protocols; using System.Web.Script.Services; using JobSiteStarterKit.BOL; namespace IntroAjax.WebServices { [WebService(Namespace = "http://IntroAjax/")] [ScriptService] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class StatService : IStatistics { public StatService() {} [WebMethod] public JobStatistics GetStatistics() { JobStatistics stat = new JobStatistics(); stat.CompanyCount = Company.GetCompanyCount(); stat.JobCount = JobPosting.GetJobPostingCount(); stat.ResumeCount = Resume.GetResumeCount(); stat.LastUpdate = DateTime.Now.ToString(); return stat; } } }
To invoke the Web service, first you have to register it with the script manager. In the master page, make sure the script manager is configured as shown here:
At this point, you edit the user control to add a client button to explicitly request an update of the statistics:
The refreshStatistics JavaScript function will call a dynamically generated proxy class and download statistics data. The call is made asynchronously, and a user-defined callback gathers results and updates the user interface.
function refreshStatistics() { IntroAjax.WebServices.StatService.GetStatistics(methodCompleted); } function methodCompleted(results, context, methodName) { $get("ctl00_Statistics1_lblJobs").innerHTML = results.JobCount; $get("ctl00_Statistics1_lblResumes").innerHTML = results.ResumeCount; $get("ctl00_Statistics1_lblCompanies").innerHTML = results.CompanyCount; $get("ctl00_Statistics1_lblLastUpdate").innerHTML = results.LastUpdate; }
It is worth noting that the ID of any user interface element you want to update has to exactly match the ID of the real DOM element. The user control contains a Label control named lblJobs. It would then be natural to assume that you can find its client-side DOM element using $get(“lblJobs”). This worked in earlier examples because the Label was a direct child of the form. In this case, we have two further levels of containment-the master page and user control. In ASP.NET, containment is reflected in the client-side ID of the corresponding DOM elements. You can easily verify what the real client ID of the DOM element of an ASP.NET control is by looking at the page’s source code.
Note |
In server ASP.NET programming, you obtain the client ID of an ASP.NET control (or a portion of it) using the ClientID property of the Control object. On the client, though, there’s no way to automatically match the server ID (for example, lblJobs) with the client ID. In case of control containment, you have to figure out what the real ID is. |
Figure 8-5 shows the statistics panel updated by an explicit client request. The feature is used in addition to user control-managed updates triggered during postbacks and partial postbacks.
Figure 8-5: A Refresh button allows users to explicitly update the statistics making a Web service call
Using Server Timers
To schedule panel updates at a fixed time or periodically, you use timers. Timers are browser objects created and controlled using a couple of methods on the window object-setTimeout and clearTimeout. Once the interval has been set, the timer waits for the specified number of milli-seconds and then fires a call to a user-defined function. To make the timer proceed indefinitely, you generally write a user-defined callback that performs its task and then restarts the timer.
In ASP.NET AJAX Extensions, you also have a server-side interface for the client timer object-the Timer control, which we discussed in Chapter 4. To periodically refresh the statistics box, you have two options: using the Timer server control in conjunction with an UpdatePanel or using a client-side timer. The former approach is really quick to implement. You add a Timer to the master page and bind the Tick event of the timer to the trigger list of the updatable panel we added earlier:
This code will refresh the panel every two seconds. Can you embed the timer in the user control? The quick answer is yes, but there’s a bit more to consider. You can certainly have a user control that encapsulates an updatable panel and a timer. Such a user control would hide all the details and just offer to the outside world an auto-refreshing statistics panel. However, this doesn’t mean that the outermost UpdatePanel we have in the code snippet can be removed. In fact, the outermost UpdatePanel serves the purpose of updating statistics during partial post-backs. It can be argued that you probably don’t need to worry about partial postbacks once you have an auto-refreshing user control. That’s true.
So, in the end, you can certainly move UpdatePanel and Timer within the user control. If you also keep the UpdatePanel control that wraps the user control, you’ll get an additional refresh whenever a partial update occurs within any page based on the master.
Using Client Timers
A server timer has just one drawback. It requires an UpdatePanel to refresh the user interface, and subsequently it moves a chunk of markup at every tick. A completely client-based timer, on the other hand, will use a Web service (or a page method) to get fresh data. In this case, only statistics will travel over the wire-just three numbers rather than an HTML table with data, markup, and perhaps auxiliary resources.
To manage a client timer, you can use direct calls to the browser’s window object or, better yet, manage to create a Microsoft AJAX library JavaScript class. Here’s the SimpleTimer.js class:
Type.registerNamespace('IntroAjax.Components'); IntroAjax.Components.Timer = function(userCallback) { this._interval = 10000; this._raiseTickDelegate = Function.createDelegate(this, this._tick); this._userCallback = userCallback; this._timer = null; } function IntroAjax$Components$Timer$get_interval() { return this._interval; } function IntroAjax$Components$Timer$set_interval(value) { this._interval = value; } function IntroAjax$Components$Timer$stop() { this._stopTimer(); } function IntroAjax$Components$Timer$start() { this._startTimer(); } function IntroAjax$Components$Timer$_tick() { if (this._userCallback !== null) this._userCallback(); this._startTimer(); } function IntroAjax$Components$Timer$_startTimer() { this._timer = window.setTimeout( this._raiseTickDelegate, this.get_interval()); } function IntroAjax$Components$Timer$_stopTimer() { if (this._timer !== null) { window.clearTimeout(this._timer); this._timer = null; } } IntroAjax.Components.Timer.prototype = { get_interval: IntroAjax$Components$Timer$get_interval, set_interval: IntroAjax$Components$Timer$set_interval, stop: IntroAjax$Components$Timer$stop, start: IntroAjax$Components$Timer$start, _raiseTick: IntroAjax$Components$Timer$_tick, _startTimer: IntroAjax$Components$Timer$_startTimer, _stopTimer: IntroAjax$Components$Timer$_stopTimer } IntroAjax.Components.Timer.registerClass('IntroAjax.Components.Timer');
You register this file with the script manager using the