As we seen in my last post, declarative is very powerful. Let's now see how simple it is to use his own custom behavior.
First of all, we need to create a behavior. In this example, we will use a simple HoverBehavior. His goal is just to swap a CSS class when mouse is over a DOM element.
To create a behavior, you'll need to:
- Inherits from Sys.UI.Control
- Override initialize and dispose methods
- Implement your behavior logic
Our behavior is really simple, only one public property to define the hoverCssClass, the actual one is internally track into a private member.
Aurelien.UI.HoverBehavior.prototype = { _actualCssClass: '', // Private member used to track original css class
_hoverCssClass: '', // Private member for hoverCssClass Property
_hoverHandler: null, // Private member used to track hover handler
_blurHandler: null, // Private member used to track blur handler
initialize: Aurelien$UI$HoverBehavior$initialize, // Initialize override
dispose: Aurelien$UI$HoverBehavior$dispose, // Dispose overrive
_hover: Aurelien$UI$HoverBehavior$_hover, // Private method used to handle hover event
_blur: Aurelien$UI$HoverBehavior$_blur, // Private method used to handle blur event
get_hoverCssClass: Aurelien$UI$HoverBehavior$get_hoverCssClass, // hoverCssClass getter
set_hoverCssClass: Aurelien$UI$HoverBehavior$set_hoverCssClass // hoverCssClass setter
}
Aurelien.UI.HoverBehavior.registerClass('Aurelien.UI.HoverBehavior', Sys.UI.Control);
If you wonder why I'm using this strange "$" syntax in function name then you should have a look to Bertrand Leroy's post around JavaScript Stack Trace.
In the Initialize override, we define and attach event handlers for "mouseover" and "mouseout":
function Aurelien$UI$HoverBehavior$initialize() { if (arguments.length !== 0) throw Error.parameterCount();
if (this._hoverHandler == null) { this._hoverHandler = Function.createDelegate(this, this._hover);
$addHandler(this.get_element(), 'mouseover', this._hoverHandler);
}
if (this._blurHandler == null) { this._blurHandler = Function.createDelegate(this, this._blur);
$addHandler(this.get_element(), 'mouseout', this._blurHandler);
}
Aurelien.UI.HoverBehavior.callBaseMethod(this, 'initialize');
}
Don't forget to release them in the Dispose override:
function Aurelien$UI$HoverBehavior$dispose() { if (arguments.length !== 0) throw Error.parameterCount();
if (this._hoverHandler != null) { $removeHandler(this.get_element(), 'mouseover', this._hoverHandler);
this._hoverHandler = null;
}
if (this._blurHandler != null) { $removeHandler(this.get_element(), 'mouseout', this._blurHandler);
this._blurHandler = null;
}
Aurelien.UI.HoverBehavior.callBaseMethod(this, 'dispose');
}
Last part is around events handling. We track the actual CSS class and swap to hoverCssClass in the "mouseover" event handler to set it back through "mouseout" one:
function Aurelien$UI$HoverBehavior$_hover(eventElement) { this._actualCssClass = this.get_element().className;
this.get_element().className = this.get_hoverCssClass();
}
function Aurelien$UI$HoverBehavior$_blur(eventElement) { this.get_element().className = this._actualCssClass;
}
Let's now take a look to the declarative part of this example.
We first need some CSS classes:
.HoverCountry
{ color: Red;
}
.HoverCity
{ color: Blue;
}
To use our own behavior, we need to include our JavaScript file through ScriptManager:
<asp:ScriptManager ID="ScriptManager1" runat="server" EnableHistory="false" EnablePageMethods="false"
EnablePartialRendering="false" EnableSecureHistoryState="false">
<Scripts>
<asp:ScriptReference Path="~/AjaxPreview2/MicrosoftAjaxTemplates.debug.js" />
<asp:ScriptReference Path="~/Aurelien.UI.HoverBehavior.debug.js" />
</Scripts>
</asp:ScriptManager>
We also need to declare a namespace on the <body> tag:
<body xmlns:sys="javascript:Sys"
xmlns:dataview="javascript:Sys.UI.DataView"
xmlns:hover="javascript:Aurelien.UI.HoverBehavior"
sys:activate="dataview1">
We're now ready to use our behavior declaratively in our template:
<div id="dataview1" class="sys-template" sys:attach="dataview" dataview:data="{{ arrayCountries }}"> <ul>
<li sys:attach="hover" hover:hovercssclass="HoverCountry">{{ CountryName }} <ul class="sys-template" sys:attach="dataview" dataview:data="{{ Cities }}"> <li sys:attach="hover" hover:hovercssclass="HoverCity">{{ CityName }}</li> </ul>
</li>
</ul>
</div>
And here's the result:
Tips:
As you can see in the template, the behavior property is lowercased.
Actually, if you want to use hover:hoverCssClass as defined in the behavior, don't use the .debug.js version of the preview because it will throw an InvalidOperationException: "Invalid attribute name 'hoverCssClass'. Declared attribute names must be in lowercase".
Seem's strange? Actually not. For me, there is two reasons for that. First one is technical, if you look at the JavaScript code, all the string comparison around object/method properties are in lowercase. The second reason, for me, is that the Framework ensure the XHTML Compliance Rule: Attributes names should be in lowercase.
Exception is not thrown with the release version of the JavaScript file because compacted version doesn't contains exceptions handling.