Classes and ASP.NET AJAX

In this second part of a two-part article focused on using ASP.NET AJAX, you'll learn about class inheritance, interfaces, and more. It is excerpted from chapter four of Programming ASP.NET AJAX, written by Christian Wenz (O'Reilly, 2007; ISBN: 0596514247). Copyright © 2007 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Contributed by
Rating: 4 stars4 stars4 stars4 stars4 stars / 7
June 19, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

Class Inheritance

As detailed in Chapter 2, the prototype property provides limited support for class inheritance in JavaScript. ASP.NET AJAX provides more abstraction. The prototypemechanism is supported for namespace classes that were registered usingClass name.registerClass(). As a second parameter forregisterClass(), you can specify a base class. Here is where you specify from which class the current class derives.

Derived classes

Let’s create a class that inherits from Software. One very specific type of software is a web browser, so let’s create a Browser class. In addition to the features of the genericSoftware class, a browser would benefit from some extra properties. AnisJavaScriptSupportedproperty can provide information about whether a particular browser is capable of running JavaScript.

  OReilly.Browser = function(name, vendor, isJavaScriptSupported) {
    //...
  }

Here’s how to register the class. Note how the new class (the string parameter) derives from the oldOReilly.Softwareclass (no string!).

  OReilly.Browser.registerClass("OReilly.Browser", OReilly.Software);

Of course, it would be possible to create getter and setter methods fornameandvendoronce again, and to write the constructor code as well. However, one of the benefits of class inheritance (actually, the major benefit) is that you can reuse functionality. BecauseOReilly.Browserinherits fromOReilly.Software, you can use the getter and setter methods (i.e., the properties) that are already there, as well as the_nameand_vendor“private” members. You do, however, need to add getter and setter methods and private members for the newisJavaScriptSupportedproperty, as shown here:

  var _isJavaScriptSupported = (isJavaScriptSupported != null)?
    isJavaScriptSupported : false;

  this.getIsJavaScriptSupported = function() {
   
return _isJavaScriptSupported;
  }
  this.setIsJavaScriptSupported = function(isJavaScriptSupported) {
    _isJavaScriptSupported = isJavaScriptSupported;
  }

All that remains is for us to write the constructor. But instead of writing it again from scratch, you can reuse the base class constructor. To do so, ASP.NET AJAX
provides theinitializeBase()method. The first parameter is the instance of which the base class will be initialized; usually, you providethisas the value. The second parameter is an array of arguments to be passed to the base constructor (the base constructor defines which arguments it expects). In our case, this array consists of the browser name and vendor.

  OReilly.Browser.initializeBase(this, new Array(name, vendor));

You can save a few characters and use JSON to create the array:

  OReilly.Browser.initializeBase(this, [name,vendor]);

Example 4-2 shows the code needed to create and use the new derived Browser class.

Example 4-2. Using ASP.NET AJAX class inheritance

ClientInheritance.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 id="Head1" runat="server">

  <title>ASP.NET AJAX</title>

  <script language="Javascript" type="text/javascript">
  function pageLoad() {
    var s = "";

    Type.registerNamespace("OReilly");
   
OReilly.Software = function(name, vendor) {
      var _name = (name != null) ? name : "unknown";
      var _vendor = (vendor != null) ? vendor : "unknown";

      this.getName = function() {
       
return _name;
      }
      this.setName = function(name) {
        _name = name;
      }

      this.getVendor = function() {
       
return _vendor;
      }
      this.setVendor = function(vendor) {
        _vendor = vendor;
     
}
    }
    Type.registerClass("OReilly.Software");

    OReilly.Browser = function(name, vendor, isJavaScriptSupported) {
   
OReilly.Browser.initializeBase(this, new Array(name, vendor));
      var _isJavaScriptSupported = (isJavaScriptSupported != null) ?
        isJavaScriptSupported : false;
 

      this.getIsJavaScriptSupported = function() {
       
return _isJavaScriptSupported;
      }
      this.setIsJavaScriptSupported = function(isJavaScriptSupported) {
       
_isJavaScriptSupported = isJavaScriptSupported;
      }

    }
    OReilly.Browser.registerClass("OReilly.Browser", OReilly.Software);
    var ie = new OReilly.Browser("Internet Explorer", "Microsoft", true);
    s = ie.getName() + " from " + ie.getVendor() +
      (ie.getIsJavaScriptSupported() ? " (w/ JS)" : " (w/o JS)") +
      " <br />";

    var lynx = new OReilly.Browser("Lynx");
    lynx.setIsJavaScriptSupported(false);
    s += lynx.getName() + " from " + lynx.getVendor() +

    (lynx.getIsJavaScriptSupported() ? " (w/ JS)" : " (w/o JS)");

    document.getElementById("output").innerHTML = s;
  }
  </script>

</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div id="output">
    </div>
  </form>
</body>
</html>

Figure 4-2 shows the results displayed when the page is loaded and its JavaScript runs.


Figure 4-2.  Instantiating objects derived from the same base class

Just in case you are wondering, the Lynx text browser does have a “vendor.” The copyright holder is the University of Kansas.

Accessing base methods

When we talk about class inheritance, a logical question is whether methods can be overridden in derived classes. The answer is yes. The next question: is there any way to access the equivalent method of the base class, (i.e., the overridden method)? Even better, the answer is again yes, ASP.NET AJAX allows you to do so. To demonstrate this, let’s add a toString() method to OReilly.Softwarethat outputs the product and vendor names stored by the class. Theprototypeproperty ensures automated inheritance and also helps demonstrate access to the base method later on.

  OReilly.Software.prototype.toString = function() {
   
return this.getName() + " from " + this.getVendor();
  }

You could also directly access the properties_nameand_vendoras variables. Using the getter methods is just a personal preference. There is no functional difference in doing so.

In theOReilly.Browserclass, you could write a similartoString()method:

  OReilly.Browser.prototype.toString = function(){
    return this.getName() + " from " + 
this.getVendor() +
           (this.getIsJavaScriptSupported() ? " (w/ JS)" : " (w/o JS)");
  }

However, it is once again advisable to reuse existing code, in this case, the base class’stoString()method. ASP.NET AJAX provides you withcallBaseMethod(), a helper method to call a method from the parent class that can take up to three parameters:

instance
  
The instance whose parent’s method to call (usually
   this)

methodName
  
The name of the method (as a string)

baseArguments
   Parameters for the method, if any (as an array)

In this case, thetoString()method ofOReilly.Browsercan be implemented as the following code demonstrates:

  OReilly.Browser.prototype.toString = function() {
    return OReilly.Browser.callBaseMethod(this, "toString") +
           (this.getIsJavaScriptSupported() ? " (w/ JS)" : " (w/o JS)");
  }

Now, the code to output the browser information can be reduced a bit to these commands below:

  var s = "";
  var ie = new OReilly.Browser("Internet Explorer", "Microsoft", true);
  s = ie.toString() + "<br />";

  var lynx = new OReilly.Browser("Lynx", null, false);
  s += lynx.toString();
  document.getElementById("output").innerHTML = s;

Example 4-3 shows the complete listing.

Example 4-3. Accessing a base class method

ClientBaseMethods.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 id="Head1" runat="server">

  <title>ASP.NET AJAX</title>

  <script language="Javascript" type="text/javascript">
  function pageLoad() {
    var s = "";

    Type.registerNamespace("OReilly");
   
OReilly.Software = function(name, vendor) {
      var _name = (name != null) ? name : "unknown";
      var _vendor = (vendor != null) ? vendor : "unknown";

      this.getName = function() {
       
return _name;
      }
      this.setName = function(name) {
        _name = name;
      }

      this.getVendor = function() {
       
return _vendor;
      }
      this.setVendor = function(vendor) {
        _vendor = vendor;
     
}
    }
    Type.registerClass("OReilly.Software");

    OReilly.Browser = function(name, vendor, isJavaScriptSupported) {
      OReilly.Browser.initializeBase(this, new Array(name, vendor));
      var _isJavaScriptSupported = (isJavaScriptSupported != null) ?

isJavaScriptSupported : false;
      this.getIsJavaScriptSupported = function() {
       
return _isJavaScriptSupported;
        }
        this.setIsJavaScriptSupported = function(isJavaScriptSupported) {
         
_isJavaScriptSupported = isJavaScriptSupported;
      } 
   
}
   OReilly.Browser.registerClass("OReilly.Browser", OReilly.Software);

    OReilly.Software.prototype.toString = function() {
      return this.getName() + " from " + this.getVendor();
      } 

     OReilly.Browser.prototype.toString = function() {
        return OReilly.Browser.callBaseMethod(this, "toString") + 
         (this.getIsJavaScriptSupported() ? " (w/ JS)" : " (w/o JS)");
    };

    var ie = new OReilly.Browser("Internet Explorer", "Microsoft", true);
    s = ie.toString() + "<br />";

    var lynx = new OReilly.Browser("Lynx", null, false);
    
s += lynx.toString();

    document.getElementById("output").innerHTML = s;
  }
 
</script>

</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div id="output">
    </div>
  </form>
</body>
</html>

Note that when you run this page, the output of this code is identical to that shown in Figure4-2.

Interfaces

 

The final OOP-like feature made available to JavaScript by ASP.NET AJAX is interfaces. An interface does not contain any implementation at all but instead specifies the members that subclasses must implement. Even if you inherit from an interface, there is no implementation you can use. Instead, you must create the methods that are defined in the interface. This is a good way for developers to keep class structure and implementation details separated in their code.

As you have probably already guessed, the method for creating an interface isType.registerInterface(). The interface name you just created is provided as the third (optional) parameter ofregisterClass(). So, starting with the interface itself, we will use the following code:

  OReilly.IProduct = function() {
   
this.toString = Function.abstractMethod;
  }
  Type.registerInterface("OReilly.IProduct");

Here,OReilly.Productis an abstract class. Unfortunately, the final version of ASP.NET AJAX does not support abstract classes (pre-release versions did). Therefore, there is no technical difference between abstract classes and regular classes.

In the following example, theOReilly.Productclass introduces and implements the propertiesnameandvendor.

  OReilly.Product = function(name, vendor) {
    var _name = (name != null) ? name : "unknown";
    var _vendor = (vendor != null) ? vendor : "unknown";

    this.getName = function() {
     
return _name;
    }
    this.setName = function(name) {
      _name = name;
    }
    this.getVendor = function() {
      return _vendor;
    }
    this.setVendor = function(vendor) {
     
_vendor = vendor;
   
}
  }
  Type.registerClass("OReilly.Product");

The next class to be implemented isOReilly.Software. Since we do not want to instantiate this class directly (we have subclasses likeOReilly.Browserfor that), this can now also be turned into an abstract class. It derives fromOReilly.Product(to getnameandvendor), but it also implementsOReilly.IProduct(for thetoString()method). After declaring the class, we register it with the following call toType.registerClass():

  OReilly.Software.registerClass("OReilly.Software", OReilly.Product, 
  OReilly.IProduct);

The rest of the code remains unchanged. It is quite long, so you might consider putting it into an external .js file for legibility of the .aspx file. Example 4-4 shows the complete listing.

Example 4-4. Using interfaces to structure code

ClientInterface.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 id="Head1" runat="server"> 
  <title>ASP.NET AJAX</title>

  <script language="Javascript" type="text/javascript">
  function pageLoad() {
    var s = "";

    Type.registerNamespace("OReilly");

    OReilly.IProduct = function() {
     
this.toString = Function.abstractMethod;
    }
    Type.registerInterface("OReilly.IProduct");

    OReilly.Product = function(name, vendor) {
      var _name = (name != null) ? name : "unknown";
      var _vendor = (vendor != null) ? vendor : "unknown";

      this.getName = function() {
       
return _name;
      }
      this.setName = function(name) {
        _name = name;
      }
      this.getVendor = function() {
       
return _vendor;
      }
      this.setVendor = function(vendor) {
       
_vendor = vendor;
     
}
    }
    Type.registerClass("OReilly.Product");

    OReilly.Software = function(name, vendor) {
      var _name = (name != null) ? name : "unknown";
      var _vendor = (vendor != null) ? vendor : "unknown";
      this.getName = function() {
        return _name;
      }
      this.setName = function(name) {
       
_name = name;
      }
      this.getVendor = function() {
        return _vendor;
      }
      this.setVendor = function(vendor) {
        _vendor = vendor;
      }
    }
      OReilly.Software.registerClass("OReilly.Software", OReilly.Product, OReilly.IProduct)
    OReilly.Software.prototype.toString = function() {
      return this.getName() + " from " + this.getVendor();
    }

    OReilly.Browser = function(name, vendor, isJavaScriptSupported) {
      OReilly.Browser.initializeBase(this, new Array(name, vendor));
      var _isJavaScriptSupported = (isJavaScriptSupported != null) ? vendor : false;
      this.getIsJavaScriptSupported = function() {
       
return _isJavaScriptSupported;
      }
      this.setIsJavaScriptSupported = function(isJavaScriptSupported) {
        _isJavaScriptSupported = isJavaScriptSupported;
     
}
    }
    OReilly.Browser.registerClass("OReilly.Browser", OReilly.Software);
    OReilly.Browser.prototype.toString = function() {
     
return OReilly.Browser.callBaseMethod(this, "toString") +
             (this.getIsJavaScriptSupported() ? " (w/ JS)" : " (w/o JS)");
    }

    var ie = new OReilly.Browser("Internet Explorer", "Microsoft", true);
    s = ie.toString() + "<br />";
    var lynx = new OReilly.Browser("Lynx", null, false);
    s += lynx.toString();
    document.getElementById("output").innerHTML = s;
 
}
 
</script>
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div id="output">
    </div>
  </form>
</body>
</html>

Client Versions of .NET Classes

In addition to adding OOP-like features for JavaScript coding, ASP.NET AJAX achieves two goals through client class implementions that are analogs of some .NET classes:

  1. Functionality missing in JavaScript is provided as part of ASP.NET AJAX.
  2. .NET developers with little JavaScript experience can use some familiar elements in their code.

In my opinion, this is one of the areas where upcoming ASP.NET AJAX versions will most certainly add more features, so the following list of classes is neither exhaustive nor final. Two useful features that are already available areSys.StringBuilderand enumerations.

Sys.StringBuilder

One of the new features introduced in .NET 1.0 that really improved performance was the introduction of the StringBuilder class. The downside, however, is that applications are usually full of code such as that illustrated below:

  string s = "", t;
  while (){
    t = <value>;
    s += t;
 
}

The problem lies in the statements += t, which is equivalent tos = s + t. Whenever this code is executed, a copy ofsand a copy oftare created in memory, concatenated, then saved back intos. However, it’s inefficient to create a copy ofsto achieve these results. Therefore,StringBuilderuses an optimized algorithm for string concatenation.

In JavaScript, this approach does not have any measurable effect on memory (in fact, the implementation seems to be a tick slower than the standard approach). Then again, performance is not as critical an issue for client script as it is for server code. Nevertheless, for consistency with your server coding techniques, you can rely on your knowledge of .NET coding techniques and useStringBuilder on the client. Example 4-5 puts the StringBuilder class to work. It concatenates some strings to build an HTML chessboard.

Example 4-5. Using an ASP.NET AJAX StringBuilder

ClientStringBuilder.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 id="Head1" runat="server">
  <title>ASP.NET AJAX</title>

  <script language="Javascript" type="text/javascript">
 
window.onload = function() {
    var sb = new Sys.StringBuilder();
    for (var i = 8; i >= 1; i--) {
      for (var j = 97; j <= 104; j++) {
        sb.append(String.fromCharCode(j));
        sb.append(i);
        sb.append(" ");
      }
      sb.appendLine();
      sb.appendLine();
   
}
    document.getElementById("output").innerHTML = "<pre>" +
sb.tostring() + "</pre>";
 
}
  </script>
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div id="output"></div>
  </form>
</body>
</html>

The built-in JavaScript functionString.fromCharCode()converts an ASCII code to its associated character, so the innerforloop runs from"a"through"h". As Figure 4-3 reveals, the code in Example 4-7 creates a simple chessboard.


Figure 4-3.  A chessboard (with some potential)

Enumerations

 

Another .NET type that is emulated by ASP.NET AJAX for JavaScript is Enum. You can create a custom enumeration using the createEnum() method. The API for this changed quite a bit during the Atlas and ASP.NET AJAX development cycle. In its current form, you can create an enumeration as shown in the following listing, but you cannot iterate over it. You can create a namespace, if you wish to use one:

  Type.registerNamespace("ORA.MyEnums");

Then, create the enum object, assigning it an (empty) function:

  ORA.MyEnums.Ajax = function() {};

Next, define all values in the enumeration, using the syntax below:

  ORA.MyEnums.Ajax.prototype = {
    "Asynchronous": 0,
    "JavaScript": 1,
    "and": 2,
    "XML": 3
 
};

Finally, the enumeration needs to be registered:

  ORA.MyEnums.Ajax.registerEnum("ORA.MyEnums.Ajax");

Example 4-6 shows a complete example that creates the enumeration and then accesses it.

Example 4-6. Using an ASP.NET AJAX Enum

ClientEnum.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 id="Head1" runat="server">

  <title>ASP.NET AJAX</title>

  <script language="Javascript" type="text/javascript">
  function pageLoad() {
   
Type.registerNamespace("ORA.MyEnums");
    ORA.MyEnums.Ajax = function() {};
    ORA.MyEnums.Ajax.prototype = {
     
"Asynchronous": 0,
      "JavaScript": 1,
      "and": 2,
      "XML": 3
   
};
    ORA.MyEnums.Ajax.registerEnum("ORA.MyEnums.Ajax");

    document.getElementById("output").innerHTML +=
      ORA.MyEnums.Ajax.Asynchronous + " " +
      ORA.MyEnums.Ajax.JavaScript + " " +
      ORA.MyEnums.Ajax.and + " " +
      ORA.MyEnums.Ajax.XML;
 
}
  </script>

</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div id="output"></div>
  </form>
</body>
</html>

This code outputs the string"0 1 2 3 "(the keys for the enumeration entries) in the<div>element.

Enumerations are also used internally by ASP.NET AJAX to define mouse button values (the following code snippet has been edited and reformatted for clarity).

  Sys.UI.MouseButton = function() { };
  Sys.UI.MouseButton.prototype = {
    leftButton:0,
    middleButton:1,
    rightButton:2
 
};
  Sys.UI.MouseButton.registerEnum("Sys.UI.MouseButton");

Summary

The ASP.NET AJAX client script library implements several convenient features not present in standard JavaScript, including OOP-like functionality and client-side equivalents of .NET Framework features. These features can be used by any JavaScript programmer, without repercussions to ASP.NET or the server-side features of ASP.NET AJAX.

For Further Reading

http://www.kevlindev.com/tutorials/javascript/inheritance
   Online tutorial for JavaScript’s OOP capabilities

http://aspnetresources.com/blog/ms_ajax_cheat_sheets_batch2.aspx
  
“Cheat sheets” for ASP.NET AJAX’s JavaScript
   extensions

http://ajax.asp.net/docs/ClientReference/Global/default.aspx
   Documentation for helper functions and JavaScript
   base type extensions

http://quickstarts.asp.net/Futures/ajax/doc/cssselectors.aspx
   The ASP.NET AJAX Futures provides JavaScript helper
   functions to select elements based on CSS rules  

blog comments powered by Disqus
ASP.NET ARTICLES

- Implementing ASP.NET 4.0 Page.MetaDescriptio...
- ASP.Net Development Tips
- Intro to Sessions in ASP.Net
- Google Maps API Introduction in ASP.NET usin...
- Creating an ASP.NET 3.5 Gridview Image Galle...
- Encrypt QueryString in ASP.NET 3.5 using VB....
- ASP.NET 3.5 Drop Down List Controls
- Connect to Access Database with ASP.Net
- Secure Audio Streaming with ASP.Net and Flash
- Dynamic Sitemap and Navigation in ASP.Net
- Implement Gzip and Deflate Compression in AS...
- Run ASP.Net in Ubuntu with Apache
- ASP.Net Mono Website Contact Forms
- ASP.Net URL Rewriting Methods
- Murach`s ASP.NET 4 Web Programming with C# 2...

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 2 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials