Opened 12 years ago

Closed 11 years ago

#4920 closed defect (fixed)

_Container.addChild() tests for _started attribute

Reported by: Robert Coup Owned by:
Priority: high Milestone: 1.1
Component: Dijit Version: 0.9
Keywords: Cc:
Blocked By: Blocking:

Description

dijit._Container.addChild() tests for the _started attribute on the parent widget to decide whether to call a child widgets startup() method.

16 widgets implement _Container but only 8 use _started (rough grep count).

This will only be noticeable if people are messing with containers dynamically. eg. adding a menu item programmatically after the menu is created.

Widgets that implement _Container but don't use _started:

  • dijit.form.Button
  • dijit.form.HorizontalSlider?
  • dijit._TreeNode
  • dijit.Menu
  • dijit.Toolbar

(from a quick grep, may not include everything)

Change History (6)

comment:1 Changed 12 years ago by bill

I don't understand what the issue is here. There's a startup() method in Widget.js that everyone inherits. I guess that should be setting this._started to true. Is that the problem? You said that dijit.form.Button doesn't reference _started, but why do you say that it should?

comment:2 Changed 12 years ago by Robert Coup

IMO, any widget that implements Container should use _started:

  • When a child widget gets added, the child's startup() only gets called if the parent is already started (determined via _started).
  • Otherwise it's assumed startup() on the parent will be called in the future and will call all the child startup methods (which is true)

Putting it into the base startup method (or in Container.startup()?) would be fine, except that widgets don't seem to call the parent startup() methods.

Menu, Toolbar, & Tree are probably the key ones since they are much more likely to have children manipulated at runtime than a button or a slider.

comment:3 Changed 12 years ago by bill

Version: 0.9

I see. That sounds reasonable. OK, can you provide a patch for this? Everyone should be calling this.inherited("startup", arguments), and I guess Widget.js should set this._started=true;

comment:4 Changed 12 years ago by Robert Coup

hrm. Slightly more complicated than I expected.

Is startup() guaranteed to only be called once? In what cases would it ever be called more than once?

A number of widgets check for it like this:

if (this._started) return;

... call child widgets and do other things ...

this._started = true;

So, we could replace that with this pattern (_started is set at the start of started()):

started : function() {
    if (!this.inherited("startup")) return false;

    ... do things ...

    return true;
}

and make startup() return false if the widget has previously been started.

or (_started is set at the end of started()):

startup : function() {
    if (this._started) return;

    ... do stuff ...
    
    this.inherited("started", arguments);
}

I'm wary of changing how startup() works for widgets where I'm not sure of the ramifications. But my vote would be for the second option.

I'm happy to prepare a patch for dijit/dojox once a call is made :)

comment:5 in reply to:  4 Changed 12 years ago by bill

Replying to rcoup:

hrm. Slightly more complicated than I expected.

Is startup() guaranteed to only be called once? In what cases would it ever be called more than once?

I don't remember the exact sequence here but sometimes it was being called more than once. That's why we have the _started flag.

The second option looks good to me (except you need braces on your if() statement as per dojo coding standards.

comment:6 Changed 11 years ago by bill

Resolution: fixed
Status: newclosed

(In [11731]) Make sure _started flag is set and checked correctly. Fixes #4920.

Note: See TracTickets for help on using tickets.