Showing posts with label dojo dijit. Show all posts
Showing posts with label dojo dijit. Show all posts

Monday, August 23, 2010

dojo.connect Widget gotcha

I'm not too sure this small issue I ran into actually constitutes a full blog post, but here goes all the same. After reading phiggins' article on how to dispose of programatically created dijits, I started keeping track of the handles returned from dojo.connects and dojo.subscribes. I figured that these return values should be disconnected/unsubscribed just before a widget was destroyed to aid with garbage collection. Hence, a lot of my widgets started to look like this:
dojo.declare(
"my.widget.AccountCreator",
[dijit._Widget, dijit._Templated],
{
templatePath: dojo.moduleUrl("my.widget", "templates/AccountCreator.html"),
widgetsInTemplate: true,
_subscriptions: [],
_connects: [],

postCreate: function() {
this._connects.push(dojo.connect(dijit.byId("accountCreatorDialog"), "hide", this, "_handleHideDialog"));
this._subscriptions.push(dojo.subscribe("/acct/created", this, "_handleAccountCreated"));
},

destroy: function() {
// first clean up my connects and handles
dojo.forEach(this._subscriptions, dojo.unsubscribe);
dojo.forEach(this._connects, dojo.disconnect);
this._subscriptions = [];
this._connects = [];
// now go ahead and delete the rest of the widget
this.inherited(arguments);
}

Seems relatively simple, right? Being a good memory citizen and cleaning up just before the widget gets destroyed seems like a win-win situation. However, notice that my collection of dojo.connect handles is named _connects. Check out the source code for dijit._Widget (my.widget.AccountCreator inherits from dijit._Widget):
  create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
// summary:
// Kick off the life-cycle of a widget
// params:
// Hash of initialization parameters for widget, including
// scalar values (like title, duration etc.) and functions,
// typically callbacks like onClick.
// srcNodeRef:
// If a srcNodeRef (DOM node) is specified:
// - use srcNodeRef.innerHTML as my contents
// - if this is a behavioral widget then apply behavior
// to that srcNodeRef
// - otherwise, replace srcNodeRef with my generated DOM
// tree
// description:
// Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
// etc.), some of which of you'll want to override. See http://docs.dojocampus.org/dijit/_Widget
// for a discussion of the widget creation lifecycle.
//
// Of course, adventurous developers could override create entirely, but this should
// only be done as a last resort.
// tags:
// private

// store pointer to original DOM tree
this.srcNodeRef = dojo.byId(srcNodeRef);

// For garbage collection. An array of handles returned by Widget.connect()
// Each handle returned from Widget.connect() is an array of handles from dojo.connect()
this._connects = [];

Having a property of _connects on a custom dijit which inherits from dijit._Widget landed me in trouble. When my.widget.AccountCreator's destroy method was called, the call to dojo.disconnect was throwing an odd looking error:
Result of expression '([dojo._listener, del, node_listener][listener])' [undefined] is not an object

As pointed out by both @neonstalwart and @cb1kenobi (thanks!), there's a simple solution to all this - just use the connect method in dijit._Widget.
this.connect(dijit.byId("accountCreatorDialog"), "hide", "_handleHideDialog");

dijit._Widget's connect method will also take care of automatically disconnect each dojo.connect handle when the widget is destroyed. As a result, you don't need to override the destroy method of your custom widget to clean up any lingering handles.

Have you ever stumbled into an issue like this - are there any other 'common' internal dojo variable names that you've unconsciously clobbered?