Archive for August, 2008

August 10th 2008

Properly using the “references” parameter to the MS Ajax $create statement

Suppose you want to reference a component from another component. The MS Ajax documentation gives the following explanation of the $create function:

Syntax

$create(type, properties, events, references, element);

Arguments

Parameter Description
type The type of the component to create.
properties (Optional) A JSON object that describes the properties and their values.
events (Optional) A JSON object that describes the events and their handlers.
references (Optional) A JSON object that describes the properties that are references to other components.
element (Optional) The DOM element that the component must be attached to.

The documentation doesn’t provide much information, however, on why you should use the references parameter instead of just using the $find method in the initialize event of your component.

When the framework renders $create statements through the scriptmanager,  each script descriptor becomes a $create statement in the page source. These statements are executed by the ajax framework when page_load. In order for a component instance to reference another component instance, the object being referenced has to exist already.  If the $create statement for the object being referenced comes after the referencing component’s $create, an error will occur when the $find method is called.

One way around this is to execute $find every time that the component needs to be referenced. This has performance implications and doesn’t scale well when dealing with collections of components.

Another way around this is to use the MS Ajax Application_load event using code similar to the following:

MyNamespace.MyComponent.prototype = {
 initialize: function() {
  MyNamespace.MyComponent.callBaseMethod(this, 'initialize');

  this._applicationLoadHandler = Function.createDelegate( this, this.applicationLoad );
  Sys.Application.add_load( this._applicationLoadHandler );
  
 },

 applicationLoad: function(sender, e) {
 
  this._componentReference = $find(this._componentReferenceId);
  //Remove the LoadHandler 
  Sys.Application.remove_load( this._applicationLoadHandler );
  this._applicationLoadHandler = null;
 },
 
 set_ComponentReferenceId : function( value ) {
  this._componentReferenceId = value;
 },
  
 get_ComponentReferenceId : function() {
  return this._componentReferenceId;
 }
 // other code
}

This approach is explicit and therefore more clear to other developers who might inherit your code. Additionally for collections of components, this method is an excellent option.

The framework does, however, does provide a way to ensure that when the initialize method is called, the component references have already been set just like other component properties.

If our custom component inherits from AjaxControlToolkit.ExtenderControlBase, we can add the AjaxControlToolkit.ComponentReference attribute to the property declaration

<AjaxControlToolkit.RequiredProperty()> _
<AjaxControlToolkit.ExtenderControlProperty()> _
<IDReferenceProperty()> _
<AjaxControlToolkit.ComponentReference()> _
<AjaxControlToolkit.ClientPropertyName("ComponentReference")> _
Public Property ComponentReferenceId() As String
 Get
  Return GetPropertyValue(Of String)("ComponentReferenceId", String.Empty)
 End Get
 Set(ByVal value As String)
  SetPropertyValue(Of String)("ComponentReferenceId", value)
 End Set
End Property

Note: we also need to change the getter/setter methods in the javascript to:

set_ComponentReference : function( value ) {
  this._componentReference = value;
},
get_ComponentReference : function() {
  return this._componentReference;
}

Before the initialize method is called the framework will now call set_ComponentReference and pass $find(value from the server side property) as the sole parameter.

If you’re not extending from AjaxControlToolkit.ExtenderControlBase, you can perform the same operation by using the SetComponent method in the GetScriptDescriptors method:

Protected Overrides Function GetScriptDescriptors(ByVal targetControl As System.Web.UI.Control) As System.Collections.Generic.IEnumerable(Of System.Web.UI.ScriptDescriptor)
 Dim descriptor As New ScriptBehaviorDescriptor("MyNamespace.MyComponent", targetControl.ClientID)
 descriptor.AddComponentProperty("ComponentReference",Me.ComponentReference)
 ...
 Return New ScriptDescriptor() {descriptor}
End Sub

In either event, the net result should be a statment similar to the following generated by the framework in the page source:

Sys.Application.add_init(function() {
    $create(MyNamespace.MyComponent, {…,”id”:”CustomComponentId”}, null, $find(“RefefencedBehaviorId”), $get(“targetControlId)”));
});

No Comments yet »

Next »