APPENDIX

Reference & Cheat Sheets

Top 20 gotchas, widget lifecycle quick-reference, AMD module directory, dojox index, Dojo 1.x→2 migration guide, and recommended tools

Gotchas Lifecycle AMD Cheat Sheet dojox Migration Tools
A

Top 20 Gotchas

These are the bugs that cost senior developers the most hours on real Dojo projects. Each one has a short explanation and the fix.

1
startup() not called on layout widgets

BorderContainer, TabContainer, dgrid — all require startup() after insertion into the DOM. Missing it causes zero-height containers. Always call myWidget.startup() and then myContainer.resize().

2
dojo/store vs dstore — dgrid uses dstore

dgrid 1.x expects a dstore collection, not a dojo/store object. Passing dojo/store/Memory silently produces an empty grid. Use dstore/Memory with dstore/Trackable.

3
topic.publish() is synchronous

All subscribers execute immediately in the publish call stack. If a subscriber throws, the remaining subscribers are skipped. Wrap subscriber bodies in try/catch for production code; never rely on async behaviour from publish.

4
Forgetting this.own() on subscriptions and handles

Handles returned by topic.subscribe(), dojo/on(), and dojo/aspect must be registered with this.own() inside widgets. Otherwise they survive widget destruction and fire against null DOM nodes.

5
Reusing a dijit/Dialog

Calling dialog.show() a second time on a form Dialog can leave stale validation state and broken focus management. Create a new Dialog instance per open; destroy it in onHide.

6
validate(true) vs isValid()

widget.isValid() returns a boolean without showing errors. widget.validate(true) also shows the red border and tooltip. Use isValid() to aggregate, then validate(true) to make errors visible.

7
parseOnLoad:true with programmatic widgets

parseOnLoad: true scans the entire DOM on load for data-dojo-type attributes. If you also create widgets programmatically, double-parsing causes widget ID conflicts. Set parseOnLoad: false and call parser.parse() only on specific nodes.

8
this.inherited(arguments) in every lifecycle hook

If you override postCreate, startup, or destroy, you must call this.inherited(arguments). Omitting it breaks the MRO chain — mixins up the chain never run their own hooks.

9
Deferred .then() return value

Returning a value from a .then() callback wraps it in a resolved Deferred for the next handler. Returning nothing passes undefined. Always explicitly return the next promise in chains, or you'll lose the resolved value silently.

10
lang.hitch vs arrow functions

Dojo 1.x runs in ES5 environments. Arrow functions are unavailable. Always use lang.hitch(this, fn) or lang.hitch(this, "methodName") to bind this inside callbacks and event handlers.

11
Multiple inheritance MRO order matters

declare([A, B, C], {...}) — C3 MRO processes left to right. If A and B both define postCreate, B's runs first in this.inherited() chain. Draw your MRO graph before mixing more than two classes.

12
extraLocale not set for runtime locale switching

Without extraLocale: ["fr"] in dojoConfig, the French NLS bundle is never fetched at startup. A runtime switch will fail in production (built app) because the bundle was never included in the build layers.

13
dgrid column renderer returns DOM node, not HTML string

renderCell must return a DOM Node, not an HTML string. Returning a string causes it to be rendered as text (escaped). Use document.createElement() or domConstruct.create().

14
_TemplatedMixin attach points before startup

Attach points (e.g., data-dojo-attach-point="myNode") are resolved in buildRendering(). They are available in postCreate(). But widget children placed via attach points are not started — call their startup() explicitly.

15
destroy vs destroyRecursive

destroy() cleans up the widget itself but leaves child widgets in memory. destroyRecursive() destroys the widget and all dijit descendants. Use destroyRecursive() on container widgets; destroy() on leaf widgets.

16
dojo/request Content-Type header

xhr.post(url, { data: JSON.stringify(obj) }) does NOT set Content-Type: application/json automatically. Add headers: { "Content-Type": "application/json" } explicitly, or the server will receive the body as form data.

17
domReady! vs window.onload

dojo/domReady! fires when the DOM is parsed, before images and stylesheets finish loading — same as DOMContentLoaded. window.onload waits for everything. Use domReady! for widget setup; use window.onload only if you need fully-loaded resources (e.g., image dimensions).

18
Build: customBase without staticHasFeatures

Using customBase: true without staticHasFeatures can produce a larger dojo.js than expected because dead-code elimination doesn't fire. Always pair customBase with the full staticHasFeatures table (see Phase 9).

19
ContentPane href vs content

Setting href on a ContentPane fetches HTML via XHR and parses widgets inside. Setting content sets innerHTML and does NOT parse widgets. If your pane content has data-dojo-type attributes, use href or call parser.parse(pane.containerNode) after setting content.

20
Select widget empty option on first render

A dijit/form/Select created programmatically with an empty options array will show a blank dropdown. Always provide at least one option at construction time, even a placeholder { value:"", label:"Select…" }, or call select.startup() after adding options.

B

Widget Lifecycle Quick-Reference

The eight lifecycle hooks in creation order. The "when to use" column is the single most-referenced piece of information for Dojo widget development.

constructor()
Before DOM exists
Set instance defaults. No DOM available. Mixins receive parameters here.
⚠ Don't call get/set on other widgets — they may not exist yet.
postMixInProperties()
After params merged, before DOM
Computed properties based on constructor params. Use to derive values before the template is rendered.
buildRendering()
DOM creation
Creates this.domNode. Template is parsed, attach points resolved. Don't add event listeners here.
⚠ Always call this.inherited(arguments) first.
postCreate()
DOM ready, not in document
Most-used hook. Set up event listeners, DOM manipulation, widget children creation. Node is not yet in the live document.
Don't call resize() or read layout metrics here — dimensions are zero.
startup()
Widget placed in document
Required for layout widgets. Called after the widget is placed in the live DOM. This is where BorderContainer/StackContainer calculate sizes. Call child widget startup() here.
⚠ Forgetting startup() is the #1 cause of zero-height layout widgets.
resize()
On container resize
Called by layout containers when available space changes. Override if your widget has children that need relayout. Always call this.inherited(arguments).
destroy()
Widget removed
Cleans up this widget's event handles (own() handles auto-removed). Does NOT destroy child widgets.
Use destroyRecursive() on containers to clean up children too.
destroyRecursive()
Recursive cleanup
Destroys this widget plus all dijit descendants. The correct method to call on BorderContainer, TabContainer, and Dialog.
postCreate vs startup — the key distinction
QuestionpostCreatestartup
Is widget in live DOM?NoYes
Can read layout dimensions?No (all zero)Yes
Create child widgets?YesNo (too late for most)
Attach event listeners?YesRarely
Call resize()?NoYes (layout containers)
C

AMD Module Cheat Sheet

The 50 most-used Dojo modules organized by category. Memorize these and you'll write Dojo fluently.

Core utilities
dojo/_base/declare
OOP class system
declare(superClass, props)
dojo/_base/lang
hitch, mixin, clone, isArray
lang.hitch(ctx, fn)
dojo/_base/array
ES5 array methods (pre-ES5 compat)
array.forEach, map, filter
dojo/topic
Pub/sub event bus
publish(topic, args) / subscribe
dojo/on
DOM event binding
on(node, "click", fn)
dojo/aspect
AOP: before/after/around
aspect.after(obj, "method", fn)
dojo/Deferred
Promise-style async
new Deferred() / resolve/reject
dojo/when
Normalize value or promise
when(val, onResolve)
dojo/promise/all
Parallel promises
all([p1, p2]).then(fn)
dojo/Stateful
Observable properties
obj.watch("prop", fn)
DOM manipulation
dojo/dom
Basic DOM access
dom.byId("id")
dojo/dom-attr
Get/set DOM attributes
domAttr.set(node, "value", v)
dojo/dom-class
CSS class manipulation
add/remove/toggle/contains
dojo/dom-style
Inline style get/set
domStyle.set(node, "color", v)
dojo/dom-construct
Create and place nodes
create, place, empty, destroy
dojo/dom-geometry
Position and dimensions
getMarginBox, position
dojo/query
CSS selector → NodeList
query(".cls").on("click", fn)
Data / Store
dojo/store/Memory
In-memory object store
get / put / add / remove / query
dojo/store/Observable
Wrap store for live queries
Observable(store)
dstore/Memory
dstore in-memory (dgrid)
filter / sort / forEach
dstore/Trackable
Trackable mixin for dgrid
Memory.createSubclass([Trackable])
dojo/request/xhr
XHR shorthand
xhr.get/post/put/delete
dojo/request
Unified request API
request(url, opts)
Dijit layout
dijit/layout/BorderContainer
5-region layout
design: "headline"/"sidebar"
dijit/layout/TabContainer
Tabbed pane set
selectChild / addChild
dijit/layout/StackContainer
One-pane-at-a-time
selectChild(pane)
dijit/layout/ContentPane
Region / pane container
href, refreshOnShow
dijit/layout/AccordionContainer
Collapsible accordion panels
selectChild, duration
Dijit form
dijit/form/ValidationTextBox
Text input with regex validation
required, regExp, isValid()
dijit/form/Select
Styled dropdown
options array, get/set "value"
dijit/form/FilteringSelect
Autocomplete dropdown
store, searchAttr
dijit/form/CheckBox
Styled checkbox
get/set "checked"
dijit/form/DateTextBox
Date picker
constraints: { min, max }
dijit/form/Button
Styled button
onClick handler, disabled
dijit/Dialog
Modal dialog
show/hide, onHide→destroyRecursive
dijit/Menu
Context / popup menu
MenuItem, PopupMenuItem
i18n / a11y
dojo/i18n!path/nls/bundle
Load NLS bundle
Resolved as dependency argument
dojo/date/locale
Locale-aware date formatting
format(date, { selector:"date" })
dojo/number
Locale number/currency format
format(1234.5, { currency:"INR" })
dijit/focus
Focus management
watch("curNode", fn)
D

dojox Module Index

dojox contains experimental and domain-specific extensions. They are stable enough for production use but have less polish than core dojo/dijit. Always check the specific version for your app — APIs changed significantly between 1.x minor versions.

Module
Description
dojox/charting/Chart
Full charting library — line, bar, pie, scatter, stacked. Requires dojox/gfx. Used in Capstone 2.
dojox/charting/plot2d/*
Plot types: Lines, Bars, Columns, Pie, Scatter, Areas, Candlesticks
dojox/charting/axis2d/*
Axis types: Default (numeric/category), Invisible
dojox/charting/themes/*
Chart themes: MiamiNice, Claro, PlotKit, Shrooms, Tom (dark)
dojox/gfx
Vector graphics abstraction — SVG/Canvas/VML backend auto-detection. Required by charting.
dojox/mvc
Model-View-Controller layer — at.bind(), Output, Group, Repeat. Two-way data binding for forms.
dojox/mvc/at
Binding helper: at(model, "prop") for declarative two-way binding in templates
dojox/gesture
Touch gesture events: tap, doubletap, press, swipe, rotate, pinch. Mobile support.
dojox/mobile
Mobile widget set — ScrollableView, ListItem, Switch, Heading. Dojo's mobile UI layer.
dojox/grid/DataGrid
Legacy grid — deprecated. Replaced by dgrid. Do not use in new code.
dojox/layout/ExpandoPane
Collapsible BorderContainer region — toggleable side panel.
dojox/io/uploadIframe
File upload via hidden iframe (pre-HTML5 FormData). Use FormData + XHR in modern browsers.
dojox/widget/Standby
Loading overlay for any DOM node — shows spinner while async operation runs.
dojox/widget/ColorPicker
HSV color picker widget for form integration.
PRODUCTION RULE

Only include dojox modules you actually use in your build layers. The full dojox package is large (~4 MB unminified). Selectively adding charting or mvc to one build layer keeps your production bundles lean.

E

Migration Notes — Dojo 1.x → Modern Alternatives

Dojo 2 (released as "dojo/framework" in 2019) was a complete rewrite with TypeScript and a React-like VDOM. It is NOT backward-compatible with Dojo 1.x. Most Dojo 1.x shops migrated to React, Vue, or Angular rather than Dojo 2. This table maps the Dojo 1.x concept to its modern equivalent.

Dojo 1.x
Dojo 2 / Modern
Notes
dojo/_base/declare
TypeScript class
Direct replacement. TS classes have native extends/super.
AMD require/define
ES modules (import/export)
Bundler (webpack/vite) replaces the Dojo loader.
dojo/Deferred
native Promise / async-await
Dojo Deferred is thenable — wrap in Promise.resolve() for interop.
dojo/topic
Redux / Zustand / EventEmitter3
Pub/sub pattern still valid; just different libraries.
dojo/store
TanStack Query / SWR / Zustand
Server-state management has matured significantly.
dijit/_TemplatedMixin
React JSX / Vue SFC
Template strings replaced by component trees.
dgrid
AG Grid / TanStack Table
AG Grid is the closest feature-for-feature replacement.
dijit/layout/BorderContainer
CSS Grid / Flexbox
No library needed — native CSS handles 5-region layouts.
dojo/i18n!
i18next / react-i18next / vue-i18n
Same NLS bundle concept; more tooling and pluralization support.
Dojo build system
webpack / Vite / Rollup
Far faster, better tree-shaking, hot module replacement.
dojox/charting
Chart.js / D3 / ApexCharts
All have far richer ecosystems and active maintenance.
dojo/aspect
TypeScript decorators / Proxy
AOP is natively expressible with TS decorators or ES Proxy.
REAL-WORLD MIGRATION ADVICE

Most Dojo 1.x applications are internal enterprise tools with 10+ year lifespans. The pragmatic migration path is: wrap Dojo widgets as web components first (using customElements.define), then incrementally replace sections with React/Vue at the page level, without a full rewrite. The pub/sub architecture you built throughout this tutorial makes incremental migration tractable — React components can publish and subscribe to the same topic bus during the transition period.

F

Recommended Tools

Development
http-server (npm)
Zero-config local HTTP server for AMD development
npx http-server . -p 3000 --cors -c-1
Chrome DevTools — Console
Inspect widget registry, resolve AMD modules, check topics
require("dijit/registry").byId("myWidget")
Dojo AMD inspector
List loaded modules and their paths
require.loaded — in DevTools console
eslint (ES5 config)
Lint Dojo AMD code — catches missing returns in .then()
eslint --env browser,amd *.js
Build
Dojo Build System
Official AMD optimizer bundled in dojo-src/util
node dojo.js load=build --profile p.js
shrinksafe
Default JS minifier for Dojo build (Java-based)
optimize: "shrinksafe" in profile
Java 8+
Required by shrinksafe and Closure ADVANCED
java -version (must be present on PATH)
Build verification
Check built output contains expected modules
grep "define(" dist/app/app-layer.js | wc -l
Debugging
Widget registry inspect
Find all live widgets, check for leaks
require("dijit/registry").toArray()
Topic spy
Log all published topics to console
dojo/aspect.after(dojo.topic,"publish",console.log)
dojo.config inspect
Verify dojoConfig was parsed correctly
require("dojo/_base/config")
dgrid debug
Inspect dgrid collection and rendered rows
grid.collection.fetch().then(console.log)
Testing
Intern (by SitePen)
Official Dojo testing framework — AMD-aware unit + functional tests
npm install intern
Sinon.js
Spies, stubs, mocks — compatible with AMD; mock XHR responses
sinon.stub(xhr, "get").returns(deferred)
DOH (Dojo Objective Harness)
Legacy Dojo test runner — still used in many enterprise codebases
doh.register("myTest", [{...}])

🎓 Dojo 1.17.3 Tutorial Complete

You've covered 10 phases, 3 capstone applications, and this reference appendix — over 300 pages of senior-level Dojo content. You're equipped to build, maintain, and migrate any Dojo 1.x enterprise application.

Tutorial Hub Start from Phase 0