 | Level: Intermediate Gal Shachor (shachor@il.ibm.com), Senior Technical Staff Member, IBM Ksenya Kveler (ksenya@il.ibm.com), Research Staff Member, IBM Maya Barnea (mayab@il.ibm.com), Research Staff Member, IBM
11 Dec 2007 The previous installment
of this series introduced you to Ajax development by walking through the practical information
essential for getting an Ajax-enabled environment up and running. In this article, Part 2 of the series, the authors put
your newly gained knowledge into practice by starting the development of a simple Dojo and Atom-based blog reader.
The Atom protocol pair lets online providers of resources such as news, community
Web sites, and blogs to syndicate their content through the Web. In a typical use of
Atom, content providers syndicate a file, or a Web feed, and make it available over
the Web. The feed, whose representation is defined in the Atom Syndication Format,
provides a summary of recently added resources. Published feeds can then be used by Atom client software, such as blog readers, which
leverage the Atom Publishing Protocol to discover newly added content and present it.
This article will add to your Ajax knowledge by demonstrating how to start to develop an Ajax-
and Atom-based blog reader (in this installment you will develop the view
and controller components of the reader). You'll use the Dojo toolkit to develop the application, which will communicate with back-end Atom feeds using the Atom
Publishing Protocol. You'll also use the Dojo storage package to keep feed subscription data.
Dojo — our Ajax toolkit of choice
This blog reader application is based on the Dojo toolkit, and you might wonder
why
we selected Dojo and not some of the other toolkits out there. The Book of Dojo (see Resources)
suggests several reasons, but for the purposes of this exercise, we cite two simple reasons
that drove us to use Dojo:
- Vertical integration and completeness: compared to other open source Ajax
toolkits, Dojo offers the most complete and integrated library of
components.
- Black box reuse: the Dojo widget mechanism allows developers to compose
new applications from widgets without knowing their internals. This makes it
relatively easy to create sophisticated Ajax applications.
 |
What's in this article?
Learn how to use the Dojo toolkit to develop an Ajax- and Atom-based blog reader. This part of the series will get you started with the essentials of blogging architecture and demonstrate how to implement the blog reader. The next article of the series will walk you through the actual implementation of the model. |
|
Vertical integration and completeness
Dojo has a layered architecture, where each layer adds a progressively more advanced
functionality:
- The Dojo core layer includes the fundamental Ajax features that are present
in many toolkits (such as IO and DnD), cross-browser compatibility, basic DOM
manipulations, and so on.
- On top of the Dojo core, the DIJIT layer provides the widget
system as well as many widgets.
- The last layer, DOJOX, includes various extensions such as offline storage
and cross-browser vector graphics.
Collectively, the various layers of the Dojo architecture produce a comprehensive integrated toolkit that addresses
most development needs.
Black Box reuse — widgets
Dojo widgets are Ajax-based UI components that can be reused with a single line of
HTML code. For example, Listing 1 shows how an
expandable title pane widget can be instantiated in an HTML page:
Listing 1. Title pane widget instantiation in an HTML page
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<script type="text/javascript" src="../../dojo/dojo.js"
djConfig="parseOnLoad: true, isDebug: true"></script>
<script language="JavaScript" type="text/javascript">
dojo.require("dijit.TitlePane");
dojo.require("dojo.parser");
</script>
<style type="text/css">
@import "../../dojo/resources/dojo.css";
@import "../themes/tundra/tundra.css";
</style>
</head>
<body class="tundra">
<div dojoType="dijit.TitlePane"
title="This is the title" style="width: 300px;">
And this is the content, clicking the title
will expand/collapse me.
</div>
</body>
</html>
|
As you can see, the page starts by importing the core Dojo library. Once the Dojo
core is loaded, we can use dojo.require() to load the
TitlePane widget, which we then reference inside the body tag.
Figure 1 shows the TitlePane widget in action, the outcome of the code fragment shown in Listing 1:
Figure 1. TitlePane widget in action
InformationBox — developing a simple Dojo widget
Widgets are implemented by a Dojo JavaScript class that provides the widget's
functionality. For widgets with a complex UI, you can also provide an HTML
fragment to function as the widget UI template.
To dispel the magic behind widgets, let's develop a very simple widget that displays
informational messages. The widget will allow users to title an information fragment and
provide outcomes similar to the illustration in Figure 2:
Figure 2. InformationBox widget in action
Listing 2 presents dwsample.Info, our informational
message widget:
Listing 2. InformationBox widget implementation
if(!dojo._hasResource["dwsample.Info"]) {
dojo._hasResource["dwsample.Info"] = true;
dojo.provide("dwsample.Info");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.declare(
"dwsample.Info",
[dijit._Widget, dijit._Templated],
{
title: "",
// The widget content template
templateString: [
"<div id='${id}' class='dijitTitlePaneTitle'>",
" <div>",
" <span dojoAttachPoint='titleNode' style='font-size:1.1em; margin:
1em;'></span>",
" </div>",
" <div dojoAttachPoint='containerNode' class='dijitTitlePaneContent'></div>",
"</div>"
].join('\n'),
postCreate: function(){
this.setTitle(this.title);
},
setTitle: function(/*String*/ title){
this.titleNode.innerHTML = title;
}
});
}
|
As shown in Listing 2, the widget implementation really starts with the dojo.declare() function.
This function receives the widget's definition in JSON format and initializes a new widget.
An important aspect of this new widget is the templateString. It provides an HTML fragment that serves as a template to the HTML content generated by the widget. The template is scattered with dojoAttachPoint
attributes instructing Dojo to attach the marked DOM element as a member of the
generated template. This allows widgets direct access to the DOM nodes without DOM
manipulations (as seen in the method setTitle). A special
attach point, containerNode, signals a special DOM node where
the widget body should be appended.
Using the new widget is simple, as demonstrated in Listing 3:
Listing 3. InformationBox widget usage
<div dojoType="dwsample.Info"
title="Do not panic, here is what you need to do"
style="width: 500px;">
At first, developing a Dojo widget looks like a chore, but it is
not! Here is what you should do:
<ol>
<li>Create a JavaScript file with the name of the widget and in
a directory structure known by Dojo</li>
<li>Extend the Dojo widget base classes</li>
<li>...</li>
</ol>
</div>
|
While the informational message is a very simple widget — it does not use events
or expose new methods — it should give you enough of an idea of how widgets operate.
Now that you have a basic knowledge of Dojo, let's take
a look at Atom and its use in our blog reader.
Atom and blogging
The term Atom refers to a pair of standards that define a feed format (for
representing) and a protocol (for editing) Web resources such as Weblogs, online
journals, Wikis, and similar content:
- The Atom Syndication Format (ASF) was standardized in RFC 4287 and
specifies the representation format of Atom resources.
- The Atom Publishing Protocol (APP) is still in its draft form and specifies
how to use REST to manage Atom resources.
Note that while Atom started with blogging in mind, it has a more
general use in that it allows for CRUDing Web resources (CRUD is an acronym for create/read/update/delete).
The Atom Syndication Format (ASF)
The ASF specifies the XML representation for resources carrying information on
syndicated item collections (also known as feeds) and the items they contain (known
as entries):
- A feed is a uniquely identified, titled, and time-stamped collection of
syndicated resource entries. Blogs are normally represented by one or more
feeds.
- An entry is a uniquely identified, time-stamped resource with metadata such
as title, summary, and categories. The entry's resource content can be anything
from inline text to binary base64 encoded blob to out-of-line content
specified by URI. Blog entries are normally represented by an entry in the
blog feed.
To make the ASF more concrete, let's take a look at a short, single-entry Atom feed
document in Listing 4:
Listing 4. Sample Atom feed
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>My Blog</title>
<link href="http://example.org/myblog/"/>
<updated>2005-11-13T16:25:05Z</updated>
<author>
<name>John Smith</name>
</author>
<id>urn:uuid:50a70c74-d399-11d9-b93C-0004545e0af6</id>
<entry>
<title>What is Atom?</title>
<link rel="alternate" href="http://example.org/myblog/1"/>
<id>urn:uuid:1234c670-cfb8-4ebb-aaaa-80da352efa6a</id>
<updated>2005-11-13T16:25:05Z</updated>
<summary> The term Atom refers to a pair of standards</summary>
<content type="text/html">
The term Atom refers to a pair of
standards which are primarily used in the context of Web
content syndication. The emergence of Atom was motivated ...
</content>
</entry>
</feed>
|
As you can see, the feed representation carries with it some feed-specific
information, followed by any number of entries.
The Atom feed information includes a few mandatory elements as well as recommended and optional elements. The mandatory feed elements include:
- Title: Provides a human-readable title for the feed. Typically, it is the
same as the title of the associated Web site.
- Updated: Indicates the most recent time when the feed was modified, in a
format the publisher considers significant.
- ID: Conveys a permanent, universally unique identifier for the feed.
The Link element, a recommended Atom feed element, can be used to link to various other resources that are related to
the feed (including links back to the Atom feed resource).
The Atom entry element is similar to the feed element in that it also must contain the
title, updated, and ID sub-elements. In reality, however, the entry does not serve its
purpose without additional elements, such as:
- Link: Links to a resource that is related to the entry. The relation type is
defined by the
rel attribute; for example, the value
alternate signifies that the link provides an
alternate representation of the resource described by the entry element.
- Content: Contains either the actual resource representation or links to the
representation.
- Summary: Provides a short excerpt of the entry.
If no entry content is provided, there must be at least one alternate link pointing to the content.
Atom Publishing Protocol (APP)
The APP is a REST-based protocol for publishing and editing Web resources. This means
that APP applies not only to Atom resources, but also to other Web-related resources,
such as images, e-mail content, and more. As RESTful protocols go, APP leverages the HTTP protocol (including methods,
headers, and status codes, among others) to CRUD the resources. APP will be used by
your blog reader to interact with ATOM feeds and entries.
APP introduces the concept of collections, which are sets of resources that can be
retrieved in whole or in part. In the Atom syndication realm, these collections and
resources are mapped to Atom feed and entry resources, whose representation is
encoded according to the ASF. In addition to collections and resources, the APP also
defines workspaces and services that can be used to discover collections (however, this is beyond the scope of this article).
Now, having covered plenty of background, you're ready to start building your blog reader!
Implementing a blog reader with Atom
Our blog reader application (shown in Figure 3) manages a list of blog feeds
(and their URIs) which are of interest to the user:
Figure 3. Blog reader application
Feeds can be added to and removed from the list. In our application, users
subscribe to a specific feed of interest by entering its title and URI into the dialog
box (as shown in Figure 4):
Figure 4. Adding a feed
When a user wants to see content in a certain feed, he or she simply selects it in the feeds
combo-box. Behind the scenes, the blog reader application follows the APP and sends
an HTTP GET request to the feed URI. The reader then checks the returned HTTP status code and in case of success (status
code is 200), it retrieves the resulted Atom entry collection from the HTTP response
payload. At this point, the reader inspects the returned entries and presents them to the
user. First, the entries are listed together at the left side of the
application, with the entry title representing the entry in the list (see Figure 5):
Figure 5. Listing the entries
At this point, the user can select an entry from the list by clicking it and the reader
does its best to present the content of the entry using the following algorithm:
- If the entry includes a
content element with actual
resource representation, it is used.
- Otherwise, if a
summary element is available, it is used.
- If both content and summary elements are missing, a link to the content is presented.
If the original article link could not be detected either, the link to the alternate
entry resource representation is given, as specified in the
link element with rel
attribute having alternate value.
When the user follows the link, a new browser window is opened with the link.
The reader application follows the model view control (MVC) design pattern. Upcoming sections cover the implementation of the reader view and
control components. (The model and the technology used for implementation will be
covered in a future article.) Note that in our MVC implementation, a practical approach was taken that
preferred simplicity over design purity. Therefore, the view and control are coupled
more than may be optimal.
The View component
The blog reader view is implemented as a single HTML file that leverages Dojo.
The view includes three main sections as sketched in the (skeletal) Listing 5:
Listing 5. View component implementation
<html>
<head>
<!-- Section 1, Dojo and application imports -->
</head>
<body class="tundra">
<!-- Section 2, Blog Reader UI -->
<fieldset class="feed-definition-area">
Select Feed:
...
</fieldset>
<hr>
<div class="feed-area">
<div dojoType="dijit.layout.SplitContainer"
...
</div>
</div>
<!-- Section 3, dialog box definitions -->
<div dojoType="dijit.Dialog"
id="add-feed-dlg"
...
</div>
<!-- Progress dialog -->
<div dojoType="dijit.Dialog"
id="progress-dlg"
title="">
...
</div>
<div dojoType="dijit.Dialog"
id="error-msg-dlg"
...
</div>
</body>
</html>
|
- The first section imports the various JavaScript, CSS, and widgets used
throughout the application.
- The second section constructs the main reader UI.
- The third section defines all the various dialog boxes used throughout the reader implementation.
Let's take a more detailed look into the various sections of the application.
Section 1: Imports
Take a look at Listing 6, which presents the full Imports section of the
application:
Listing 6. Imports section
<head>
<title>Blog Reader</title>
<script type="text/javascript"
src="./reset-styles.js"></script>
<style type="text/css">
@import "../dojoAjax/dojo/resources/dojo.css";
@import "../dojoAjax/dijit/themes/tundra/tundra.css";
@import "css/blogreader.css";
</style>
<script type="text/javascript"
src="../dojoAjax/dojo/dojo.js"
djConfig="parseOnLoad: true"></script>
<script type="text/javascript">
dojo.require("dijit.Dialog");
dojo.require("dijit.util.manager");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.Textbox");
dojo.require("dijit.layout.SplitContainer");
dojo.require("dijit.layout.ContentPane");
dojo.require("dojo.parser");
</script>
<script type="text/javascript" src="blog-reader.js"></script>
</head>
|
The Imports section starts by importing the JavaScript file
reset-styles.js, which resets the CSS
properties of the various tags to well-known defaults. Following the style reset, the various CSS files that set the look and feel of the application are imported. We first import the Dojo CSS files (and especially tundra.css that sets the look and feel of the
various Dojo widgets) and then add to them several blog reader-specific styles.
The next step is to import the Dojo library. In this case, Dojo is located in the
dojoAjax directory and the core dojo.js is imported. The various
required Dojo widgets are also imported using the dojo.require() method
(provided by the Dojo core).
As a last step, the blog reader logic held inside the file
blog-reader.js is imported (you will explore
blog-reader.js later in this article).
Section 2: Reader UI construction
Now that the various CSS and JavaScript files are included, we can start building the
application using HTML tags and Dojo widgets, shown in Listing 7:
Listing 7. Blog reader UI implementation
<body class="tundra">
<fieldset class="feed-definition-area">
Select Feed:
<select dojoType="blogreader.FeedsCombo" id="feed-combo" comboClass="feedsCombo">
</select>
<button dojoType="dijit.form.Button" id="add-feed-btn" iconClass="addIcon">
Add Feed
</button>
<button dojoType="dijit.form.Button" id="clear-feed-btn" iconClass="cancelIcon">
Clean Feeds
</button>
</fieldset>
<hr>
<div class="feed-area">
<div dojoType="dijit.layout.SplitContainer"
orientation="horizontal"
sizerWidth="7"
activeSizing="false"
class="article-splitter">
<div dojoType="dijit.layout.ContentPane"
sizeShare="30"
sizeMin="20"
style="overflow:auto">
<div id="articles-table-wrapper"
style="visibility: hidden; width:96%">
<div dojoType="blogreader.ArticleList"
id="article-list"
listClass="articles-list"
titleClass="articles-list-title"></div>
</div>
</div>
</div>
<div dojoType="dijit.layout.ContentPane"
sizeShare="70"
style="overflow:auto">
<div id="article-content-pane" class="article-content-pane">
<div id="article-title-id" class="article-title">
</div>
<div id="article-content" class="article-content">
</div>
</div>
</div>
</div>
|
The first noticeable Dojo dependency in this section is the use of the tundra class
inside the body. Dojo implements visual themes using CSS. Setting the body class
to tundra kicks in the use of the Dojo tundra theme.
The first UI element we create is the feed selection control located at the top-most
portion of the application's UI. The feed selection control is created by assembling
together an HTML select control and two Dojo buttons. A few things to note in this
section:
- We implemented a Dojo widget to wrap the select control. Wrapping the
select with our own widget allows us to put all the presentation logic related to
the feed selection (adding and removing feeds) in a single place.
- We do not set callbacks from the view to the controller directly in the markup.
Later on we will bind the controller JavaScript methods and control event
handlers programmatically.
- We decorate the buttons with icons (determined from a
CSS class).
Next we construct the main area where the selected feed is presented. The goals for
the feed presentation area are:
- If a feed is selected, present a list of entries at the left.
- If a certain entry is clicked, present its contents at the right. However, if
the user did not select an entry, do not present anything.
We implement these goals using the Dojo split container, which allows us to take over
the browser space and split it into two areas, each populated by
ContentPane. The
ContentPanes are separated by a sizer control that allows users to resize the
ContentPanes. Each of the ContentPanes contains a div HTML element that wraps the
content (entry list/entry content). Using these div elements, we can easily show and
hide their content (using the display CSS style).
The entry list is once again implemented as a home-grown Dojo widget.
Section 3: Dialog boxes
The third and last view section instantiates the dialog boxes used within the reader. In a perfect world, each of the dialog boxes would have been implemented
as a widget. However, to keep things short, we embed the dialog boxes into our main
UI and handle their events in the controller.
Take a look at Figure 6, which presents the Add Feed dialog box, which
is the
most complex of the various dialog boxes:
Figure 6. Add Feed dialog box
Listing 8 presents the implementation of the Add Feed dialog box:
Listing 8. Add Feed dialog box implementation
<div dojoType="dijit.Dialog"
id="add-feed-dlg"
title="Add Feed"
closeNode="cancel-add-feed-btn"
style="width: 400px;">
<form onsubmit="return false;">
<table class="table-no-border">
<tr>
<td>
<label for="feed-name">Title: </label>
</td>
<td width="100%">
<div dojoType="dijit.form.TextBox"
id="feed-name"
type="text"
required="true"
value=""
trim="true"
autocomplete="on"
classPrefix="noColors"
class="text-box"></div>
</td>
</tr>
<tr>
<td>
<label for="feed-url">URL: </label>
</td>
<td width="100%">
<div dojoType="dijit.form.TextBox"
id="feed-url"
type="test"
required="true"
value=""
trim="true"
classPrefix="noColors"
class="text-box"></div>
</td>
</tr>
</table>
<fieldset>
<button dojoType="dijit.form.Button" id="ok-add-feed-btn">
OK
</button>
<button dojoType="dijit.form.Button" id="cancel-add-feed-btn">
Cancel
</button>
</fieldset>
</form>
</div>
|
An interesting thing to note about the Add Feed dialog box is how easy it is to implement it out of
widgets and HTML markup, as follows:
- By using the
dijit.Dialog widget, we immediately gain the
ability to open a simple and lightweight dialog box in the context of the existing browser
window.
- By populating
dijit.Dialog with HTML markup and widgets,
we determine how the body of the dialog box is going to look.
- The cancel action can be easily implemented by using the
closeNode attribute.
This way, we do not need to implement the handler to the Cancel button.
- The rest of the implementation is delegated to Dojo, which provides APIs that
let us show and hide the dialog box including its content.
The Controller component
Our blog reader controller listens to events flowing from the view components and
reacts to them by operating on the model. In general, the UI events are:
- New feed selection: The controller needs to load and present the newly
selected feed.
- New entry selection: The controller needs to present the newly selected entry.
- Add feed button activation: The controller needs to pop up the Add Feed dialog
box.
- Clean feeds button activation: The controller needs to erase the feed list.
- The OK button was pressed in the Add Feed dialog box: The
controller should take the entered title and URL and add a feed using them.
The controller interaction with the model has an interesting twist to developers new
to Ajax — it is asynchronous. In general, the JavaScript code running inside the
browser is single threaded. This means that if you want to keep the UI alive and
responsive while the model communicates over the network, you should work in
asynchronous mode. For this reason, the controller also listens to the following model
events:
-
feedDataArrived: Fired by the model when it is done reading a feed. Upon
receiving this event, the controller knows that the feed data is cached in the
model object and can be presented in the view.
-
feedReadFailed: Fired by the model when the communication fails. Upon
receiving this event, the controller knows that the model failed to communicate
with the ATOM feed and an error message needs to be displayed
The upcoming
sections walk you through the controller implementation and
show how to connect to the view components and respond to various events
using Dojo.
Binding to the UI and Model components
Listing 9 shows the general structure of the controller and focuses on the
initialize() method and the two tasks it performs:
- Locating the UI components through their IDs
- Assigning listeners to various events in the model and the UI
Listing 9. Binding to the UI and Model components
var BlogController = {
mHideProgressFunction: null,
mFeedsCombo: null,
mAddFeedDlg: null,
mFeedURLTextbox: null,
mFeedTitleTextbox: null,
mArticleWrapper: null,
mProgressDlg: null,
mProgressDlgMsg: null,
mContentDiv: null,
mContentTitle: null,
mContentPane: null,
mErrDlgContent: null,
mErrDlg: null,
mArticleList: null,
// Initialize the page
initialize: function() {
this.mFeedsCombo = dijit.byId("feed-combo");
this.mArticleList = dijit.byId("article-list");
this.mAddFeedDlg = dijit.byId("add-feed-dlg");
this.mFeedURLTextbox = dijit.byId("feed-url");
this.mFeedTitleTextbox = dijit.byId("feed-name");
this.mArticleWrapper = dojo.byId("articles-table-wrapper");
this.mProgressDlg = dijit.byId("progress-dlg");
this.mProgressDlgMsg = dojo.byId("progress-msg");
this.mContentDiv = dojo.byId("article-content");
this.mContentTitle = dojo.byId("article-title-id");
this.mContentPane = dojo.byId("article-content-pane");
this.mErrDlgContent = dojo.byId("error-msg-content");
this.mErrDlg = dijit.byId("error-msg-dlg");
dojo.connect(BlogModel,
"feedDataArrived",
this,
this.feedDataArrived);
dojo.connect(BlogModel,
"feedReadFailed",
this,
function(errMsg) {
this.hideProgressDlg();
this.cleanFeedUI();
this.showErrorMsgDlg(errMsg);
this.mFeedsCombo.setValue("");
this.mArticleWrapper.style.visibility = "hidden";
});
BlogModel.initialize(this);
this.populateFeedsCombo();
// connect add feed/clean feeds to functions
dojo.connect(dojo.byId("add-feed-btn"),
"onclick",
this,
function() {
this.mAddFeedDlg.show();
this.mFeedTitleTextbox.focus();
});
dojo.connect(dojo.byId("clear-feed-btn"),
"onclick",
this,
this.cleanFeeds);
// dialog box callbacks
dojo.connect(dojo.byId("ok-add-feed-btn"),
"onclick",
this,
this.createFeed);
// Listen to combo box selection changes
dojo.connect(this.mFeedsCombo,
"onSelectionChanged",
this,
function() {
this.readFeed(this.mFeedsCombo.getSelectedValue());
});
// List selection changes
dojo.connect(this.mArticleList,
"onSelectionChanged",
this,
this.updateArticleContent);
},
createFeed: function(event) {
// Create a new feed reference, add it to the model and
// to the feeds combo
},
cleanFeeds: function() {
// erase all feed references from both the model and the view
},
// Updates the page content according the feed xml
feedDataArrived: function() {
// Feed was read. Update the reader with the new information
},
updateArticleContent: function() {
// The selection in the articles list changed, update the article pane
},
// Utility methods
populateFeedsCombo: function() {
// Populate the feeds combo box with the feeds registered in the model
},
readFeed: function(url) {
// Cleans the feed presentation UI and initiates fetching a new feed
},
// Clean feed content
cleanFeedUI: function() {
// Zero the Feed presentation UI
},
showErrorMsgDlg: function(errorMsgContent) {
// Pops up an error dialog box with a specific message
},
showProgressDlg: function(msg) {
// Pops up the progress dialog box with a specific message
}
};
|
The initialize() method shows how Dojo programs locate and
reference UI components (HTML elements and widgets) using their component IDs and two
methods:
-
dojo.byId(id): Looks up an HTML element using its
ID as a parameter. The method returns the DOM object.
-
dijit.byId(id): Looks up a Dojo widget using its ID
as a parameter. The method returns the JavaScript object instance representing the widget.
If you follow our use of the byId() methods in the controller,
you will notice that we use them in the initialize() method
and cache the returned results in member variables. This is not just for performance
reasons, but more for maintainability. The IDs used in the view can change frequently
during the development phase, so looking them up once (and in a well-known location)
makes it easier to maintain the code.
Our controller uses dojo.connect to listen to events in the
view and the model. dojo.connect allows Dojo applications to
attach functions to method invocations in other objects. For example, the following code
fragment attaches the function foo to the
method bar in the object obj:
Listing 10. Listening to events
dojo.connect(obj, "bar", expectedThis, foo);
|
The third parameter of dojo.connect is the object reference to
be used as this in the called method. This can turn out to be
useful if you want to use a template function on different objects.
Note that because JavaScript treats functions as data, it is
possible to pass function objects as parameters without declaring them specifically, as
presented in the following code fragment:
Listing 11. Attaching function object
dojo.connect(this.mFeedsCombo,
"onSelectionChanged",
this,
function() {
this.readFeed(this.mFeedsCombo.getSelectedValue());
});
|
This feature comes in handy if you have many short functions (say, 2 - 3 lines) that are not
used by anything but the connect method. In such a case, declaring the function at its
place of use lets you improve the readability of your code.
Calling back on the Dojo widgets
As seen before, the Dojo widgets drive controller using events. However, how
can the event handlers call back on the Dojo widgets? The answer is simple: Dojo
widgets are simply JavaScript objects with public methods, and all you need to do is
call these methods. Similarly, HTML elements and their attributes get exposed as
DOM objects. As a result, the function showProgressDlg(msg)
presented in the following code fragment can easily set a message into the progress
dialog box and pop it up:
Listing 12. Event handler calling back on the Dojo widgets
initialize: function() {
// setup references to Dojo widgets and DOM elements
this.mProgressDlg = dijit.byId("progress-dlg");
this.mProgressDlgMsg = dojo.byId("progress-msg");
...
}
...
showProgressDlg: function(msg) {
// Use the methods exposed by DOM and the widget
this.mProgressDlgMsg.innerHTML = msg;
this.mProgressDlg.show();
},
...
|
(For more information about the methods exposed by Dojo and DOM objects,
refer to the DOM and Dojo references listed in the Resources section).
 |
The developerWorks Ajax resource center
Check out the Ajax resource center, your one-stop shop for free tools, code, and information on developing Ajax applications. The active Ajax community forum, hosted by Ajax expert Jack Herrington, will connect you with peers who might just have the answers you're looking for right now.
|
|
Getting initialized
At this point, you want to initialize the controller when the page finishes
loading and the Dojo widgets are ready for interaction. In this blog reader case, this
means calling to BlogController.initialize.
Dojo helps you accomplish the initialization tasks by providing a simple on-load
hook mechanism through dojo.addOnLoad. For example, in our controller example, we are using the
following code fragment to kickstart BlogController.initialize:
Listing 13. Controller initialization
dojo.addOnLoad(BlogController, BlogController.initialize);
|
dojo.addOnLoad can accept either one or two parameters:
- When a single parameter is used, Dojo expects it to be the function to execute
when Dojo is loaded.
- With two parameters, it is expecting the first parameter to serve as
this and
the second parameter to be the function to execute when Dojo is loaded.
Note that as a developer, you can also try other approaches. For example,
you could use dojo.connect and bind to one of the dojo object
life cycle methods in a manner similar to the following code fragment. (However, note that this approach is not recommended because the life cycle methods can change in the future):
Listing 14. Another way for Controller initialization
dojo.connect(dojo,
"loaded",
BlogController,
BlogController.initialize);
|
Conclusion
You have learned about the essentials of blogging architecture and looked at two
Atom standards and how they can be exploited by blog reader applications.
You examined how the view and controller of a simple, yet functioning,
Dojo-based reader can be implemented. The next article of the series will walk you through the implementation of the model. Stay tuned!
Download | Description | Name | Size | Download method |
|---|
| Sample blog application | wa-aj-basics2.zip | 18KB | HTTP |
|---|
Resources
About the authors  | |  | Gal Shachor is an IBM Senior Technical Staff Member and researcher working at the IBM Haifa Research Lab, on various topics related to middleware and rich Internet applications. |
 | |  | Ksenya Kveler holds a B.Sc. in Computer Science from the Technion - Israel
Institute of Technology. For the last five years, she has been working as a
software engineer in the IBM Haifa Research Lab, mostly in the fields of distributed systems, simplified application development tools and, currently, medical informatics. Her main interests are Java and Web technologies. |
 | |  | Maya Barnea holds a B.Sc. in Mathematics and Computer Science from the
Haifa University. For the last five years, she has been working as a
software engineer in the IBM Haifa Research Lab, mostly in the fields of
visual application development tools and complex event processing. Her main interests are Java and Web technologies, event processing, simplified programming, and usability. |
Rate this page
|  |