Nested widgets
In this section, you'll look at techniques for dealing with user-specified nested widgets.
In some cases a user can specify nested widgets for the widget. The Dojo TabContainer and Tree widgets are good examples of this.
Listing 30 shows how a nested Button widget is specified for the HelloWorld widget by a programmer -- widget user in test.html
Listing 30. test.html: Sample static nested widget
<html>
<head>
<script type="text/javascript"
src="dojoAjax/dojo.js"></script>
<script type="text/javascript">
dojo.require("dojo.widget.HelloWorld");
dojo.require("dojo.widget.Button");
</script>
</head>
<body>
<div dojoType="HelloWorld">
<div dojoType="Button"></div>
</div>
</body>
</html>
|
In the simplest case, you need to define the isContainer widget parameter to be true, and define a containerNode attachment point in the HTML template. And that's it -- nested widgets will automatically render and add to your containerNode.
Listing 31. dojoAjax/src/widget/HelloWorld.js
dojo.provide("dojo.widget.HelloWorld");
dojo.require("dojo.widget.HtmlWidget");
dojo.widget.defineWidget("dojo.widget.HelloWorld", dojo.widget.HtmlWidget, {
isContainer: true,
templateString: '<div><div dojoAttachPoint="containerNode">'
+ '</div></div>'
});
|
The solution above is simple because it relies on default Dojo behavior. As soon as you need to do something more complex, however, you have to handle the children array (a standard widget field which holds all static nested widgets) in the postCreate() method.
In Listing 32, you not only add nested user-defined widgets to
the containerNode, but you also add a list of nested widget types to the titleNode attachment point.
Listing 32. dojoAjax/src/widget/HelloWorld.js
dojo.provide("dojo.widget.HelloWorld");
dojo.require("dojo.widget.HtmlWidget");
dojo.widget.defineWidget("dojo.widget.HelloWorld", dojo.widget.HtmlWidget, {
isContainer: true,
templateString: '<div><div dojoAttachPoint="titleNode"></div>'
+ '<br/><div dojoAttachPoint="containerNode">'
+ '</div></div>',
postCreate: function() {
for(var i in this.children) {
this._addRow(this.children[i]);
}
},
_addRow: function(component) {
this.titleNode.appendChild(
document.createTextNode(component.widgetType));
this.titleNode.appendChild(
document.createElement("br"));
}
});
|
Listing 33 defines a test.html that runs with the widget defined in Listing 32.
Listing 33. test.html: Multiple static nested widgets
<html>
<head>
<script type="text/javascript" src="dojoAjax/dojo.js"></script>
<script type="text/javascript">
dojo.require("dojo.widget.HelloWorld");
dojo.require("dojo.widget.Button");
dojo.require("dojo.widget.ContentPane");
</script>
</head>
<body>
<div dojoType="HelloWorld">
<div dojoType="Button"></div>
<div dojoType="ContentPane"></div>
</div>
</body>
</html>
|
See the Dojo toolkit's TabContainer and Tree widgets for a more useful example of handling nested widgets.
Adding nested widgets dynamically
Imagine a user wants to add Dojo widgets to the widget programmatically at runtime.
Remember the discussion of widget attributes and their dynamic setters? The fact that consistency between these two things is not architecturally enforced might be the biggest flaw of current Dojo widgets architecture. It is, of course, more flexible this way and not that critical; but it is annoying because you never know if there is a dynamic setter for a given attribute. Developers tend just to forget to add them, which greatly decreases the widget's usability.
With nested widgets you have the same issue -- they are added
dynamically in a different way than statically. For dynamic addition, you need to use the addChild() method. Listing 34
shows a widget that handles both static and dynamic widget adding. Note that the reusable part is extracted in the _addRow()
method. When overriding the addChild() method, you need to call its superclass as well.
Listing 34. dojoAjax/src/widget/HelloWorld.js
dojo.provide("dojo.widget.HelloWorld");
dojo.require("dojo.widget.HtmlWidget");
dojo.widget.defineWidget("dojo.widget.HelloWorld", dojo.widget.HtmlWidget, {
isContainer: true,
templateString: '<div><div dojoAttachPoint="titleNode"></div>'
+ '<br/><div dojoAttachPoint="containerNode">'
+ '</div></div>',
postCreate: function() {
// adding statically
for(var i in this.children) {
this._addRow(this.children[i]);
}
},
addChild: function(child, overrideContainerNode,
pos, ref, insertIndex) {
this._addRow(child);
dojo.widget.HelloWorld.superclass.addChild.call(
this, child, overrideContainerNode);
},
_addRow: function(component) {
this.titleNode.appendChild(
document.createTextNode(component.widgetType));
this.titleNode.appendChild(
document.createElement("br"));
}
});
|
Listing 35 shows how to create a Button widget and dynamically add it to the widget in Listing 34.
Listing 35. test.html: Adding nested widgets dynamically
<html>
<head>
<script type="text/javascript" src="dojoAjax/dojo.js"></script>
<script type="text/javascript">
dojo.require("dojo.widget.HelloWorld");
dojo.require("dojo.widget.Button");
dojo.addOnLoad(function(){
var button = dojo.widget.createWidget("Button",
{caption: "Submit"});
dojo.widget.byId('someID').addChild(button);
});
</script>
</head>
<body>
<div id="someID" dojoType="HelloWorld">
</div>
</body>
</html>
|


