Creating A Custom Widgets Library
The framework comes with a predefined set of widgets grouped in the
aria.widgets.AriaLib library available by default using the
@aria: prefix in templates. Those widgets may suit your needs very well, but in some cases you may need to develop your own ones.
This article explains how to create a new widget class, how to create the corresponding widget library and how to use it.
Basically, implementing a widget means creating a class which extends
aria.widgetLibs.BaseWidget and overriding some of its methods. In addition, some features provided by Aria Templates like CssTemplates, event delegation and data model listeners are very useful when developing a widget.
Here's a sample for a slider widget that uses all these concepts:
Widgets have a specific lifecycle (which is similar to the one of a template). Here are the main steps:
During a template refresh, for each widget which belongs to the section being refreshed, an instance of the corresponding widget class is created.
When the HTML markup of the whole section to be refreshed has been generated, it is inserted in the DOM.
Then the initialization method of the widget is called (
Finally, when there is a refresh of a section containing the widget (or a parent template), the widget is disposed.
The constructor of the widget is called with three parameters:
- the widget configuration (as defined in the template),
- the template context,
- the line number.
Calling the parent widget constructor is mandatory and important for it is needed to set the
_lineNumber internal properties (among other things.)
For a simple widget this is typically done like this:
If CSS templates are defined in the
$css section of the widget, the CSS manager is notified so that they are loaded before the markup is inserted in the DOM.
If the widget is intended to be a container, with an opening and a closing tag, both the
writeMarkupEnd methods have to be overriden. On the other hand, if the widget is intended to be used with a single tag, only the
writeMarkup method needs to be overridden.
These methods all accept an object of type
aria.templates.MarkupWriter as their only parameter. Their default implementation in the base class is to raise an error explaining that the widget does not support this usage.
Here are some methods or properties which are often used in the MarkupWriter:
write(): method used to output the HTML of the widget.
skipContent: if the widget is a container, setting this property to true in
writeMarkupBeginallows for skipping its content.
_createDynamicId(): protected method is used to generate an automatic id, which is useful when you need to access the widget's DOM elements. The id will be automatically freed when the widget is disposed (and will probably be reused later).
initWidget() method can be overridden if some action should be done after the markup of the widget has been inserted in the DOM. The default implementation of this method does nothing.
Custom widgets can use CSS templates, declared as
When processed those CSS templates will not be prefixed with a unique class id as standard CSS templates linked to HTML templates are. As a consequence, class names should be chosen carefully as they will apply to the whole page.
Here's an example of a simple widget:
Here's an example of a simple container widget:
aria.widgetLibs.BindableWidget extends from
aria.widgetLibs.BaseWidget and, in addition to that class, registers listeners on the data model for any widget's bind property.
This class provides also a method to transform a bound value to and from the widget.
The function that registers data model listeners is
_registerBindings(), you can call it in the widget's constructor or inside
initWidget depending on your needs.
When called, for every bound property, this function calls
setWidgetProperty() with the transformed value and register a listeners on the data model that is then removed when the widget is disposed.
Whenever the value changes in the data model, the protected method
_notifyDataChange() is called on the widget with information about the change.
You can override this function to implement your own logic to be executed when a bound value changes from outside the widget.
The following flow graph is a visual representation of what happens when a custom widget is used in a template
When the template is loaded, the widget's
constructor() is called, you can override this method to handle widget's configuration parameters or call
Then the framework calls
writeMarkupEnd() to get the widget's markup and put it in the DOM, once done it calls
This is where you can get DOM reference and call
_registerBindings() if you didn't call it in the
_registerBindings() is called,
setWidgetProperty() gets called with the name of the bound property and its value after a transform to widget.
Something similar to
_registerBindings() happens when the bound value changes in the data model, either because you set it in a template script or because of other automatic bindings.
When a value changes
_notifyDataChange() gets called with the name of the bound property and a description of the change.
The value received inside
_notifyDataChange() has not been transformed yet.
aria.html.Element extends from
aria.widgetLibs.BindableWidget and is the base class for creating a single html tag element.
BindableWidget it has data model bindings, but it also handles event delegation of events defined in
Let's imagine to create a widget extending from
Element class takes care of
Whenever a bound value changes the widget's public method
onbind() is called with the name of the changed property and its new and old value, this signature is simpler to use than
Moreover every event in
on statement is delegated and the associated callback will be called when the event happens.
The constructor of
aria.html.Element has the same signature as
The configuration object
cfg is normalized against the bean
aria.html.beans.ElementCfg. This bean defines the properties
tagName: Qualified name of the Element node generated by the widget
attributes: List of allowed html attributes
bind: List of properties that can be bound to this widget
on: List of registered events and their callbacks
aria.html.Element generates a tag element of type
tagName with the specified attributes, for instance
generates the following markup
Widgets extending from
aria.html.Element can optionally override
this.$bean before calling the parent constructor to specify their own configuration bean in case they want to provide validation of configuration parameters.
The following is the flow graph of
It is similar to
aria.widgetLibs.BindableWidget but in this case when a value bound to the data model is changed,
onbind is called.
onbind has a different signature than
_notifyDataChange, it is called with the name of the changed property, its current value after transform and the value before change.
Every event raised by the user (mouse, keyboard, ...) is handled by
_delegate function that calls the right callback for that event. Normally you don't need to override this function.
A custom library is a singleton class which extends
aria.widgetLibs.WidgetLib and specifies for each widget the classpath of the class which implements its behavior.
It typical library looks like this (example defining two widgets):
To use a specific widget library in a template, you need to declare its classpath and an alias in the template's
$wlibs section. This alias can then be used as a prefix in the widget statement, as shown in the example below:
If the widget library is supposed to be used in the whole application, it is possible to declare it as a global library in the application environment rather than referencing it in each template. Note that, in this case, the alias can still be overridden in a specific template (in the
The code below declares the
mycustomlib library in the environment in addition to the default
aria library, so that it is no longer necessary to declare mycustomlib in each template: