Using Client-Side ASP.NET AJAX

Making JavaScript Look Like C#
Let me start by saying that there is nothing wrong with JavaScript the language. It is not a toy language. It is not a limited language. JavaScript simply has it roots in a different programming language family than other languages you are familiar with, such as C# and VB.NET. For a great, quick introduction to JavaScript the language, I recommend that you read “A re-introduction to JavaScript” at http://developer.mozilla.org/en/docs/A_re-introduction_to_JavaScript. JavaScript is an object-oriented programming language. In fact, one could argue that it is more object-oriented than languages such as C# and VB.NET. In a language such as C#, you make a distinction between classes (Aristotelian forms) and objects (Aristotelian matter). An object is an instance of a class, but a class does not exist in its own right. In JavaScript, classes do not exist. The only thing that exists are objects (everything is matter). Objects are not related to one another by being instances of the same class. Instead, one object can be related to another object when one object is the prototype for another object. Another major difference between JavaScript and C# is that JavaScript is a dynamic language. The type of a variable can change at any moment during runtime. When JavaScript code is executed, a String might transform into an Integer and back again. The C# language, on the other hand, is statically typed. Once declared, a String is a String and it can never be anything else.

Using the Microsoft AJAX Library
The supporting code for the client-side Microsoft AJAX Framework is contained in a single JavaScript file named MicrosoftAjax.js. This file is included in a page automatically when you add an ASP.NET ScriptManager control to a page. If you add an AJAX Web Form to a website within Visual Web Developer, the page contains the ScriptManager control automatically.

Creating an AJAX Client Library
Before we do anything else, we need to discuss how you create an external JavaScript file and reference it in an AJAX Web Form page. You create a JavaScript file by selecting the menu option Website, Add New Item and selecting the AJAX Client Library  For example, the file in Listing contains a single JavaScript function called sayMessage() that displays a JavaScript alert with a message.


LISTING myLibrary.js
/// <reference name=”MicrosoftAjax.js”/>
function sayMessage()
{
alert(“Hello World!”);
}
if(typeof(Sys) !== “undefined”) Sys.Application.notifyScriptLoaded();

You should notice two special things about this file. First, at the top of the file is a comment. The significance of this comment will be explained in the next section. Second, at the bottom of the file is a conditional that checks whether a namespace named Sys exists. If it does exist, the Application.notifyScriptLoaded() method is called. You should add this conditional to the bottom of every external JavaScript file that you use with the ASP.NET AJAX Framework. The notifyScriptLoaded() method is used to tell the Framework that the external JavaScript file has been successfully loaded. The check for the Sys namespace enables you to use the JavaScript file in applications that do not use the ASP.NET AJAX Framework. If the Sys namespace doesn’t exist, the application is not an ASP.NET AJAX application and there is no point in calling the notifyScriptLoaded() method. After you create an external JavaScript file, you can use it in an ASP.NET AJAX–enabled page by creating a script reference. The page in Listing illustrates how you add a script reference to the myLibrary.js library.

LISTING ShowMyLibrary.aspx
<%@ Page Language=”C#” %>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title>Show myLibrary</title>
<script type=”text/javascript”>
function pageLoad()
{
sayMessage();
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
<Scripts>
<asp:ScriptReference Path=”~/myLibrary.js” />
</Scripts>
</asp:ScriptManager>
</div>
</form>
</body>
</html>

Taking Advantage of JavaScript Intellisense
One huge advantage of using Visual Web Developer to write client-side AJAX applications is its support for Intellisense for JavaScript. The Intellisense appears both for built-in JavaScript objects and methods and for JavaScript libraries you write. Visual Web Developer will attempt to infer the data type of a JavaScript variable. This is quite an accomplishment considering that JavaScript is a dynamic language and a variable might change its data type at any time at runtime.

Working with Classes
As discussed previously, the JavaScript language does not have classes. In the JavaScript universe,  everything is an object. In order to make JavaScript look more like C#, Microsoft has extended JavaScript in such a way that you can pretend that the JavaScript language has classes. That way, you can use familiar C# language constructs such as interfaces and class inheritance.

By convention, when building an ASP.NET AJAX application, you name private members of an object with a leading underscore. Any field, property, or method of an object that has a name starting with an underscore does not appear in Intellisense. Strictly speaking, these object members are not truly  private. From the point of view of the JavaScript language, there is no difference between an object member named _message and an object member named message. Calling alert(obj._message) will show the value of the private _message field. The JavaScript language does support truly private fields if you are willing to do some work (see http://www.crockford.com/javascript/private.html).

Working with Inheritance
If you use the registerClass() method to simulate creating .NET classes on the client, you can take advantage of something that resembles class inheritance. You can create a derived class that inherits from a base class. The derived class can override and extend the properties and methods of the base class.

Working with Namespaces
In the .NET Framework, namespaces are used to group related classes. For example, all the classes related to working with the file system are located in the System.IO namespace. This is done for the purposes of documentation and to prevent naming collisions. If a class appears in the System.IO namespace, you know that is has something to do with file access. Different namespaces can have classes with the very same name.

Retrieving DOM Elements
One of the most common operations you perform when building Ajax applications is retrieving DOM elements. For example, you might need to grab a span element from a page and modify its innerHTML. In a typical JavaScript application, you would use the document.getElementById() method to grab the DOM element, like this:
var span = document.getElementById(“mySpan”);
span.innerHTML = “Hello World!”;

The Microsoft AJAX Library introduces a shortcut method you can use instead of the document.getElementById() method. You can use the $get() method, like this:
var span = $get(“mySpan”);
span.innerHTML = “Hello World!”;

Alternatively, if you want to write really condensed code, you can use the $get() method, like this:
$get(“mySpan”).innerHTML = “Hello World!”;

When calling $get(), you can pass a second parameter that represents the DOM element to search. For example, the following statement returns the mySpan element contained in the myDiv element:
var myDiv = $get(“myDiv”);
$get(“mySpan”, myDiv).innerHTML = “Hello World!”;

Be careful when calling either $get() or document.getElementById() because they are expensive operations in terms of performance. It is better to use $get() to grab a reference to a DOM element and assign it to a variable once than to use $get() multiple times to work with the same DOM element.

The Prototype framework (a popular, non-Microsoft AJAX framework) first introduced the $() function as an alias for document.getElementById(). Originally, Microsoft used $() instead of $get() as well. However, Microsoft wanted developers to be able to mix Prototype applications with Microsoft AJAX applications so it changed the name of the function to $get().

Handling DOM Events
One of the biggest pains associated with writing client-side applications has to do with handling DOM events (for example, handling a client-side Button click event). The problem is that Microsoft Internet Explorer does not follow W3C standards. Therefore, you always end up writing a lot of extra code to make sure your application works with Internet Explorer and standards-compliant browsers such as Firefox and Opera. The Microsoft AJAX Library provides an abstraction layer that smoothes over the difference between browsers. You handle a DOM event either with the $addHandler() shortcut or the $addHandlers() shortcut.

Retrieving DOM Event Information
When you are writing a normal JavaScript application, retrieving information about an event is just as difficult as wiring up a handler for a client-side event. Again, Internet Explorer represents event information in a completely different way than Firefox or Opera. The Microsoft AJAX Library, once again, enables you to smooth over the differences between the different browsers. If you create an event handler with the Microsoft AJAX Library, event information is passed to the event handler as a parameter. In particular, an instance of the Sys.UI.DomEvent class is passed to the event handler.

The Sys.UI.DomEvent class has a number of useful properties:
. altKey—Returns true when the Alt key is pressed.
. button—Returns a value from the Sys.UI.MouseButton enumeration: leftButton, middleButton, or rightButton.
. charCode—Returns the code for the key pressed that raised the event. Use the Sys.UI.Key enumeration to compare the charCode against the particular types of keys, such as the Backspace, Tab, and Enter.
. clientX—Returns the horizontal position of the mouse relative to the client area of the browser window, excluding the scroll bars.
. clientY—Returns the vertical position of the mouse relative to the client area of the browser window, excluding the scroll bars.
. ctrlKey—Returns true when the Ctrl key is pressed.
. offsetX—Returns the horizontal position of the mouse relative to the element that raised the event.
. offsetY—Returns the vertical position of the mouse relative to the element that raised the event.
. screenX—Returns the horizontal position of the mouse relative to the entire screen.
. screenY—Returns the vertical position of the mouse relative to the entire screen.
. shiftKey—Returns true when the Shift key is pressed.
. target—Returns the original element that raised the event (as distinct from the element associated with the event handler).
. type—Returns the name of the event (for example, click).

The Sys.UI.DomEvent class also has two useful methods:
. preventDefault—Stops the default action associated with the event.
. stopPropagation—Stops the event from bubbling up to its parent element.


Creating Callbacks and Delegates
In the previous sections, you learned how to handle DOM events and retrieve event information by using the $addHandler() method. This method of wiring up an event handler, however, has a serious limitation: It doesn’t enable you to pass additional information to the event handler. The ASP.NET AJAX Library includes two methods you can use to pass additional information to event handlers:
. Function.createCallback(method, context)
. Function.createDelegate(instance, method)

Calling the createCallback() method creates a method that passes an additional context parameter to an event handler. You can pass anything you want as the context parameter. For example, the context parameter could be a simple string or it could be a reference to a component. Calling the createDelegate() method does not pass any additional parameters to the event handler. However, it changes the meaning of this in the handler. Normally, if you use this in an event handler, it refers to the DOM element that raised the event. The createDelegate() method enables you to change this to refer to anything you like.

Debug and Release AJAX Libraries
Two versions of the MicrosoftAjax.js file contain the AJAX Library: the release version and the debug version. The release version of the library is minimized to its smallest possible size. All inessential code and comments have been stripped. The debug version, on the other hand, is quite readable. It contains comments on each of the methods. Furthermore, the debug version contains additional code intended to inform you when you are misusing a method. When you call a method using the release version of the library, all the parameters passed to the method are validated (the type and number of parameters are validated).

You will want to use the release version of the AJAX Library for a production application and the debug version only while actively developing an application. The easiest way to switch between the release and debug versions of the script is to switch between debug and release mode in the web configuration file. The same setting you use to control whether server-side code is compiled in debug or release mode controls whether the debug or release version of the AJAX Library is served. To switch to debug mode, find or add the compilation element in the system.web section of the web.config file and assign the value true to its debug attribute, like this:
<compilation debug=”true”>

Alternatively, you can control whether the debug or release version of the AJAX Library is used by modifying the ScriptMode property of the ScriptManager control. This property has the following four possible values:
. Auto—This is the default value. Use the compilation setting from the Web.config file.
. Debug—Use debug scripts.
. Inherit—Same as Auto.
. Release—Use release scripts.

Declaring a ScriptManager control in a page like this forces the debug version of the AJAX Library to be used:
<asp:ScriptManager ID=”ScriptManager1” ScriptMode=”Debug” runat=”server” />

Debugging Microsoft AJAX Applications
The last topic we need to examine in this section is debugging. There are two ways that you can debug a client-side ASP.NET AJAX application: You can display trace messages, and you can use the Visual Web Developer debugger.The Microsoft AJAX Library includes a Sys.Debug class. You can use this class to output trace messages and break into the Visual Web Developer debugger. The Sys.Debug class supports the following methods:
. assert—Enables you to evaluate a JavaScript expression. If the expression returns false, a message is displayed in the debugger and the trace console and you are prompted to break into the debugger.
. clearTrace—Enables you to clear all messages from the trace console.
. fail—Enables you to display a message in the debugger and the trace console and break into the debugger.
. trace—Enables you to output a message to the debugger and trace console.
. traceDump—Enables you to dump all properties of an object to the debugger and trace console.

These methods output trace messages to two different consoles. The debugger console is the Visual Web Developer debugger console that appears when you execute an application in debug mode. Right-click a page in the Solution Explorer window and select the menu option Set as Start Page. Select the menu option Debug, Start Debugging (F5), and the messages will appear in the debugger console Output window.

Calling Web Services from the Client
The heart of Ajax is the ability to send and retrieve information from the web server without needing to post a page back to the web server. Ajax is all about performing “sneaky” postbacks. The vision behind a pure Ajax application is that it should consist of a single page. All updates to the single page after it has been loaded should be performed by calling web services. You should never need to perform a postback because any postback results in a bad user experience (the page jumps and the universe freezes). The ASP.NET AJAX Library provides support for calling web services directly from the client (the web browser). In this section, you learn two methods of exposing a web method to an AJAX page. You learn how to call a web method from a separate web service, and you learn how to call a web method exposed by the page itself. Finally, weexamine three specialized web services exposed by the ASP.NET AJAX Framework: the Authentication service, the Role service, and the Profile service.

Calling an External Web Service
Let’s start simple. We’ll create a Quotation web service that randomly returns a quotation from a list of quotations. Next, we’ll create an AJAX page that contains a button. When you click the button, a random quotation will be displayed in a <span> tag. The first step is to create the web service. The web service is contained in Listing.

LISTING QuotationService.asmx
<%@ WebService Language=”C#” Class=”QuotationService” %>
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Script.Services;
using System.Collections.Generic;
[WebService(Namespace = “http://tempuri.org/”)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class QuotationService : System.Web.Services.WebService
{
[WebMethod]
public string GetQuote()
{
List<string> quotes = new List<string>();
quotes.Add(“The fool who is silent passes for wise.”);
quotes.Add(“The early bird catches the worm.”);
quotes.Add(“If wishes were true, shepherds would be kings.”);
Random rnd = new Random();
return quotes[rnd.Next(quotes.Count)];
}
}

You create the file in Listing by selecting the menu option Website, Add New Item and choosing the Web Service item. The web service contains a single web method named GetQuote(). This method returns a single quotation from a list of quotations as a string. There is only one thing special about this web service. Notice that a ScriptService attribute is applied to the web service class (the ScriptService attribute lives in the System.Web.Script.Services namespace). You must add this attribute in order to call the web service from an AJAX page. Now that we have created the web service, we can call it from an AJAX page. The page in Listing calls the web service in order to display a random quotation.

LISTING ShowWebServiceMethod.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title>Show Web Service Method</title>
<script type=”text/javascript”>
function pageLoad()
{
$addHandler( $get(“btnGet”), “click”, getQuote );
}
function getQuote()
{
QuotationService.GetQuote(getQuoteSuccess, getQuoteFail);
}
function getQuoteSuccess(result)
{
$get(“spanQuote”).innerHTML = result;
}
function getQuoteFail(error)
{
alert(error.get_message());
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
<Services>
<asp:ServiceReference
InlineScript=”true”
Path=”~/Services/QuotationService.asmx” />
</Services>
</asp:ScriptManager>
<input id=”btnGet” type=”button” value=”Get Quote” />
<br /><br />
<span id=”spanQuote”></span>
</div>
</form>
</body>
</html>

Calling a Static Page Method
If you are not planning to call a web method from multiple pages, there is no reason to perform all the work of creating a separate web service. Instead, you can expose a static method from the same AJAX page that is calling the web method. For example, the page in Listing includes a server method named GetQuote(). The server GetQuote() method is called by a client method named GetQuote().

LISTING ShowPageMethod.aspx
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Collections.Generic” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
[System.Web.Services.WebMethod]
public static string GetQuote()
{
List<string> quotes = new List<string>();
quotes.Add(“The fool who is silent passes for wise.”);
quotes.Add(“The early bird catches the worm.”);
quotes.Add(“If wishes were true, shepherds would be kings.”);
Random rnd = new Random();
return quotes[rnd.Next(quotes.Count)];
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title>Show Page Method</title>
<script type=”text/javascript”>
function pageLoad()
{
$addHandler( $get(“btnGet”), “click”, getQuote );
}
function getQuote()
{
PageMethods.GetQuote(getQuoteSuccess, getQuoteFail);
}
function getQuoteSuccess(result)
{
1732 CHAPTER 33 Using Client-Side ASP.NET AJAX
LISTING 33.18 Continued
$get(“spanQuote”).innerHTML = result;
}
function getQuoteFail(error)
{
alert(error.get_message());
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager
ID=”ScriptManager1”
EnablePageMethods=”true”
runat=”server” />
<input id=”btnGet” type=”button” value=”Get Quote” />
<br /><br />
<span id=”spanQuote”></span>
</form>
</body>
</html>

You must do one special thing before you can expose web methods from a page. You must assign the value true to the ScriptManager control’s EnablePageMethods property. In Listing, the server GetQuote() method is called with the following line of code:
PageMethods.GetQuote(getQuoteSuccess, getQuoteFail);

Just like in the previous section, when you call a page method, you can supply both a success and failure handler. You might be wondering where the PageMethods class comes from. This class is generated in the page automatically when you expose a page method. The class will always be called PageMethods. The class will contain a proxy client method that corresponds to each server page method.

Editing Movies with AJAX
In this section, I want to present you with a more complicated (and realistic) example of calling server web methods from the client. In this section, we create a page that can be used to edit the Movie database table. The page enables you to add new movies to the database and display all the existing movies. Both operations are performed through AJAX calls so that a postback is never necessary.

LISTING EditMovies.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title>Edit Movies</title>
<script type=”text/javascript”>
function pageLoad()
{
$addHandler($get(“btnAdd”), “click”, addMovie);
bindMovies();
}
function bindMovies()
{
MovieService.SelectAll(selectAllSuccess);
}
function addMovie()
{
var movieToAdd =
FIGURE 33.15 Displaying and inserting database records with AJAX.
1734 CHAPTER 33 Using Client-Side ASP.NET AJAX
LISTING 33.19 Continued
{
Title: $get(“txtTitle”).value,
Director: $get(“txtDirector”).value
};
MovieService.Insert(movieToAdd, addMovieSuccess);
}
function addMovieSuccess()
{
bindMovies();
}
function selectAllSuccess(results)
{
var sb = new Sys.StringBuilder()
var movie;
var row;
for (var i=0;i < results.length; i++)
{
movie = results[i];
row = String.format(“{0} directed by {1}<br />”,
movie.Title, movie.Director);
sb.appendLine(row);
}
$get(“divMovies”).innerHTML = sb.toString();
}
</script>
</head>
<body>
<form runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
<Services>
<asp:ServiceReference
InlineScript=”true”
Path=”~/Services/MovieService.asmx” />
</Services>
</asp:ScriptManager>
<fieldset>
<legend>Add Movie</legend>
<label for=”txtTitle”>Title:</label>
<input id=”txtTitle” />
<br /><br />
<label for=”txtTitle”>Director:</label>
<input id=”txtDirector” />
<br /><br />
<input id=”btnAdd” type=”button” value=”Add Movie” />
</fieldset>
<div id=”divMovies”></div>
</form>
</body>
</html>

The page in Listing calls an external web service named MovieService. The two web methods from MovieService are SelectAll() and Insert(). The SelectAll() method is called to get the current list of movies. This method is called when the page first loads and it is called after a new movie is inserted. The list of movies is displayed in a <div> element with the following code:

function selectAllSuccess(results)
{
var sb = new Sys.StringBuilder()
var movie;
var row;
for (var i=0;i < results.length; i++)
{
movie = results[i];
row = String.format(“{0} directed by {1}<br />”,
movie.Title, movie.Director);
sb.appendLine(row);
}
$get(“divMovies”).innerHTML = sb.toString();
}

This code iterates through the list of Movie objects returned by the web service. A single string that represents all the movies is built with a StringBuilder object. Finally, the contents of the StringBuilder are displayed in a <div> tag. The Insert() web method is called to add a new movie to the database. The body of the page contains a simple form for gathering the movie title and director. When you click the Add Movie button, the following method is called:

function addMovie()
{
var movieToAdd =
{
Title: $get(“txtTitle”).value,
Director: $get(“txtDirector”).value
};
MovieService.Insert(movieToAdd, addMovieSuccess);
}

The addMovie() method creates a new Movie object named movieToAdd. The movieToAdd object represents the values that the user entered into the <input> elements of txtTitle and txtDirector. Finally, the movieToAdd object is passed to the Insert() method exposed by the MovieService web service proxy.

LISTING MovieService.asmx
<%@ WebService Language=”C#” Class=”MovieService” %>
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Web.Script.Services;
[WebService(Namespace = “http://tempuri.org/”)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class MovieService : System.Web.Services.WebService
{
[WebMethod]
public List<Movie> SelectAll()
{
MyDatabaseDataContext db = new MyDatabaseDataContext();
return db.Movies.ToList();
}
[WebMethod]
public int Insert(Movie movieToAdd)
{
MyDatabaseDataContext db = new MyDatabaseDataContext();
db.Movies.InsertOnSubmit(movieToAdd);
db.SubmitChanges();
return movieToAdd.Id;
}
}

Notice that you can pass objects back and forth between an AJAX page and a web service. The AJAX page passes a Movie object to the web service’s Insert() method. The web service’s SelectAll() method returns a collection of Movie objects. These objects are seamlessly passed back and forth between the client world and the server world. Using the Authentication Service The Microsoft AJAX Library includes three built-in web services. In this section, we examine the first of these built-in web services: the Authentication service. The Authentication service works with ASP.NET Form authentication. You can use it with the ASP.NET membership framework to authenticate users using one of the ASP.NET membership providers. The two providers included with the ASP.NET framework are SqlMembershipProvider (authenticates users against a database of usernames and passwords) and ActiveDirectoryMembershipProvider (authenticates users against the Active Directory). Before you can use the Authentication service, you need to make two configuration changes to your web configuration file. First, you need to enable Forms authentication (the default form of authentication is Windows). Find the <authentication> element in the <system.web> section and modify it to look like this:
<authentication mode=”Forms”/>

Second, you need to enable the Authentication service because it is disabled by default. If your web configuration file does not already contain a <system.web.extensions> element, you will need to add one. Add it outside of the <system.web> section. The <system.web.extensions> element must contain an <authenticationService> element that looks like this:
<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled=”true”/>
</webServices>
</scripting>
</system.web.extensions>

The <authenticationService> element includes a requireSSL attribute in case you want to require an encrypted connection when logging in to the server. Finally, if you want to create one or more users, you can use the Website Administration Tool. Select the menu option Website, ASP.NET Configuration and then select the Security tab. Click the Create User link to create a new user. The page in Listing demonstrates how you can call the Authentication service from an AJAX page. The page contains a login form that you can use to authenticate against the SqlMembershipProvider. If you log in successfully, you get to see the secret message

If you want to try the ShowLogin.aspx page, a valid username and password are Steve and secret#, respectively.

LISTING ShowLogin.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
[System.Web.Services.WebMethod]
public static string GetSecretMessage()
{
return “Time is a fish”;
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title>Show Login</title>
<script type=”text/javascript”>
function pageLoad()
{
$addHandler( $get(“btnLogin”), “click”, login);
}
function login()
{
Sys.Services.AuthenticationService.login
(
$get(“txtUserName”).value,
$get(“txtPassword”).value,
false,
null,
null,
loginSuccess,
loginFail
);
}
function loginSuccess(isAuthenticated)
{
if (isAuthenticated)
PageMethods.GetSecretMessage(getSecretMessageSuccess);
else
alert( “Log in failed” );
}
function loginFail()
{
alert( “Log in failed” );
}
function getSecretMessageSuccess(message)
{
$get(“spanMessage”).innerHTML = message;
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” EnablePageMethods=”true” runat=”server” />
<fieldset>
<legend>Login</legend>
<label for=”txtUserName”>User Name:</label>
<input id=”txtUserName” />
<br /><br />
<label for=”txtUserName”>Password:</label>
<input id=”txtPassword” type=”password” />
<br /><br />
<input id=”btnLogin” type=”button” value=”Login” />
</fieldset>
The secret message is:
<span id=”spanMessage”></span>
</form>
</body>
</html>

The page in Listing contains a simple Login form. When you click the Login button, the login() method executes and calls the following method:
Sys.Services.AuthenticationService.login
(
$get(“txtUserName”).value,
$get(“txtPassword”).value,
false,
null,
null,
loginSuccess,
loginFail
);

The AuthenticationService.login() method accepts the following parameters:
. userName—The username to authenticate.
. password—The password to authenticate.
. isPersistent—Determines whether a session or persistent cookie is created after successful authentication.
. customInfo—Not used.
. redirectUrl—The page to which the user is redirected after successful authentication.
. loginCompletedCallback—The method to call when the web service call completes.
. failedCallback—The method to call when the web service call fails.
. userContext—Additional information to pass to the loginCompletedCallback or failedCallback method.

If the web service call to the Authentication service is successful, the following method is called:

function loginSuccess(isAuthenticated)
{
if (isAuthenticated)
PageMethods.GetSecretMessage(getSecretMessageSuccess);
else
alert( “Log in failed” );
}

It is important to understand that this method is called both when the user is successfully authenticated and when the user is not successfully authenticated. This method is called when the Authentication web service call is successful. The first parameter passed to the method represents whether or not the user is successfully authenticated. If the user is authenticated, the secret message is grabbed from the server and displayed in a <span> element in the body of the page. Otherwise, a JavaScript alert is displayed that informs the user that the login was a failure.

Don’t ever put secret information in your JavaScript code. Anyone can always view all your JavaScript code. If you have secret information that you only want authenticated users to view, don’t retrieve the information from the server until the user is authenticated successfully. Notice that we were not required to create the Authentication web service. The Authentication web service is built in to the AJAX framework.

Using the Role Service
The second of the built-in application services included with ASP.NET AJAX is the Role service. This service enables you to retrieve the list of roles associated with the current user. For example, you might want to display different content and provide different functionality to different users depending on their roles. A member of the Administrators role can edit a record, but a member of the Public role can only view a record. To use the Role service, you need to make two configuration changes to your web configuration file. First, you need to enable the Role service. You can enable the Role service by adding the following <roleService> element to the <system.web.extensions> section of your web.config file:

<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled=”true”/>
<roleService enabled=”true”/>
</webServices>
</scripting>
</system.web.extensions>

Second, you need to enable ASP.NET roles. You enable roles by adding the following element to the <system.web> section of your web configuration file: <roleManager enabled=”true” /> After you make these configuration changes, roles are enabled for both the server and the client. You can create new roles, as well as associate the roles with users, by using the Website Administration Tool. Select the menu option Website, ASP.NET Configuration and then select the Security tab. Click the Create or Manage Roles link to create a new role and associate it with a user.

LISTING ShowRoles.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title>Show Roles</title>
<script type=”text/javascript”>
function pageLoad()
{
Sys.Services.AuthenticationService.login
(
“Steve”,
“secret#”,
false,
null,
null,
loginSuccess
);
}
function loginSuccess(isAuthenticated)
{
if (isAuthenticated)
loadRoles();
else
alert(“Log in failed!”);
}
function loadRoles()
{
Sys.Services.RoleService.load(loadRolesSuccess, loadRolesFail);
}
function loadRolesSuccess()
{
var isPlumber = Sys.Services.RoleService.isUserInRole(“Plumber”);
$get(“spanPlumber”).innerHTML = isPlumber;
var isPainter = Sys.Services.RoleService.isUserInRole(“Painter”);
$get(“spanPainter”).innerHTML = isPainter;
}
function loadRolesFail(error)
{
alert(“Could not load roles!”);
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:ScriptManager ID=”ScriptManager1” runat=”server” />
Is Plumber: <span id=”spanPlumber”></span>
<br /><br />
Is Painter: <span id=”spanPainter”></span>
</div>
</form>
</body>
</html>

In the pageLoad() method in Listing, the Authentication service is called to authenticate a user named Steve. When the Authentication web service call completes, and the user is authenticated successfully, the following loadRoles() method is called:

function loadRoles()
{
Sys.Services.RoleService.load(loadRolesSuccess, loadRolesFail);
}

The RoleService.load() method loads all the roles for the current user. The method accepts two parameters: the method to call if the web service call is successful, and the method to call if the web service call fails. If the RoleService.load() method completes successfully, the following method is called:

function loadRolesSuccess()
{
var isPlumber = Sys.Services.RoleService.isUserInRole(“Plumber”);
$get(“spanPlumber”).innerHTML = isPlumber;
var isPainter = Sys.Services.RoleService.isUserInRole(“Painter”);
$get(“spanPainter”).innerHTML = isPainter;
}

This method uses the RoleService.isUserInRole() method to detect whether the current user is a member of the Plumber and Painter roles. This information is displayed in two <span> tags contained in the body of the page. The RoleService class also includes a roles property. You can call Sys.Services.RoleService.get_roles() to get a list of all roles associated with the current user.

Using the Profile Service
The final built-in application service we need to discuss is the Profile service. The Profile service enables you to store information associated with a user across multiple visits to a web application. You can use the Profile service to store any type of information you need. For example, you can use the Profile service to store a user shopping cart. Before you use the Profile service, you must enable it for both the server and the client. On the server side, you need to enable and define the profile. The following <profile> element, which you should add to the <system.web> section of the web.config file, enables the Profile object and defines two properties named pageViews and backgroundColor:

<anonymousIdentification enabled=”true”/>
<profile enabled=”true”>
<properties>
<add name=”pageViews” type=”Int32” defaultValue=”0” allowAnonymous=”true” />
<add name=”backgroundColor” defaultValue=”yellow” allowAnonymous=”true” />
</properties>
</profile>

Notice the <anonymousIdentification> element. This element causes anonymous users to be tracked by a cookie. If you don’t include the <anonymousIdentification> element, only authenticated users can modify profile properties. Both the pageViews and backgroundColor properties include an allowAnonymous=”true” property so that anonymous users can modify these properties. If you want to restrict the profile to authenticated users, remove the <anonymousIdentification> element. In that case, you will need to use the Authentication service to authenticate a user before you modify the user’s profile properties. You must perform an additional configuration step on the server to enable an AJAX page to access a user profile. You must add a <profileService> element to the <system.web.extensions> section of the web configuration file, like this:

<system.web.extensions>
<scripting>
<webServices>
<profileService enabled=”true” readAccessProperties=”pageViews,backgroundColor”
writeAccessProperties=”pageViews” />
</webServices>
</scripting>
</system.web.extensions>

Now that we have the profile configured on the server, we can use it in an AJAX page. The page in Listing keeps count of the number of times a particular user has requested the page. Each time a user requests the page, the count increments by one and the total count is displayed in the body of the page.

LISTING ShowProfile.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html id=”html1” xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title>Show Profile</title>
<script type=”text/javascript”>
function pageLoad()
{
// Increment page views
Sys.Services.ProfileService.properties.pageViews ++;
// Save profile
Sys.Services.ProfileService.save([“pageViews”], saveSuccess);
// Show page views
$get(“spanPageViews”).innerHTML =
Sys.Services.ProfileService.properties.pageViews;
// Change background color
var backgroundColor = Sys.Services.ProfileService.properties
[“backgroundColor”];
$get(“html1”).style.backgroundColor = backgroundColor;
}
function saveSuccess(countOfPropertiesSaved)
{
Sys.Debug.trace(“Profile properties saved: “ + countOfPropertiesSaved);
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
<ProfileService LoadProperties=”pageViews,backgroundColor” />
</asp:ScriptManager>
Your total page views:
<span id=”spanPageViews”></span>
</div>
</form>
</body>
</html>

If you want to load profile properties after a page loads, you can use the Sys.Services. ProfileService.load() method to load them through a web service call. The pageLoad method increments the total page views with the following line of code:
Sys.Services.ProfileService.properties.pageViews ++;

Notice that you can access profile properties on the client just like you can on the server. Profile properties are automatically exposed through the Sys.Services. ProfileService.properties property. You even get Intellisense for the client-side profile properties. After the pageViews profile property is modified, it is saved back to the server with the following line of code:
Sys.Services.ProfileService.save([“pageViews”], saveSuccess);

The first parameter to the ProfileService.save() method represents a JavaScript array of profile property names to save back to the server. The second parameter is a method that gets called when the web service call completes.As an alternative to supplying a list of property names to the ProfileService.save()
method, you can pass the value null. When you pass the value null, all profile properties are saved. The saveSuccess() method gets called when the profile properties are saved to the server. This method looks like this:
function saveSuccess(countOfPropertiesSaved)
{
Sys.Debug.trace(“Profile properties saved: “ + countOfPropertiesSaved);
}

A count of the profile properties that got saved on the server successfully is passed to this method. The saveSuccess() method simply writes the count to the debug and trace console. After updating the total page views, the pageLoad method displays the page views in a <span> element in the body of the page. Finally, the pageLoad() method changes the background color of the page to the value of the profile backgroundColor property.

Creating Custom AJAX Controls and Behaviors
In this final part of this chapter, you learn how to create custom AJAX controls and behaviors. AJAX controls and behaviors are some of the most exciting features of the Microsoft AJAX Framework. Unfortunately, they are also some of the features that are least developedIn the Ajax world, an AJAX control is the equivalent of an ASP.NET control. However, whereas an ASP.NET control executes on the server and renders content to the browser, an AJAX control executes entirely in the browser. An AJAX control is built entirely from  JavaScript and DOM elements.

You can build an AJAX control that works with all major modern browsers, including Microsoft Internet Explorer, Mozilla Firefox, Opera, and Safari. However, browser compatibility is entirely up to you. The version of JavaScript supported by Firefox is different from the version of JavaScript supported by Internet Explorer. There are substantial differences in how the DOM is implemented in different browsers.When using Visual Web Developer, you create an AJAX control by selecting the menu option Website, Add New Item and selecting the AJAX Client Control template. When you create a new control, you start with the control skeleton in Listing

LISTING ClientControl.js
/// <reference name=”MicrosoftAjax.js”/>
Type.registerNamespace(“myControls”);
myControls.ClientControl = function(element)
{
myControls.ClientControl.initializeBase(this, [element]);
}
myControls.ClientControl.prototype =
{
initialize: function()
{
myControls.ClientControl.callBaseMethod(this, ‘initialize’);
// Add custom initialization here
},
dispose: function()
{
//Add custom dispose actions here
myControls.ClientControl.callBaseMethod(this, ‘dispose’);
}
}
myControls.ClientControl.registerClass(‘myControls.ClientControl’, Sys.UI.Control);
if (typeof(Sys) !== ‘undefined’) Sys.Application.notifyScriptLoaded();

Like the JavaScript classes we created earlier in this chapter, an AJAX control consists of a constructor function and a set of methods defined in the control’s prototype. If you look at the second-to-last line in the skeleton, you will notice that an AJAX control inherits from the base Sys.UI.Control class. For this reason, in the constructor function, it is important to call initializeBase() so that the base Sys.UI.Control class get initiated. Notice that a parameter is passed to the constructor function (and to the initializeBase() method). This parameter represents the DOM element with which the AJAX control is associated. You can use the base Sys.UI.Control class’s get_element() method to retrieve the DOM element within any of the control methods. The control has two methods: initialize() and dispose(). You use the initialize() method to build up the control’s DOM elements and wire up the control’s event handlers. You use the dispose() method to unwire the event handlers to prevent memory leaks. Let’s go ahead and create a really simple AJAX control: a Glow control. When you hover your mouse over the control, its background color changes to yellow. The Glow control is contained in Listing.

LISTING Glow.js
/// <reference name=”MicrosoftAjax.js”/>
Type.registerNamespace(“myControls”);
myControls.Glow = function(element)
{
myControls.Glow.initializeBase(this, [element]);
// initialize fields
this._text = “Glow Control”;
this._backgroundColor = “yellow”;
}
myControls.Glow.prototype =
{
initialize: function()
{
myControls.Glow.callBaseMethod(this, ‘initialize’);
// Wire-up delegates to element events
$addHandlers
(
this.get_element(),
{
mouseover: this._onMouseOver,
mouseout: this._onMouseOut
},
this
);
},
dispose: function()
{
// Unwire delegates
$clearHandlers(this.get_element());
myControls.Glow.callBaseMethod(this, ‘dispose’);
},
_onMouseOver: function()
{
this.get_element().style.backgroundColor = this._backgroundColor;
},
_onMouseOut: function()
{
this.get_element().style.backgroundColor = ““;
},
get_text: function()
{
return this._text;
},
set_text: function(value)
{
this._text = value;
this.get_element().innerHTML = value;
},
get_backgroundColor: function()
{
return this._backgroundColor;
},
set_backgroundColor: function(value)
{
this._backgroundColor = value;
}
}
myControls.Glow.registerClass(‘myControls.Glow’, Sys.UI.Control);
if (typeof(Sys) !== ‘undefined’) Sys.Application.notifyScriptLoaded();

A single parameter is passed to the Glow control’s constructor function. This parameter represents the DOM element to which the Glow control will be attached. In the Glow control’s constructor function, two private fields named _text and _backgroundColor are initialized with default values. If you don’t modify the control’s properties, the control contains the text “Glow Control” and changes its background color to yellow when you hover over it.

Launching a Client-Side Behavior from the Server
In the same way that you can launch a client-side control from the server, you can launch a client-side behavior from the server. A server-side control that launches a behavior is called an extender control. The extenders in the AJAX Control Toolkit ultimately derive from the ExtenderControl class. However, the Toolkit uses an intermediate base class named the ExtenderControlBase class. This intermediate class is included in the source code download of the AJAX Control Toolkit. You can create an extender control in one of two ways: You can implement the IExtenderControl interface, or you can derive a control from the base ExtenderControl class. The control in Listing illustrates how you can create a new extender control by inheriting from the ExtenderControl class.

LISTING HelpExtender.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Generic;
namespace MyControls
{
1767
33
Creating Custom AJAX Controls and Behaviors
[TargetControlType(typeof(TextBox))]
public class HelpExtender: ExtenderControl
{
private string _Text = “Help Text...”;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
protected override IEnumerable<ScriptReference> GetScriptReferences()
{
ScriptReference sref = new ScriptReference(“~/HelpBehavior.js”);
List<ScriptReference> colSRefs = new List<ScriptReference>();
colSRefs.Add(sref);
return colSRefs;
}
protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors
(
Control targetControl
)
{
ScriptControlDescriptor des = new ScriptControlDescriptor
(“myControls.HelpBehavior”, TargetControlID);
des.AddProperty(“text”, _Text);
List<ScriptDescriptor> colDes = new List<ScriptDescriptor>();
colDes.Add(des);
return colDes;
}
}
}

The extender control in Listing derives from the base ExtenderControl class. The ExtenderControl class is an abstract class. You must implement the following two methods:
GetScriptReferences()—Returns a list of JavaScript files required by the clientside behavior.
GetScriptDescriptors()—Returns a list of $create() shortcuts for instantiating the client-side behavior.

This attribute restricts the type of control to which you can apply the extender control. In particular, this attribute prevents you from applying the extender control to anything other than a TextBox control.Unfortunately, an extender control must be applied to a server-side control. You cannot use an HTML element as the target of an extender control. For this reason, when building pure client-side AJAX applications, I would stick with the server-side ScriptControl class as a launch vehicle for both client-side controls and behaviors.The page in Listing demonstrates how you can use the HelpExtender control to extend the functionality of two TextBox controls.

LISTING ShowHelpExtender.aspx
<%@ Page Language=”C#” %>
<%@ Register TagPrefix=”custom” Namespace=”MyControls” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title>Show Help Extender</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:ScriptManager ID=”ScriptManager1” runat=”server” />
<asp:Label id=”lblFirstName” Text=”First Name:” AssociatedControlID=”txtFirstName” Runat=”server” />
<asp:TextBox id=”txtFirstName” Runat=”server” />
<custom:HelpExtender id=”he1”TargetControlID=”txtFirstName” Text=”Enter your first name.”
Runat=”server” />
<br /><br />
<asp:Label id=”lblLastName” Text=”Last Name:” AssociatedControlID=”txtLastName”
Runat=”server” />
<asp:TextBox id=”txtLastName” Runat=”server” />
<custom:HelpExtender id=”he2” TargetControlID=”txtLastName” Text=”Enter your last name.”
Runat=”server” />
</div>
</form>
</body>
</html>

When you move focus between the two TextBox controls rendered by the page in Listing, the different help boxes appear.

No comments:

Post a Comment