With ASP.NET AJAX 1.0
By Ryan Keeter
Overview
With ASP.NET AJAX, and the libraries that were created revolving that framework, one has the ability to program within Javascript much the same way that they do in a compiled language like C# or VB .NET. Writing Javascript with the ASP.NET AJAX libraries allows a person to created hooked events that are similar to the server page lifecycle (just not as robust). One could create classes, enums, namespaces, all within Javascript, making JS a much more object oriented programming model.
Browser Page Life Cycle
Along with the libraries in the ASP. NET AJAX framework, there are hooked events that one could program against. A couple common events that are in the framework are the page Load events and the page Unload events. When the page is completely loaded and some script has been run, then the page load event will fire. If the page changes, closes applications, or an event otherwise, then the page unload event will fire. For example:
<script type="text/javascript">
function pageLoad(sender,args)
{
alert("Hello there!"); ///will show once the page officially loads
}
function pageUnload(sender, args)
{
alert("goodBye from " + sender.toString()); /// shows up after browser is closed
}
</script>
Namespaces in Javascript
Namespaces allow for logical gathering of code and classes. There are parts of namespaces that allow certain functionality that is not used all the time. For example, the system.web.services namespace in .NET will not be loaded unless it is specifically told to be loaded. This type of functionally can be created within Javascript if there are different files that have a namespace attributed to them, they will not be loaded until they are called, it is like a pay for play type of idea.
To register a namespace within Javascript use the keywords Type.registerNamespace:
<script type="text/javascript">
function pageLoad(sender, args)
{
Type.registerNamespace('Tutorial');
alert(Type.isNamespace(Tutorial));//displays True
}
</script>
The function Type.isNamespace returns a Boolean value of true, thus showing that the namespace is in fact registered and everything worked as it should. Adding namespaces in this fashion adds a large amount of a speed to a Javascript application because instead of having to search through the entire global object library, the Javascript engine only has to go to the namespace that the class or function that is being called lives. This allows for more organized code and also a swifter Javascript environment.
Classes
When creating classes, an excellent Javascript programmer will want to assign the classes within a namespace so as to not clutter the global object namespace. Let’s say that we want to create a namespace of Tutorial and a class of Chapter 4:
<script type="text/javascript">
function pageLoad(sender, args)
{
Type.registerNamespace('Tutorial.Chapter4');
}
</script>
When creating the above class of Chapter 4, we are also creating the Tutorial namespace at the same time. When doing this, AJAX already knows to create the namespace hierarchy and can understand that Chapter 4 is a child class of the namespace Tutorial.
If we wanted to add a constructor to the Chapter 4 class and then add some properties to the Chapter 4 class then we would assign a function (which is a primitive type, the same as string or Boolean) to the chapter 4 class. This would look like the below:
<script type="text/javascript">
function pageLoad(sender, args)
{
Type.registerNamespace('Tutorial.Chapter4');
Tutorial.Chapter4 = function(page,topic)
{
this._page = page;
this._topic = topic;
}
}
</script>
I have added a function to the Tutorial.Chapter4 class that will take two parameters, page and topic. Then within the function I will undeclare (not use the “var” keyword) two variables. You might be asking yourself though “if I use the variables in this manner, won’t the variables be global variables and as such will throw errors in the browsers for being global and undeclared?” Well actually, by using the this keyword, one is scoping the variable to the type of function. So the variable, though undeclared and act just like a global variable, will be scoped to the function and thus maintain themselves within the function and not throw an error. However, if the this keyword is not used, then the scope becomes global and this could lead to a hard problem to track down.
What good is a class though without the ability to retrieve the assignments of the setters. We passed in parameters to the constructor, so let’s now go into that class and access and create the getters. We will need to use the prototype keyword to add a function to the class (like in the previous tutorial):
<script type="text/javascript">
function pageLoad(sender, args)
{
Type.registerNamespace('Tutorial.Chapter4');
Tutorial.Chapter4 = function(page,topic)
{
this._page = page;
this._topic = topic;
}
Tutorial.Chapter4.prototype =
{
get_page : function() {
return this._page;
},
get_topic: function(){
return this._topic;
}
}
}
</script>
What I have done here is use the prototype keyword on our Chapter4 class creating function extensions that will return the page and topic variables that we set in the constructor. Finally, we need to call the registerClass function on this class to finally register the class and the members so that it can be called. Let’s go ahead and do that now:
<script type="text/javascript">
function pageLoad(sender, args)
{
Type.registerNamespace('Tutorial.Chapter4');
Tutorial.Chapter4 = function(page,topic)
{
this._page = page;
this._topic = topic;
}
Tutorial.Chapter4.prototype =
{
get_page : function() {
return this._page;
},
get_topic: function(){
return this._topic;
}
}
Tutorial.Chapter4.registerClass('Tutorial.Chapter4');
var chp4 = new Tutorial.Chapter4(72,"OOP in Javascript");
alert(chp4.get_page()); //Returns 72
}
</script>
We have registered the class Tutorial.Chapter4 and we then new it up with a variable and then throw some variables at the constructor and then retrieve them by called a method on the class (“get_page”).
Inheritance
With most object oriented principles, we need to have some methods of adding, or extending classes through inheritance. When adding inheritance to an existing class, there is a keyword that must be called, and that keyword is initializebase. If we wanted to add another type to the Chapter4 object called author, then we would need to create that class, assign a function constructor passing in the same variables as the Chapter4 object (page, topic), but also pass in the parameter of author. I think we need to see some code to best explain this:
<script type="text/javascript">
function pageLoad(sender, args)
{
Type.registerNamespace('Tutorial.Chapter4');
Tutorial.Chapter4 = function(page,topic)
{
this._page = page;
this._topic = topic;
}
Tutorial.Chapter4.prototype =
{
get_page : function() {
return this._page;
},
get_topic: function(){
return this._topic;
}
}
Tutorial.Chapter4.registerClass('Tutorial.Chapter4');
Tutorial.Author = function(page, topic,author){
Tutorial.Author.initializeBase(this, [page,topic]);
this._author = author;
}
}
</script>
When calling intializebase we need to pass in the type (using this) and then an array of parameters we designated in the constructor (page,topic). We then need to register the class Author and all the inheritance semantics will be taken care of. I will also alert some stuff so that you can all see how the checking for inheritance methods are built into the ASP. NET AJAX Framework.
<script type="text/javascript">
function pageLoad(sender, args)
{
Type.registerNamespace('Tutorial.Chapter4');
Tutorial.Chapter4 = function(page,topic)
{
this._page = page;
this._topic = topic;
}
Tutorial.Chapter4.prototype =
{
get_page : function() {
return this._page;
},
get_topic: function(){
return this._topic;
}
}
Tutorial.Chapter4.registerClass('Tutorial.Chapter4');
Tutorial.Author = function(page, topic,author){
Tutorial.Author.initializeBase(this, [page,topic]);
this._author = author;
}
Tutorial.Author.prototype =
{
get_author: function()
{
return this._author;
},
set_author: function(author){
this._author = author;
}
}
//First parameter is the new class to be registered, and the second parameter is the class that is being extended
Tutorial.Author.registerClass('Tutorial.Author',Tutorial.Chapter4);
var chapter4 = new Tutorial.Author(72, "OOP Principles", "Matt Gibbs");
alert(chapter4.get_topic());
alert(chapter4.get_author());
if(Tutorial.Author.inheritsFrom(Tutorial.Chapter4) === true)
alert("Author inherits from Chapter4");
}
</script>
Interfaces
Interfaces are a contract. Any class that implements an interface must subscribe to all constraints of the interface. There is not any support for interfaces in Javascript natively, but the ASP .NET 2.0 AJAX client libraries allow for Javascript class implementation of an interface. The interface has no actual function of its own, but it does create a contract that the class must implement all necessary portions that the interface says it will. Here is an example of an interface:
< script type="text/javascript">
function pageLoad(sender, args)
{
Type.registerNamespace('Tutorial.Chapter4');
Tutorial.Chapter4 = function(page,topic)
{
this._page = page;
this._topic = topic;
}
Tutorial.Chapter4.prototype =
{
get_page : function() {
return this._page;
},
get_topic: function(){
return this._topic;
}
}
Tutorial.Chapter4.registerClass('Tutorial.Chapter4');
Tutorial.Author = function(page, topic,author){
Tutorial.Author.initializeBase(this, [page,topic]);
this._author = author;
}
Tutorial.Author.prototype =
{
get_author: function()
{
return this._author;
},
set_author: function(author){
this._author = author;
},
get_chapters: function(){
return _chapters;
}
}
//First parameter is the new class to be registered, and the second parameter is the class that is being extended
Tutorial.Author.registerClass('Tutorial.Author',Tutorial.Chapter4);
var chapter4 = new Tutorial.Author(72, "OOP Principles", "Matt Gibbs");
alert(chapter4.get_topic());
alert(chapter4.get_author());
if(Tutorial.Author.inheritsFrom(Tutorial.Chapter4) === true)
alert("Author inherits from Chapter4");
//Setup the Interface
Tutorial.IChapterInfo = function()
{
throw Error.notImplemented();
}
Tutorial.IChapterInfo.prototype =
{
get_chapters: function() {
throw Error.notImplemented();
},
get_isbn: function(){
throw Error.notImplemented();
}
}
Tutorial.IChapterInfo.registerInterface('Tutorial.IChapterInfo');
}
</script>
As seen above, the interface is created and registered. Notice that the methods that are created off of the interface must all be overridden or else an error will be thrown, the base implementation of the methods on the interface must be overridden.
Now we will change the rest of our code to make sure that they are made to handle the contract the interface implements:
<script type="text/javascript">
function pageLoad(sender, args)
{
Type.registerNamespace('Tutorial.Chapter4');
Tutorial.Chapter4 = function(page,topic)
{
this._page = page;
this._topic = topic;
}
Tutorial.Chapter4.prototype =
{
get_page : function() {
return this._page;
},
get_topic: function(){
return this._topic;
}
}
Tutorial.Chapter4.registerClass('Tutorial.Chapter4');
Tutorial.Author = function(page, topic,author){
Tutorial.Author.initializeBase(this, [page,topic]);
this._author = author;
this._isbn = null;
this._chapterCount = 0;
this._chapters = null;
}
Tutorial.Author.prototype =
{
get_author: function()
{
return this._author;
},
set_author: function(author){
this._author = author;
},
get_chapters: function(){
return this._chapters;
},
set_chapters:function(chapters){
if(typeof(chapters) !== 'undefined' && chapters !== null){
this._chapterCount = chapters.length;
this._chapters = chapters;
}
this._chapters = chapters;
},
get_isbn:function(){
return this._isbn;
},
set_isbn:function(isbn){
this._isbn = isbn;
}
}
//First parameter is the new class to be registered, and the second parameter is the class that is being extended
Tutorial.Author.registerClass('Tutorial.Author',Tutorial.Chapter4, Tutorial.IChapterInfo);
//Setup the Interface
Tutorial.IChapterInfo = function()
{
throw Error.notImplemented();
}
Tutorial.IChapterInfo.prototype =
{
get_chapters: function() {
throw Error.notImplemented();
},
get_isbn: function(){
throw Error.notImplemented();
}
}
Tutorial.IChapterInfo.registerInterface('Tutorial.IChapterInfo');
}
</script>
From above, you can see that we have successfully implemented our interface with our inherited Authors class. Notice, when we registered the class, we registered the author class throwing three parameters. The first parameter specified the name of the class, then the type it was inheriting from, and then finally the interface, or the contract for which it was built with.
Now let’s test the interface to finish the whole thing:
<script type="text/javascript">
function pageLoad(sender, args)
{
Type.registerNamespace('Tutorial.Chapter4');
Tutorial.Chapter4 = function(page,topic)
{
this._page = page;
this._topic = topic;
}
Tutorial.Chapter4.prototype =
{
get_page : function() {
return this._page;
},
get_topic: function(){
return this._topic;
}
}
Tutorial.Chapter4.registerClass('Tutorial.Chapter4');
Tutorial.Author = function(page, topic,author){
Tutorial.Author.initializeBase(this, [page,topic]);
this._author = author;
this._isbn = null;
this._chapterCount = 0;
this._chapters = null;
}
Tutorial.Author.prototype =
{
get_author: function()
{
return this._author;
},
set_author: function(author){
this._author = author;
},
get_chapters: function(){
return this._chapters;
},
set_chapters:function(chapters){
if(typeof(chapters) !== 'undefined' && chapters !== null){
this._chapterCount = chapters.length;
this._chapters = chapters;
}
this._chapters = chapters;
},
get_isbn:function(){
return this._isbn;
},
set_isbn:function(isbn){
this._isbn = isbn;
}
}
//Setup the Interface
Tutorial.IChapterInfo = function()
{
throw Error.notImplemented();
}
Tutorial.IChapterInfo.prototype =
{
get_chapters: function() {
throw Error.notImplemented();
},
get_isbn: function(){
throw Error.notImplemented();
}
Tutorial.IChapterInfo.registerInterface('Tutorial.IChapterInfo');
//First parameter is the new class to be registered, and the second parameter is the class that is being extended
Tutorial.Author.registerClass('Tutorial.Author',Tutorial.Chapter4, Tutorial.IChapterInfo);
var typeCheck = new Tutorial.Author(88,"OOP","Dan Wahlin");
typeCheck.set_isbn("123-456-789");
var chapters = new Array(3);
chapters[0] = 'intro';
chapters[1] = 'Partial Page Updates';
chapters[2] = 'Intro to Javascript';
typeCheck.set_chapters(chapters);
alert(typeCheck.get_chapters() + "\n" + typeCheck.get_isbn() + "\n" + typeCheck.get_author() + "\n" + typeCheck.get_page() + "\n" + typeCheck.get_topic());
}
</script>
Conclusion
After all of that dynamic code that doesn’t get compiled until runtime to tell us if we have messed up, it is nice to open up VS2008 and jump back into some rich C# debugging. However, I know that by following these principles, I will be able to make an easy migration to Javascript when my job and project calls for it.
Attached File(s)
-
Bringing_.NET_principles_to_JavaScript.doc (71K)
Number of downloads: 169

Start a new topic
Add Reply



MultiQuote

| 


