This tutorial is for Dojo 1.6 and may be out of date.

Up to date tutorials are available.

Application Controller

A page-level controller is the glue that ties together modular functionality to make a living, breathing application. We'll implement configuration and an explicit lifecycle over a loosely coupled architecture to assemble a single-page-application of many moving parts.


As a modular toolkit, much of Dojo's documentation and demos concern those individual components in isolation. But when you need to assemble together components to make an application, you need some framework to hang those pieces on, and flexibility in how they are wired together.

The Problem

Best practices suggest keeping a separation of concerns, and to maintain the modularity of the pieces that comprise the application. So, how do you manage the loading and initialization of disparate components and wire them up together to get data flowing and user interactions handled in a way that is itself flexible and modular?

The Solution

A page-level controller is an object that has responsibility for the management of the page or application at a large-grain level. It assumes control of the lifecycle of the application and the loading of its parts. It initializes and connects together those parts in the correct sequence and keeps specific knowlege of this big picture out of the components themselves.

Flickr Viewer with Fruit Photo

View Final Demo


Dojo does not express an opinion on how you should assemble applications out of the components it provides. It has all the nuts, bolts and moving parts, but no blueprints. As a Toolkit, this is by design. You can sprinkle a little Dojo onto an otherwise static web page, or you can build a complete GUI application framework with it, using the design patterns and implementation of your choice. For this tutorial, though, we'll take a sample somewhere in the middle, and build a concrete implementation that meets some key requirements:

  • Leverage the Dojo package system to faciliate module loading and optimization via the build scripts.
  • Maintain modularity — avoid coding specific knowledge of the application into the components that play a part in it.
  • Preserve seperation of concerns — the UI should remain ignorant of where its data comes from and vice-versa

Getting Started

The scenario for this recipe is that we want to build an application that allows a user to search for photos on Flickr, view a result listing with thumbnails, and be able click to see a large version of each image. It is a kind of master-detail pattern, much repeated and found in some form in most applications. For this tutorial we're really focussed on the orchestration — how the individual parts are brought together — so we'll just do a quick overview of the parts themselves to provide some context.

The Store

The data layer in this application is handled by the This is an out-of-the-box component, which implements the Read API over the top of requests to the Flickr API service.

We use the standard fetch method to pass a query, which is turned into a JSONP request to the Flickr service, which responds and fires our onComplete callback. The other components should know as little as possible about Flickr. Any specific knowledge should be confined to the thing instantiating the store and providing its configuration — our application in other words.

The UI Layout

We use the same kind of BorderContainer-based layout seen in the Layout tutorial. Each search result will be given its own tab in the TabContainer that occupies the central region

The Form

The user enters search keywords via an input field in the top region. They can click the search button, or just press the Enter key to submit the search. Wiring up event handlers and their actions is the domain of our application in this example.

We could have created a custom widget to provide a slightly higher-level interface to our application, but the simple requirements here didn't warrant it.

The Result Listings

Our application's onResult method renders the results and creates the new tab panel.

We use an event delegation technique to register a click event listener that will cause the item selected to display the corresponding larger image. Here too, we could have created a custom widget to render the items, but the flow and responsibilities aren't much changed at the application level.

The Lightbox

We put the large image in a lightbox-style popup, floating over the main UI. We use an instance of the dojox.image.LightboxNano widget to pop up and display the photo.

The Loading Indicator

We use a simple pair of startLoading and endLoading methods to put up loading overlay. Loading indication in this app is of page/app-level concern, so the overlay itself and the methods to show/hide it reside on the application object.

Step 1: The Layout

In this application, we're using the declarative style of UI creation. The main application layout is described in the markup in the page, with the appropriate data-dojo-type and data-dojo-props attributes to configure our widgets.

The keyword entry field is a plain HTML text input, and the button that submits the search is a plain HTML button. Dijit's BorderContainer takes care of positioning and sizing the top and center regions to make the search bar fixed, and the results area flexible in height.

Scrolling will be handled by the individual tab panes — we're using dijit.layout.ContentPanes.


		// any init

These are the modules we'll need for the initial layout. For now, they are require'd from a script block in the head. The markup is as follows:

<body class="claro">
	<div id="appLayout" class="demoLayout" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design: 'headline'">
		<div class="centerPanel" id="tabs" data-dojo-type="dijit.layout.TabContainer" data-dojo-props="region: 'center', tabPosition: 'bottom'">
			<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title: 'About'">

				<h2>Flickr keyword photo search</h2>
				<p>Each search creates a new tab with the results as thumbnails</p>
				<p>Click on any thumbnail to view the larger image</p>

		<div class="edgePanel" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'top'">
			<div class="searchInputColumn">
				<div class="searchInputColumnInner">
					<input id="searchTerms" placeholder="search terms">
			<div class="searchButtonColumn">
				<button id="searchBtn">Search</button>
View Step 1

So far, so good. Everything is where it should be, but there's no functionality, and as we build up that functionality, we'll need somewhere to put it all. Enter the application object.

Step 2: Application Object

We'll create a new module for the application controller. It assigns an object literal to, with a few methods and properties:

dojo.provide(""); = {
	store: null,
	query: dojo.config.flickrRequest || {},

	init: function() {
		// proceed directly with startup

	startup: function() {
		// create the data store
		var flickrStore = = new;

	initUi: function() {
		// set up click handling for the search button
		dojo.connect(dojo.byId("searchBtn"), "onclick", this, function(evt){
			var terms = dojo.byId("searchTerms").value;
			if(terms.match(/\w+/)) {
	doSearch: function(terms) {
		// ...
	onResult: function(term, items) {
		// ...

The gets the query details it will eventually pass to the store from the dojo.config object. That keeps a lot of the kind of specifics that might change in between testing, development and production out of the module itself. The dojoConfig object looks like this:

	dojoConfig = {
		isDebug: true,
		parseOnLoad: true,
		baseUrl: '/documentation/tutorials/1.6/recipes/app_controller/',
		modulePaths: {
			"demo": "demo"
		flickrRequest: {
			apikey: "YOURAPIKEYHERE",
				attribute: 'datetaken',
				descending: true
View Step 2

While this demo does use a specific API key, in order to be properly using the Flickr API, do make sure that you properly register your own API key.

For more on dojo.config, check out the reference guide. A tutorial covering dojo.config in depth is coming soon!

The is where we'll keep the data store reference, as well as the query information we have that will be used in each Flickr request.

We've defined an init method as the main entry point. The visual and interaction setup will be done in our initUi method, where all the widget and DOM-dependant steps can take place.

We've stubbed in the main interaction — sending keywords typed in the search field over to the doSearch method.

You can define a class that your controller object is an instance of using dojo.declare — if you have a need for variations and further componentization of the application functionality.

Step 3: Hooking up Search

The controller has the knowledge needed to create the store requests. It connects up the events from the search bar to invoke the doSearch method, where it assembles a request object and calls the store's fetch method with it.

Note that when the search successfully completes, we're not processing it here directly, but handing the results over to our onResult method, helping us to preserve separation of concerns.

doSearch: function(terms) {
	// summary:
	// 		initiate a search for the given keywords

	var request = {
		query: dojo.delegate(this.query, {
			text: terms
		count: 10,
		onComplete: dojo.hitch(this, function(items, request){
			this.onResult(terms, items);
		onError: dojo.hitch(console, "error")
View Step 3

Step 4: Search Results

To do something with the response we get back from the store, we need to build on the onResult method. Notice, the flow doesn't change, the markup doesn't change, how we fetch data is still seperate from how we render it.

We'll add a few properties to the object to faciliate the rendering — a template for the item contents, and some object paths that we'll use to locate the urls we need in the items Flickr gives back.

	itemTemplate: '${title}',
	itemClass: 'item',
	_itemsById: {},

	largeImageProperty: "media.l", // path to the large image url in the store item
	thumbnailImageProperty: "media.t", // path to the thumb url in the store item

Now the onResult method has work to do:

onResult: function(term, items) {
	// summary:
	// 		Handle fetch results

	var contr = dijit.byId("tabs");
	var listNode = dojo.create("ul", {
		'class': 'demoImageList',
		'id': 'panel'+contr.getChildren().length

	// create the new tab panel for this search
	var panel = new dijit.layout.ContentPane({
		title: term,
		content: listNode,
		closable: true

	// render the items into the <ul> node
	this.renderItems(items, listNode);

	// make this tab selected
View Step 4

The renderItems method is not listed here. It simply builds up <li> nodes to insert into the list that represents our results.

Step 5: View Large Image

The main piece we're missing at this point is the ability to view the larger image. For that we're using the dojox.image.LightboxNano widget. It is a fairly lightweight implementation of a lightbox and a good fit here.

To use it we need to add the dependency to our list of dojo.requires. We can then create and reuse a single instance:

initUi: function() {
	// summary:
	// 		create and setup the UI with layout and widgets

	// create a single Lightbox instnace which will get reused
	this.lightbox = new dojox.image.LightboxNano({});

We hook up click handling on the result items to determine which list item was clicked, map that to the store item and retrieve the current url from that item for display in the lightbox:

	showItemById: function(id, originNode) {
		var item = this._itemsById[id];
		if(item) {
			this.showImage( dojo.getObject(this.largeImageProperty, false, item), originNode);
	showImage: function(url, originNode){
		// summary:
		// 		Show the full-size image indicated by the given url{
			href:url, origin: originNode
View Step 5

Using event delegation to register a single click event listener on the whole list is both more efficient and flexible. Having nothing bound to the list nodes leaves you free to change or remove those at will, without needing to disconnect and reconnect events on each item.

Step 6: Loading Indication

Each step so far has been filling in stubs and layering functionality. That has been fairly mechanical, in part because of the structure of our application, as well as the clear sequence of events we've implemented. At this point we are feature complete, but the user experience needs work.

You've probably noticed the flash of unstyled content that appears while the Dojo modules load and before the widgets are created. In production, we would compress and concatenate together the dependencies so they would load faster, but we would improve things in either case by adding a "Loading..." overlay.

We'll use a simple implementation consisting of a pair of startLoading and endLoading methods on the Loading indications should be side effects. We don't want to have to restructure the flow we've built up to accomodate it.

We can hook into the methods to show and hide the overlay using dojo.connect from our startup method:

startup: function() {
	// create the data store
	var flickrStore = = new;

	// wait until UI is complete before taking down the loading overlay
	dojo.connect(this, "initUi", this, "endLoading");

	// build up and initialize the UI

	// put up the loading overlay when the 'fetch' method of the store is called
		dojo.hitch(this, "startLoading", dijit.byId("tabs").domNode)
	// take down the loading overlay after onResult (fetch callback) is called
	dojo.connect(this, "onResult", this, "endLoading");

The startup method represents the point in the sequence when the data store is initialized, and in which we create all the widgets (via .initUI), so it's a good place to hook in the loading indicator.

Keeping it at arm's length like this not only allows us to gather together all the loading-related connections into once place, but it also means we don't have to put code inside the functions of interest. That's of immediate value with the store's fetch method call, but might also serve us down the road if we want to inherit some of these application controller methods from a base class.

View Step 6

Step 7: Staggered Loading

With the loading indication in place, we can step back a bit. How is this going to work as we go into production and look to optimize how it is delivered?

The sequence we've put in place allows us to experiment with what we load, when. As a first step, lets take all those dojo.require statements out of the HTML page and locate them in a demo.module.

This approach buys us two things:

  • It puts the dependency list in one place so we can add or change it without having to update our HTML each time.
  • It will pay dividends when we come to create a build for this application — a topic for another tutorial.

The demo/module.js file looks like this:


// dependencies for the application UI and data layers


The script block in our HTML can now be simplified to this:



The application controller now has the responsibility for loading its own dependencies. To give it full control over the page lifecyle we need to add one more thing to our = {
	parseOnLoad: (function(config){
		// set dojo.config.parseOnLoad to false
		// and store the original value in our own parseOnLoad property
		var parseOnLoad = config.parseOnLoad;
		config.parseOnLoad = false;
		return parseOnLoad;

This is a slight tricksy way of assuming control over when the parser runs. We can look at the initial parseOnLoad setting from our Dojo configuration via the dojo.config.parseOnLoad property. By capturing the value for our own use, and then setting it to false, we can pick the right time to parse and instantiate our declarative widgets.

In this case, initUi is where we create everything else UI-related and it makes sense to run dojo.parser.parse() there.

Now, without changing the meaning of any of our existing methods and call sequence, we can defer loading dependencies until they are actually needed. The result is a staggered, 2-stage loading sequence:

init: function() {

	// phase 2: load further dependencies

	// register callback for when dependencies have loaded
	dojo.ready(dojo.hitch(this, "startup"));

View Step 7

Step 8: Further Improvements

There's clearly a lot of questions this implementation doesn't yet answer. We're not really handling errors here, and user experience overall could be improved. One quick thing we can do to improve the responsiveness of the application is to start preloading the larger images so they are already cached on disk when the user clicks to view them.

The events needed are already defined. We can inject new code to run right after renderItems, which passes the array of items we'll need:

startup: function() {
	// when items are being rendered, also preload the images
	dojo.connect(this, "renderItems", this, "preloadItems");

The result is much improved with no change to the application flow: renderItems still renders items — adding in preloading code in there would have been awkward. The new preloadItems is definately an application or page-level concern, and not something that the renderer needs to bother with.

View Final Demo


We've made many decisions along the way while building this application. At any juncture the answer could have been different, given different requirements or preferences. For example:

  • We could certainly have created custom widgets to more neatly encapsulate the result listing.
  • The controller could have been derived from a class.
  • We could have used a more generic data store, or even the newer API.
  • We could have represented the user interface with its own object — sometimes known as the "whole-UI widget" which the controller would populate and talk to.

But fundamentally, it wouldn't change what we've built here. By defining an explicit sequence of steps for initialization, and clear roles for the application controller and its parts — the store and the UI — we've made extension in any of these directions possible.

Hopefully, by going through the motions of taking a pile of building blocks and creating an application from them, we've removed some of the hand-waving and "your code goes here" puzzles.