Download DOWNLOAD
Forums FORUMS
Blogs BLOGS
Forge FORGE
Help HELP
Marketplace MARKETPLACE
DotNetNuke Home
You are here >   Community > Blogs
Register  |  Login

DNN Blog

Nov 10

Posted by: Jon Henning
11/10/2008 

With the release of DotNetNuke 5.0 drawing near, I figured it would be good to provide an introduction as to how nice client-side module development can be with the integration of the Microsoft AJAX Framework and the ClientAPI 4.0. This mini-series of blog entries will cover the basics of how to get started by creating a sample module that

  • Utilizes client-side javascript objects to encapsulate our logic, easily allowing multiple instances of a module on a page without having our variables clash
  • Utilizes the Microsoft AJAX Framework to allow for variables to be initialized in our client-side objects
  • Utilizes the new ClientAPI ControlMethods to allow for communication of complex objects both to and from the server
  • Enables localized messages to be sent down to the client-side javascript
  • Enables client-side Inter-Module Communication (IMC)

If your new to javascript, you may be confused as to where to get started. As there are many frameworks out there, it can be overwhelming as to which one to choose. One of the advantages of using both the Microsoft AJAX Framework and the ClientAPI is they both have been written to allow for easy integration with the server-side logic, whereas other frameworks may have the ability to make AJAX calls, they do not have included ASP.NET server-side code to handle easy integration with your VB/C# code.

Another vital piece of information that you should understand is that javascript is more of an emulation (prototype) language than a real language. It has the ability to morph itself to do common programming constructs like objects, events, inheritance, enumerators, etc. However, it does not have these things out of the box. Thus, a framework like Microsoft’s, is nice since it will provide a formalized way in which objects get defined and inheritance happens. For more information how this has been formalized see this document.

Let’s assume you already have a sample module you are working on and it has a typical Edit Usercontrol associated called EditDNNAjaxModule1.ascx. A convention that I like to follow is to create my javascript file with the same name plus a .js extension (EditDNNAjaxModule1.ascx.js). This keeps the files near each other in the Solution Explorer.  Another assumption we will make is that we plan on creating a namespace for our controls using a company name which is a common practice in writing server-side modules that we will apply to the client. To do this, we simply declare our namespace at the top of the js file.

Type.registerNamespace('YourCompanyHere');

The next step is to define our class’s constructor. In javascript this is done by defining a function.

YourCompanyHere.EditDNNAjaxModule1 = function()
{
    //Call Base Method
    YourCompanyHere.EditDNNAjaxModule1.initializeBase(this);
    //Member Variables
    this._myprop1 = null;
    this._msgs = {};
    this._settings = {};
    this._updateButton = null;

    //create delegates (MAKE SURE YOU CLEAN UP IN dispose!)
    this._delegates = {
        _updateSuccessDelegate: Function.createDelegate(this, this._updateSuccess),
        _updateFailDelegate: Function.createDelegate(this, this._updateFail),
        _onLoadDelegate: Function.createDelegate(this, this._onLoad)
        };

    //Event Hookup
    Sys.Application.add_load(this._delegates._onLoadDelegate);
}

There are a couple things to note here. First, we are using a keyword this. If you are a C# developer this should be somewhat familiar, if your first language is VB then think of it as the same thing as Me. The first line makes a call to the constructor’s base class, ensuring its constructor gets initialized. This is similar to the keyword base for C# and MyBase in VB. Obviously, if we are calling a base class we must be inheriting from some class. One would expect to see which class we inherit from already, but, that is not possible in javascript for how it emulates inheritance. Our entire class must be processed before it gets defined, which is covered at the end of this entry.

In the constructor we define and initialize our class’s member variables (_settings and _myprop1). These variables’ scope is limited to each EditDNNAjaxModule1 instance that is created. A common convention that Microsoft uses is to prefix all variables and methods that should be private with an underscore. While this does not stop developers from accessing them from the outside, it does help with clarifying intent, not to mention Visual Studio Intellisense.

The next thing to note is that we are creating delegates and assigning it to an member variable. The _delegates variable is actually serving as a dictionary object.  Its syntax for initializing it is json which bears much similarity to .NET object initializers. A delegate here, can be thought of in much the same way as delegates in .NET. The one difference however, is the need to assign it to a member variable. It is necessary to hold this so we can set it to null prior to the page unloading which will stop memory leaking from occurring in Internet Explorer. I will cover how this happens in a bit.

Finally, we are adding our delegate (event handler) to the Framework’s load event. While this code may not seem like much, it is important to understand that this event will fire once all components on the page have been initialized.

With our constructor done, it is not time to define the class’s methods. Javascript uses what it calls prototypes to extend objects. Those familiar with .NET’s extension methods will find some similarities here. The prototype syntax is as follows

YourCompanyHere.EditDNNAjaxModule1.prototype =
{
  //methods to extend our EditModule1 class
}

One unfortunate part of writing code to work with all browsers is that we have to code to the least common denominator. This is true when trying to associate code with our class’s properties. Ideally if our class had a property called selectedIndex we could set it like this

control.selectedIndex = 1;

However, if we needed to associate code whenever this property was set or read, there is no way to do it, so we need another convention for accessing properties. The Microsoft Framework does this by defining methods prefixed with get_ or set_.

get_myProp1: function() {return this._myprop1;},
set_myProp1: function(value) {this._myprop1 = value;},
get_settings: function() {return this._settings;},
set_settings: function(value) {this._settings = Sys.Serialization.JavaScriptSerializer.deserialize(value);},

This actually serves two purposes. First it helps to establish a common convention to make code look uniform. But the second is something we will cover in another entry in this blog series. It has to do with how we can initialize our properties from our server side ASP.NET code.

With our properties covered it’s not time to write our event handlers.

initialize: function() 
{
  //Call Base Method
  YourCompanyHere.EditDNNAjaxModule1.callBaseMethod(this, 'initialize');
  
  //hookup event handlers
  this._updateButton = $get(this.get_ns() + 'cmdUpdate');
  $addHandlers(this._updateButton, {"click": this._onUpdate}, this);
},

_onLoad: function(src, args)
{
  this._updateControls();
},

_onUpdate: function(src, arg)
{
  this._displayWait(true); 
  this._updateSettings();
  dnn.xmlhttp.callControlMethod('YourCompanyHere.Modules.DNNAjaxModule1.EditDNNAjaxModule1.' + this.get_id(), 
      'UpdateSettings', {Settings:this._settings}, this._delegates._updateSuccessDelegate, this._delegates._updateFailDelegate);
},

The first event we have is an override on our base class’s initialize event. This event is fired the instant our object is initialized. It is common practice to hook up your object’s event handlers with any DOM elements.  The next event is the _onload we talked about earlier. At first glance you may wonder what the difference is between onload and initialize. Onload fires only after all objects on the page have been initialized. This is useful if we ever need to reference other objects on the page which we will cover in a later blog entry when we tackle client-side IMC. More information on the event lifecycle can be found here.

The final method we have is the event handler that gets triggered when the update link is pressed. As you may be able to surmise, the code will show a wait graphic, call an update to our settings object and invoke a callback to the server, using the new ClientAPI ControlMethod. Further detail into how this will hook into the server-side will be covered later in this blog series.

So far all our methods were internal event handlers. Here is an example of a normal method

getSetting: function(key, def)
{
  if (this._settings[key])
    return this._settings[key];
  return def;
},

This method allows the caller to retrieve the settings Dictionary’s value, or if not present a default passed in value.

The final method we wish to cover is the dispose method. Those familiar with the .NET IDisposable interface will be right at home here. Though one may be confused as to why we need this in a language such as javascript. It has to do with the dangers of memory leaks and delegates. We need to clean up all delegates attached to DOM elements. Typical code for this my look like this

dispose: function()
{
  $clearHandlers(this._updateButton);    
  this._updateButton = null;
  this._delegates = null;
  YourCompanyHere.EditDNNAjaxModule1.callBaseMethod(this, 'dispose');
}

Notice that we are using a $clearHandlers on the same object we did the $addHandlers. These methods exposed by the Microsoft Framework are covered in their documentation and outside the scope of this entry. For our purposes just note that we can attach event handlers and remove them easily.

The final portion of code we will cover is the defining of our class. As mentioned earlier we need the entire class defined before we can extend it with our new entries. The Microsoft Framework has defined the following convention to the defining of classes along with inheritance

YourCompanyHere.EditDNNAjaxModule1.registerClass('YourCompanyHere.EditDNNAjaxModule1', Sys.Component);

As you can see we are inheriting from one of the objects defined in the Sys namespace (which is similar to System in .NET land). I will cover more of the specifics on why I chose this class in an upcoming blog. For those of you interested in reading more this site provides some useful information.

One final note is that I will be soon releasing the next version of my DotNetNuke AJAX Module Templates to codeplex. The current version has a bunch of requirements that will be removed that deter developers from getting up and running easily. There is also a video available there covering much of the same content found here, though it is slightly dated. Look for the update soon at http://www.codeplex.com/codeendeavortemplate

Tags:

3 comment(s) so far...

Re: Utilizing the Microsoft AJAX Framework and ClientAPI to Develop Rich Modules: Part I

Excellent!

Really do appreciate the documentation.

Regards, Burl

By Burldo on   11/10/2008

Re: Utilizing the Microsoft AJAX Framework and ClientAPI to Develop Rich Modules: Part I

Jon, I am a bit confused, is the field _myprop1 missing inside the constructor? Or is that not needed?

By Stefan Cullmann on   11/11/2008

Re: Utilizing the Microsoft AJAX Framework and ClientAPI to Develop Rich Modules: Part I

Stephen, Good point. It was an oversight on my part. Blog has been updated. An interesting side note is that it would have still worked as you can define vars on the fly, but this is not good practice. Thanks!

By Jon Henning on   11/11/2008

Networks

Follow DNNCorp on Twitter

LinkedIn

Follow us on Twitter @DNNCorp or join the DotNetNuke Community on LinkedIn

Sponsors

DotNetNuke®, DNN®, and the DotNetNuke logo are trademarks of DotNetNuke Corporation

Hosted by MaximumASP