Create Client Controls in ASP.NET 2.0 AJAX 1.0 Extensions
page 3 of 9
by Bilal Haidar
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 40170/ 60

Creating the Client Grid Control

The main purpose of the grid client control is to list the data we have inside the database. This means we need a property that holds the data for the grid, mainly a data source property. In addition, there will be two main events for this simple grid; one is fired when the user clicks on the edit button and another when the user clicks on the delete button. Moreover, a render method is needed to render the UI of the grid. We will go through a step by step process in showing how to develop this grid client control.

To start with, you need to create a new JavaScript file and register the namespace that will hold the control under development.

Listing 1

Type.registerNamespace("Bhaidar.AJAXControls");

The above statement registers a new namespace called Bhaidar.AJAXControls. The namespace is used mainly to logically organize the controls and prevent collisions between the different classes that might for one reason or another be the same.

Once the namespace is registered, it is time to add the main constructor of the control. The constructor is simply a function that takes as input the DOM element on the page that this control we are developing binds to. In other words, when the current client control renders its content, it will render them inside the DOM element that this client control is bound to.

Listing 2

Bhaidar.AJAXControls.ContactControl= function(element) 
{
  Bhaidar.AJAXControls.ContactControl.initializeBase(this, [element]);
  this._dataSource= null;
  this._deleteDelegate = null;
  this._editDelegate = null;
}

The above code shows the constructor for the ContactControl grid that takes as input a DOM element. The first statement in the constructor is a call to the constructor of the base class passing in the DOM element. This is a very important statement that you should always include since it fires the constructor of the base class, in this case Sys.UI.Control class, and makes all the properties and functions defined on the base class available in the current control.

Next, the code defines the data source property that will hold the data to be rendered inside the grid control. Two delegates are defined, one for the edit and another for the delete events. As in developing custom controls in ASP.NET, delegates hold references to event handler methods that will fire when a certain event is fired. In addition, when creating delegates in AJAX 1.0 extensions, we make sure that the event handler is fired in the same context of the class that fired the event handler.

Defining the methods and properties on a control or class requires using the prototype design pattern. This means adding any property or method to the prototype of the constructor of the class, meaning these properties and methods will be defined on the class itself.

Listing 3

initialize: function()
{
  Bhaidar.AJAXControls.ContactControl.callBaseMethod(this, 'initialize');
 
  if (this._editDelegate == null)
  {
    this._editDelegate = Function.createDelegate(thisthis._editHandler);
  }
 
  if (this._deleteDelegate == null)
  {
    this._deleteDelegate = Function.createDelegate(thisthis._deleteHandler);
  }
}
,

The first function to define is the initialize function. This function is inherited from the Sys.Component class which is the base class for the Sys.UI.Control class. As a developer you use this function to initialize any property or event. If it happens you define the initialize function make sure to call the base initialize method. This way, you make sure any initializing code in the base class will be done at this moment before initializing anything in your client control.

Then you define the two event delegates using the Function.createDelegate function that takes as input the event handler method and the class on which the event handler method will use a context to fire in.

Another major function to override is the dispose function.

Listing 4

dispose: function() 
{
  $clearHandlers(this.get_element());
  Bhaidar.AJAXControls.ContactControl.callBaseMethod(this, 'dispose');
},

You usually call the dispose function to clear any handlers that have been added on or inside the DOM element that the control is bound to. Once you have overridden this function, it is a must to call the base dispose method that is defined on the Sys.Component class. The base dispose method will release all resources define on the control and unregisters the control from the application. You will see how effective this method is when it comes to closing a displayed control on the page later on in this article.

Listing 5

get_dataSource: function()
{
  return this._dataSource;
}
 
, set_dataSource: function(value)
{
  this._dataSource = value;
}
,     

The above code defines the data source property. Notice that in AJAX client side, you define the set and get separately for a property. There is nothing that provides developers from directly accessing the private members, however, there is a kind of agreement among developers to access properties whenever there is a need to change or get the value of a private member.

The get function takes no input and simply returns the value of the private member _dataSource. The set method takes as input a value that will be set to the _dataSource members. Later on you will see that we will be binding a generic collection to this property. This way, we will be able to access any element inside the data source by using an index representing the element number in the collection.

As mentioned above, the grid control will expose two events, the edit and delete events. We will cover one of the events and the other one will be the same.

Listing 6

add_deleteRecord: function(handler)
{
  this.get_events().addHandler('deleteRecord', handler);
}
 
, remove_deleteRecord: function(handler)
{
  this.get_events().removeHandler('deleteRecord', handler);
}
,

The above code shows how to define events on a client control. You need to define two functions, one prefixed with add and one with remove. The add_deleteRecord function will be called whenever the $addHandler method is called or when the event handlers are assigned inside the $create function. We will see later in this article how the events are assigned in more details. But the bottom line here is to recognize that the event name is deleteRecord, this is the event exposed on the control. The add and remove functions will be called whenever we are assigning or removing an event handler to or from the control. The get_events() function, which is part of the Sys.Component class, returns a collection of type EventHandlerList that is a dictionary of client events defined on the control where the event plays the role of the key and the event handler is the value.

As you can see in the above code, the addHandler function simply adds a new mapping or record to the dictionary which binds the deleteRecord event to the handler parameter, which is nothing but a reference to a function to execute when the deleteRecord event is fired.

Whenever, the $removeHandler function is called, the remove_deleteRecord function is called, which removes a record from the dictionary representing the mapping between the deleteRecord event and the handler previously assigned to it.

When exposing an event to be utilized by the page developers, there is a missing function that you need to include, which is the internal event handler. This function is the one used when creating the delegate as you have seen in the initialize function above. This means, when the deleteRecord event is fired, the function handler defined inside the client control will be first executed. If the event is to be handled internally, i.e. inside the client control, then the internal function handler will execute and the event is now considered handled and finished. However, if the event is exposed and to be handled by the page developer, then this internal function will simply retrieve the handler mapped to the event from the EventHandlerList and fires the event handler attached to the event. In this case, we need the page developer to handler the deleteRecord, so the delete internal event handler is as follows.

Listing 7

_deleteHandler: function(event, args) 
{
  var h = this.get_events().getHandler('deleteRecord');
  if (h) 
  h(this, args);
},

The function above uses the getHandler function defined on the EventHandlerList class to retrieve the event handler function mapped into the deleteRecord event. Then the code checks, if there is a mapped event handler then fire it passing as two parameters the sender which is the current class and empty event args. Later on you will see how to fire an event handler and pass to it some data in the args parameter.

Binding the grid and rendering the UI requires the presence of a data bind method that can be called from the host page to render the grid.

Listing 8

dataBind: function() 
{
  this.renderControl();
},

The above function is called from the host page to render the grid control. Usually, we will set the value of the data source then call the data bind function to render the control UI.

The renderControl function will simply create the structure of the grid control. The code is very simple and we will not go into its details; you can simply check the code that accompanies this article. However, we will cover some important code statements inside the renderControl function that are worth mentioning.

To attach a CSS class to a DOM Element you use the following statement.

Listing 9

Sys.UI.DomElement.addCssClass(table, 'Professional');

The above code attaches a CSS class called Professional to the table DOM element using the addCssClass function defines on the Sys.UI.DOMElement class.

To attach some CSS inside a style element you use the following line of code.

Listing 10

table.style.cssText = "width:500px;border-collapse:collapse;";

The above code attaches some CSS tags to the style attribute of the table DOM element.

To attach an event handler to an event on a DOM element you follow the sample code below.

Listing 11

var img = document.createElement('img');
img.setAttribute("src", "App_Themes/Default/images/edit.gif");
img.setAttribute("border", "0");
img.setAttribute("alt""Edit Record");
img.style.cssText = "cursor:hand;";
cell.appendChild(img);
$addHandler(img, 'click', Function.createCallback(this._editDelegate, this._dataSource[i]));

First of all, the code creates a new DOM element representing the img HTML tag then uses the shorthand method $addHandler to attach an event handler to the click event defined on the img DOM element.

Notice that the code uses Function.createCallback(delegateName, args). This is the way to fire an event handler and pass in some data to the args parameter. So now the delegate will be fired, thus firing the method attached to it which is the internal event handler and passing to the args parameter whatever data you send in the createCallback function.

In the code above you can see that we have passed directly the ith element of the data source as an argument. This is ok; however, we can also create a custom EventArgs class and pass it in the args parameter.

The custom EventArgs class is very simple.

Listing 12

Type.registerNamespace("Bhaidar.AJAXComponents");
Bhaidar.AJAXComponents.ContactEventArgs = function(data)
{
  Bhaidar.AJAXComponents.ContactEventArgs.initializeBase(this);
  this._data = data;
}
 
Bhaidar.AJAXComponents.ContactEventArgs.prototype =
{
  get_data: function()
  {
    return this._data;
  }
  , set_data: function(value)
  {
    this._data = value;
  }
}
 
Bhaidar.AJAXComponents.ContactEventArgs.registerClass
  ('Bhaidar.AJAXComponents.ContactEventArgs', Sys.EventArgs);
Sys.Application.notifyScriptLoaded();

The class inherits from the Sys.EventArgs class and adds a single property which is data. This class can be used as a generic class to pass in data to the event handler function. So the data property can contain any kind of data from string, integer values to object references. Rewriting the above code looks like listing 13.

Listing 13

var contactEventArgs = new Bhaidar.AJAXComponents.ContactEventArgs(this._dataSource[i]);
$addHandler(img, 'click', Function.createCallback(this._editDelegate, contactEventArgs));

Look how elegant the code is and resembles the code we usually write when working on the server side.

If on the other hand you simply wanted to fire the event handler without sending any augments, then you simply use the following line of code.

Listing 14

$addHandler(img, 'click', this._editDelegate);

Finally, we need to register the control and notify the ScriptManager that the client control class finished.

Listing 15

Bhaidar.AJAXControls.ContactControl.registerClass
   ('Bhaidar.AJAXControls.ContactControl', Sys.UI.Control);
Sys.Application.notifyScriptLoaded();

View Entire Article

User Comments

Title: gsdfg   
Name: nikesh
Date: 2009-12-24 6:21:47 AM
Comment:
not bad
Title: Mr   
Name: Malcolm Swaine
Date: 2008-07-09 6:24:45 PM
Comment:
"A control is already associated with the element." - try removing the Ajax Functionality and this goes away ... quick workaround
Title: edit data error   
Name: jfliuyun
Date: 2007-06-21 4:26:44 PM
Comment:
when first click editimage everything is ok,but before you submit the data ,you click editimage throw error "A control is already associated with the element." can you tell me what happened,and how to result it,thanks
Title: Yes it does!   
Name: Muhammad Mosa
Date: 2007-06-21 8:10:15 AM
Comment:
Yes it does! at least you closed the door. I was thinking of another way and want to make sure there is no other way
Title: Re: Muhammad   
Name: Bilal Haidar [MVP]
Date: 2007-06-20 6:54:11 AM
Comment:
In AJAX 1.0 you only have two ways to interact with methods on the server:
1- WS methods
2- Page static methods

In the second option, you can place a static method on the page, inside it you can directly access your API the way you want. Does this help you?

Regards
Title: Bind The Control with a data layer   
Name: Muhammad Mosa
Date: 2007-06-20 3:56:54 AM
Comment:
No, this is not my target. sorry for this. what I mean I have Business Logic Rule Library. I want to use this library's methods to bind data to the AJAX control. Just as in your sample, but instead of using WS I want to use normal library
Title: Re: Bind The Control with a data layer   
Name: Bilal Haidar [MVP]
Date: 2007-06-19 3:47:13 PM
Comment:
I guess I didn't get your question right!

Do you mean you want to use WS not developed in .NET? Please correct me if I am wrong!

Thanks
Title: Bind The control with a data layer   
Name: Muhammad Mosa
Date: 2007-06-19 1:26:28 PM
Comment:
Well, we are on the same boat then. Ok in this sample, do you have any idea how can I bind the control to a data layer method that is not a WS method?!
Keep the good work brother
Title: Re: Carlito   
Name: Bilal Haidar [MVP]
Date: 2007-06-19 1:09:59 PM
Comment:
Carlito,
I will be checking on that and get back to you here on this article!
Regards
Title: Re: Muhammad Mosa   
Name: Bilal Haidar [MVP]
Date: 2007-06-19 1:07:59 PM
Comment:
Hello Muhammad,
We never use WS as a data layer. I can speak on myself, it is only for demonstration purpose that I embed the nasty DB work inside my WS. Just to save some time!
But in real projects, WS should only be an interface for your application and all your logic (BL and DAL) should be in a seperate API!!

Hope this answers your question!

Regards
Title: Mozilla compatibility   
Name: Carlito
Date: 2007-06-19 12:30:41 PM
Comment:
Hi, I was just trying your sample code which seems to work well with IE but not so well with Firefox.
It seems whenever I click on 'New Contact', the new contact sections shows for a second then disappears and the page reloads the contact list again...
Title: Nice By why always Web Services?!   
Name: Muhammad Mosa
Date: 2007-06-19 9:41:23 AM
Comment:
Wonderful as usual Bilal. I have one comment but it is not on the article it is general. Every time I read something about ASP.NET AJAX -well not everything- they just present Web Services as the data service to retrive data! While in most cases we don't use web services unless it is required. I mean I'm not going to use Web Service just to act as a data layer, I already have my data layer library!
In this I'm not going to use Web Service! you may suggest to use static web methods on the page itself, well is there are any other alternatives?!






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-18 8:58:20 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search