Let's now turn our attention to the central dijit lifecycle
methods that _Widget
provides. As
you're about to see, _Widget
packs
a lot of power with only minimal effort required on your part. By
simply including it as the primary superclass ancestor in the
inheritance hierarchy, your subclass has immediate access to the
standard dijit lifecycle methods it provides, and you may override any
of these method stubs to produce custom behavior during the
construction and destruction of the dijit.
For example, _Widget
provides
stubs to override before a dijit appears on screen, immediately after
a dijit becomes visible, and when a dijit is just about to be
destroyed. Each of these choke points can be immensely valuable times
to synchronize with the server-side model, explicitly destroy objects
(so as to avoid well-known memory leaks), or do some tactical DOM
manipulation. Regardless of the particulars for each and every
situation, this is boilerplate that you don't have to write; it's
already in place, and you can use it if and when you need it.
To introduce what _Widget
offers, Example 12-1 shows a
simple class that defines a class inheriting from _Widget
and overriding the key methods
involved in construction and destruction to produce debugging messages
in the Firebug console. As you know from the last chapter, this file
would be named Foo.js, and would be located in a
directory named after the module—nothing more than a class mapped to a
namespace.
The key point to observe in this example is that you override
the inherited methods from _Widget
just like you would expect. Take a look, and then we'll review each of
these methods in more detail.
Example 12-1. Subclassing from _Widget
dojo.require("dijit._Widget"); dojo.addOnLoad(function( ) { dojo.declare( "dtdg.Foo", // the subclass dijit._Widget, // the superclass { /* Common construction methods in chronological order */ constructor : function( ) {console.log("constructor");}, postMixInProperties : function( ) {console.log("postMixInProperties") ;}, postCreate : function( ) {console.log("postCreate");}, /* Your clever logic goes here */ talk : function( ) {console.log("I'm alive!");}, /* Canonical destructor, implicitly called via destoryRecursive( ) */ uninitialize : function( ) {console.log("uninitialize");} } ); }); foo = new dtdg.Foo( ); foo.talk( ); foo.destroyRecursive( ); /* Calls uninitialize, among other things */
When you run that example, you should notice the following output in the Firebug console:
constructor postMixInProperties postCreate I'm alive! uninitialize
To come full circle to the discussion about the creation
pattern dojo.declare
provides
from back in Chapter 10,
here's how the _Widget
lifecycle
plugs in:
preamble(/*Object*/ params, /*DOMNode*/node) //precursor to constructor; can manipulate superclass constructor args constructor(/*Object*/ params, /*DOMNode*/node) // fire any superclass constructors // fire off any mixin constrctors // fire off the local class constructor, if provided postscript(/*Object*/ params, /*DOMNode*/node) //_Widget implements postscript to kick off the create method... _Widget.create(/*Object*/params, /*DOMNode*/node) _Widget.postMixInProperties( ) _Widget.buildRendering( ) _Widget.postCreate( )
The take away is two-fold:
_Widget
builds right on top of whatdojo.declare
already provides and hooks into thepostscript
method in order to fire off thecreate
method that systematically calls_Widget
specific lifecycle methods.A widget, as an ancestor of
_Widget
, is a bona fide JavaScript Function object. Sure, there's a lot of flare and pizzazz involved, but in the end, it comes right back to the basics.
A flattened version of the lifecycle follows along with a
short synopsis of what each _Widget
lifecycle method accomplishes.
It's flattened out and starts with preamble because it's quite
uncommon to override postscript
or the create
method yourself
(although you could if you wanted to devise
your own widget lifecycle methods instead of using the standard
ones). Expanded examples that more thoroughly cover each method
appear later in this chapter.
- preamble (originating from
dojo.declare
) Preamble
provides an opportunity to manipulate arguments beforeconstructor
receives them. If you overridepreamble
, know that the same arguments that would normally be passed toconstructor
are passed topreamble
and whateverpreamble
returns is what gets passed intoconstructor
. This method is somewhat of an advanced feature and used infrequently compared to other lifecycle methods such as, for example,postCreate
.- constructor (originating from
dojo.declare
) This is the first method that you can override to perform custom behavior during dijit construction. There are two particularly common operations that are performed in
constructor
. One is including the initialization of dijit properties that are not primitive types. (Recall from Chapter 10 that declaring a complex type like an object or list inline as an object property causes it to be shared by all object instances.) Another common operation is adding any additional properties that are relied upon by other lifecycle methods downstream.- postMixInProperties (originating
from
dijit._Widget
) This method is called just after Dojo has walked the inheritance hierarchy and mixed all of the ancestors into the class. Thus, the name postMixInProperties literally refers to the time at which all a widget's properties have been mixed into the particular object instance. Therefore, by the time this method executes, your class has full access to those inherited properties and can manipulate them before the dijit visibly appears on the screen. As we'll soon see in an example that illustrates dijits that derive from a template, this method is typically the place where you'll modify or derive placeholders (indicated by
${someWidgetProperty}
style notation) that appear in the template's markup.- buildRendering (originating from
dijit._Widget
) In
_Widget
's implementation, this method simply sets the internal_Widget.domNode
property to an actual DOM element so that the dijit physically becomes a part of the page. Given that this method fires directly afterpostMixInProperties
, it should now be all the more apparent whypostMixInProperties
is the canonical location for modifying a widget's template.As you'll soon learn, another foundational Dijit class,
_Templated
, overrides this method to perform all of the myriad details that are involved in fetching and instantiating a dijit's template. Finally, note that just afterbuildRendering
is called, the dijit itself is added to Dojo's dijit manager object so that the dijit can be properly destroyed during explicit destructor methods and/or when the page is unloaded. Some browsers do have well-known memory leaks that become relevant for long-running applications, and tracking widgets through a centralized registry is Dojo's way of helping to alleviate that problem. It is quite uncommon to override this method; you'll normally use the default implementation from_Widget
or_Templated
.- postCreate (originating from
dijit._Widget
) This method executes once the dijit has been created and visibly placed in the page, so you can use it to perform any actions that might not otherwise be possible or prudent until that time. Take special care to perform actions that affect things such as a dijit's style or placement on the screen in
postMixInProperties
so that they occur before the dijit becomes visible. Performing those actions inpostCreate
may sometimes cause intermittent display "jerks" because you're manipulating the already visible dijit in this method; these issues can be difficult to locate and fix if you've forgotten the fundamental differences betweenpostMixInProperties
andpostCreate
. Additionally, note that if your dijit contains any child dijits, these children are not safely accessible here. To safely access child dijits, use the lifecycle methodstartup
instead. To safely access other nonchild widgets, wait until the page has loaded via usingdojo.addOnLoad
.- startup (originating from
dijit._Widget
) For child widgets declared in markup, this method automatically fires once the widget and all child widgets have been created. As such, this is the first safe place that a child widget could safely reference a child. As simple as it sounds, this task is often attempted in
postCreate
, which can lead to inconsistent behavior that can is difficult to detect and repair. For programmatically created widgets that contain other child widgets as part of a has-a relationship, you'll need to manually callstartup
yourself when you're sure that all child widgets have been created. The reason that you need to call it yourself for programmatically created widgets containing children is because it wouldn't make sense to proceed with sizing and rendering unless all child widgets have been added. (Otherwise, there could very well be lots of false starts.) This method is the final method stub that you can override for custom behavior to occur during dijit construction.- destroyRecursive (originating from
dijit._Widget
) This method is the generic destructor to call in order to cleanly do away with a dijit and any of its child dijits. In the processing of destructing a dijit, this method calls
uninitialize
, which is the primary stub method that you can override to perform custom tear down operations. Do not overridedestroyRecursive
. Provide custom tear-down operations inuninitialize
and call this method (it does not get automatically called), which takes care of the rest for you.- uninitialize (originating from
dijit._Widget
) Override this method to implement custom tear-down behavior when a dijit is destroyed. For example, you might initiate a callback to the server to save a session, or you might explicitly clean up DOM references. This is the canonical location that all dijits should use for these destruction operations.
Warning
Knowing the intricacies that distinguish the various lifecycle methods from one another is absolutely essential. Take special care to remember what type of behavior should be occurring in each method.
Especially common mistakes include:
Trying to manipulate a template after
postMixInProperties
has been calledModifying a widget's initial appearance after
postMixInProperties
has been calledTrying to access child widgets in
postMixInProperties
instead ofstartup
Forgetting to perform any necessary destruction in
uninitialize
Calling
uninitialize
instead ofdestroyRecursive
In addition to the _Widget
methods just described, there
are also some especially notable properties. Just like dijit
methods, you can reference these properties with dot notation.
You'll generally treat these properties as read-only:
id
This value provides a unique identifier that is assigned to the dijit. If none is provided, Dojo automatically assigns one. You should never manipulate this value, and in most circumstances, you won't want to use it at all.
lang
Dojo supports features for internationalization, and this value can be used to customize features such as the language used to display the dijit. By default, this value is defined to match the browser's setting, which usually matches that of the operating system.
srcNodeRef
If provided, this value populates the widget's
domNode
with the contents of thesrcNodeRef
and sets thedomNode
's id value to the id of thesrcNodeRef
.domNode
This property provides a reference to the dijit's most top-level node. This property is the canonical node that is the visible representation of the dijit on the screen, and although you'll probably want to avoid direct manipulation of this property if using the
_Templated
mixin, it is helpful for some debugging scenarios. As previously mentioned,_Widget
's default implementation ofbuildRendering
sets this property, and any methods that overridebuildRendering
should assume this responsibility or else strange, mysterious things may happen._Widget
's default implementation ofbuildRendering
setsdomNode
to either the value ofsrcNodeRef
(if provided) or an empty DIV element.
Just in case you're wondering, here's a simple code snippet
that you could run to inspect these properties in the Firebug
console. Again, all of the properties are inherited from _Widget
and are available via this
, when this
refers to the context of the
associative array that is the third argument to dojo.declare
:
dojo.require("dijit._Widget"); dojo.addOnLoad(function( ) { dojo.declare( "dtdg.Foo", dijit._Widget, { talk( ) : function( ) { console.log("id:", this.id); console.log("lang:", this.lang); console.log("dir:", this.dir); console.log("domNode:", this.domNode); } } ); }); foo = new dtdg.Foo( ); foo.talk( );
While _Widget
provides the
foundational stub methods that you can override for creation and
destruction events that occur during the lifecycle, _Templated
is the previously alluded-to
ancestor that actually provides the basis for defining a widget's
template in markup and using substitutions and attach points to add
functionality to it. Overall, it's a nice separation that lends
itself to tooling and separates designer tasks from
coding.
The vast majority of _Templated
's work involves parsing and
substituting into a template file. An important part of this work
entails overriding _Widget
's
buildRendering
method, which is
where all of the template mangling takes place. Three very important
concepts for templates include:
- Substitution
Dijit uses the
dojo.string
module to perform substitutions into templates using the${xxx}
styledojo.string
syntax. This is handy for taking widgets attributes that are passed in on construction and using them to customize templates.- Attach points
When the special
dojoAttachPoint
attribute is used in a template node, it provides the ability to directly reference the node via the attribute value. For example, if a node such as<span dojoAttachPoint="foo">...</span>
appears in a template, you could directly reference the node asthis.foo
(inpostCreate
or later).- Event points
Similar to attach points, you can use the special
dojoAttachEvent
attribute to create a relationship between a DOM event for a node in a template and a widget method that should be called when in response to the DOM event. For example, if a node were defined, such as<span dojoAttachEvent="onclick:foo">...</span>
, the widget'sfoo
method would be called each time a click occurred on the node. You can define multiple events by separating them with commas.
Like _Widget
, _Templated
is given more thorough coverage
with some isolated examples in just a moment. You're being exposed
to it now so that you have a general idea of its overall
purpose.
The most notable effect of mixing in _Templated
is that it results in
overriding _Widget
's buildRendering
method. Here's a synopsis
of buildRendering
:
- buildRendering
While
_Widget
provides this method,_Templated
overrides it to handle the messy details associated with fetching and instantiating a dijit's template file for on screen display. Generally speaking, you probably won't implement your ownbuildRendering
method. If you ever do override this method, however, ensure that you fully understand_Templated
's implementation first.
Here are _Templated
's
essential properties:
templatePath
Provides the relative path to the template file for the dijit, which is simply some HTML. Note that fetching the template for a dijit requires a synchronous network call, although Dojo will cache the template string after it is initially fetched. A discussion of producing a custom build of your dijits with tools from Util so that all template strings are interned is included in Chapter 16.
templateString
For dijits that have been designed or built to have their template strings interned inside of the JavaScript file, this value represents the template. If both
templatePath
andtemplateString
are defined,templateString
takes precedence.widgetsInTemplate
If dijits are defined inside of the template (either path or string), this value should be explicitly set to
true
so that the Dojo parser will know to search for and instantiate those dijits. This value isfalse
by default. Including dijits inside of other dijit templates can be quite useful. A common mechanism for passing values into child widgets that appear in a parent widget's template is via the${someWidgetProperty}
notation that is used for substitution.containerNode
This value refers to the DOM element that maps to the
dojoAttachPoint
tag in the web page that contains your dijit. It also specifies the element where new children will be added to the dijit if your dijit is acting as a container for a list of child dijits. (Dijits that act as containers multiply inherit from the Dijit_Container
class, and the dijits that are contained inherit from the Dijit class_Contained
.)
Get Dojo: The Definitive Guide now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.