|
software | articles | professional |
| Articles index | |||
Create a Custom Javascript/AJAX Widget with DojoLast updated: 2007-09-18AJAX has been a buzzword for at least a couple of years now. And as expected, there have been numerous JavaScript toolkits developed to aid in the development of AJAX-y websites. Awhile ago, I set out to choose my own toolkit of choice. I soon found myself choosing between Prototype/Scriptaculous, and Dojo. As a Java Swing developer, one thing I love to do is to create my own UI widgets, and mix them in with standard Swing widgets. Dojo, I noticed, provides hooks to perform similar feats. So I settled on the Dojo toolkit. After familiarizing myself with Dojo for awhile, I finally decided to create my own custom widget. A common complaint about Dojo currently is its lack of documentation. The subject of creating a custom Dojo widget provides no exception. So I thought I would track the steps I took in creating my own widget in this article.
The strength indicator, rendered with the password field. How strong is your password?The widget I'll develop in this article is a password strength indicator. This widget will be designed to work in conjunction with a password field; specifically, a password field in which a user is creating a new password for his or her online account. When the user enters a new password into the password field, the strength indicator widget will provide feedback to the user on how secure the password is. The widget will consist of a label and a graphical status bar. The label will display a textual description to the user (e.g. "Weak"), while the status bar will provide visual feedback (e.g. an insecure password would be represented by a short, red status bar, while a secure one might be represented by a long, green bar). The widget will also contain a text element designed to present advice to the user to increase their password's strength (e.g. "The password needs to be longer"). Furthermore, since we're talking about AJAX here, the password-strength logic will reside on the server; as the user enters a new password, that password will be submitted to the server, which will in turn evaluate the strength of the password and pass data back to the client, for rendering by our strength indicator. In my implementation, I included a few other widgets as well. First, as with most account-creation forms, I included both a create-a-password field and a confirm-the-password field to ensure that users don't mistype their created password (a mistake that, owing to the password field's obfuscated characters, the user would likely not detect). I also included a checkbox with which the user could toggle the functionality provided by the strength indicator. There are a few other things to note within this article. First, I assume some familiarity with using Dojo, and JavaScript in general. Second, although this article describes the creation of a password strength indicator, I won't spend much time on the actual algorithm used by the server to determine password strength, nor do I assert that the algorithm I use is by any means the best possible implementation.
The filesystem structure of the StrengthIndicator widget, as viewed within Eclipse. The basic partsAny dojo widget consists of three basic parts: a Javascript component, an HTML component, and a CSS component. This separation of concerns will seem somewhat familiar to developers accustomed to Model-View-Controller patterns. The dojo pattern might be described as Structure-Logic-Style. In addition to providing any necessary logic, the JavaScript component is where the widget as a whole is configured. I named my widget StrengthIndicator. So, in following Dojo's naming conventions, my widget's three components were named, respectively, So where should you place these components? It's easiest to create a directory, named after yourself, your organization, etc, at the same level as your Dojo root directory. Then, within that directory, create a subdirectory called Finally, you'll need one more file called StrengthIndicator-template.htmlLogically (to me, anyway) it makes sense to start with the HTML component. So let's look at that code first:
<div class="strength-outer" dojoAttachPoint="mainDiv">
<b>Strength</b>: <span id="description" dojoAttachPoint="description">${this.description}</span>
<div class="colorbar-outer"><div id="colorBar" dojoAttachPoint="colorBar"></div></div>
<div dojoAttachPoint="advice"></div>
</div>
Contents of StrengthIndicator-template.htmlThis code should, for the most part, look fairly straightforward. The entire widget is surrounded by a div whose ID is strength-outer. A span by the ID of description exists to display a message to the user. The aforementioned "progress bar" is formed by a div belonging to the class colorbar-outer (in order to provide a border around the status bar). Just inside that div is another div, this one with the ID of colorBar. This is the actual div that will vary in length and color in order to provide visual feedback to the user regarding their password's strength. Finally, at the bottom of the main div is another div; this one will display advice to the user when his or her password is judged to be weak.
While most of the HTML should look familiar to you, there are a few dojo-specific pieces. For example, many tags contains an attribute called StrengthIndicator-style.cssNext, let's look at the CSS file:
.strength-outer {
background-color: #FFFFFF;
border: 1px #333333 solid;
padding: 5px;
}
.colorbar-outer {
border: 1px #000000 solid;
margin: 5px;
width:200px;
height:15px;
}
#colorBar {
width:0%;
height:100%;
background-color: #999999;
}
Contents of StrengthIndicator-style.cssAgain, there should be no surprises here for anyone accustomed to Cascading Style Sheets. The borders and paddings are defined for the divs whose classes are strength-outer and colorbar-outer. The latter's margin is also set; its height is as well, since it contains no content and would otherwise default to a height of zero. Finally, div representing the color bar (with the ID of colorBar) is given an initial width of 0%, a height of 100% (to ensure that it fills up the entire 15px height of its parent's container, the div of class colorbar-outer) and a neutral starting color of mid-grey. Note that the width and background color will change as visual indicators.
StrengthIndicator.jsNow, let's look at the JavaScript file:
dojo.provide("sharedplan.widget.StrengthIndicator");
dojo.require("dojo.widget.Parse");
dojo.require("dojo.widget.HtmlWidget");
dojo.widget.defineWidget(
// widget name and class
"sharedplan.widget.StrengthIndicator",
// superclass
dojo.widget.HtmlWidget,
function() { },
{
percent: 0,
description: "0%",
color: "#000000",
templatePath: dojo.uri.dojoUri("../sharedplan/widget/template/StrengthIndicator-template.html"),
templateCssPath: dojo.uri.dojoUri("../sharedplan/widget/template/StrengthIndicator-style.css"),
isContainer: false,
snarfChildDomOutput: false,
setPercent: function(pct) {
// summary: sets the "strength" percentage, and updates the display acordingly
this.percent = pct;
this.colorBar.style.width = pct + "%";
if (pct < 40) {
this.colorBar.style.backgroundColor = "red";
this.description.innerHTML = "very weak";
} else if (pct < 55) {
this.colorBar.style.backgroundColor = "orange";
this.description.innerHTML = "weak";
} else if (pct < 70) {
this.colorBar.style.backgroundColor = "yellow";
this.description.innerHTML = "questionable";
} else if (pct <= 90) {
this.colorBar.style.backgroundColor = "green";
this.description.innerHTML = "acceptable";
} else {
this.colorBar.style.backgroundColor = "blue";
this.description.innerHTML = "strong";
}
},
setAdvice: function(advice) {
this.advice.innerHTML = advice;
},
setVisibility: function(b) {
this.mainDiv.style.display = (b) ? "block" : "none";
}
}
);
The first lines,
The properties mentioned above can be referenced using
StrengthIndicator also contains its own basic properties:
In addition, three functions are attached to the widget:
StrengthIndicator-template.html defined this element: <div dojoAttachPoint="advice"></div> It can subsequently be referenced within the JavaScript file as this.advice. The setAdvice() function does this via: this.advice.innerHTML = advice;
__package__.jsFinally, we need to configure
dojo.kwCompoundRequire({
common: [
"sharedplan.widget.StrengthIndicator"
],
browser: [ ]
});
dojo.provide("sharedplan.widget.*");
Contents of __package__.jskwCompoundRequire() defines the modules; in this case, the StrengthIndicator is assigned to the common host environment. The dojo.provide() call then makes the module available for use.
Using the StrengthIndicatorThat's really all there is to creating a custom Dojo widget. So, how do we use the widget in an HTML page? The same way we would any other Dojo widget; first, we include the widget code at the top of the page:
<script language="javascript">
// ...
dojo.require("sharedplan.widget.StrengthIndicator");
// ...
</script>
Then, we simply create a div and add a dojoType attribute:
<div dojoType="sharedplan:StrengthIndicator" id="strind"></div>
Now, the StrengthIndicator is ready for use. In my implementation, I hooked up the password field to a method called
var pwdFormObj = document.forms["createform"];
var pwdFieldObj = pwdFormObj.elements['password'];
dojo.event.connect("around", pwdFieldObj, "onblur", "testPwdStrength");
Thus, whenever the user exits (blurs the focus of) the password field, the testPwdStrength() function will be invoked. The testPwdStrength() function, in turn, looks like this:
function testPwdStrength() {
var formObj = document.forms["createform"];
var cb = document.getElementById('doPwdStr').checked;
if (cb == false) { return; }
var pwd = formObj.password.value;
if (pwd == "") { return; }
var u = "Dispatcher?action=testPassword&password="+escape(pwd);
var uname = formObj.username.value;
var fname = formObj.firstname.value;
var lname = formObj.lastname.value;
if (uname && uname != "") { u += "&username=" + uname; }
if (fname && fname != "") { u += "&firstname=" + fname; }
if (lname && lname != "") { u += "&lastname=" + lname; }
var bindArgs = {
url: u,
error: function(type, data, xhro) {
if (data.message) { data = data.message; }
alert("An error occurred. " + data + "..." + xhro);
},
load: function(type, data, xhro) {
var xmldoc = xhro.responseXML;
var scoreNode = xmldoc.getElementsByTagName('score');
var score;
var adviceNode = xmldoc.getElementsByTagName('advice');
var advice;
if (scoreNode.length > 0 && scoreNode[0].childNodes.length > 0) {
score = parseFloat(scoreNode[0].childNodes[0].nodeValue);
}
if (adviceNode.length > 0 && adviceNode[0].childNodes.length > 0) {
advice = adviceNode[0].childNodes[0].nodeValue;
}
var strind = dojo.widget.byId('strind');
if (strind) {
var percent = parseInt(score * 100);
strind.setPercent(percent);
if (advice) { strind.setAdvice(advice); }
}
},
mimetype: "text/xml"
};
dojo.io.bind(bindArgs);
return false;
}
The first portion of the function gathers up values from the form. It checks whether the the user has toggled the widget off, or the password field is empty; if either is true, the function simply returns. Otherwise, it begins constructing the URL to which to send the AJAX request. It gathers up values from the username, firstname, and lastname form fields (which the serverside algorithm will use). Then, it creates an array of arguments to pass to the dojo.io.bind() method: the url argument is the newly-contructed AJAX URL; the error argument is a function to be called if an error occurs during the AJAX call; the load argument is the callback function for successful AJAX calls; the mimetype argument is set to "text/xml" to tell the server that we want to receive our requested data in XML format. Finally, we invoke dojo.io.bind() and pass the newly-created argument array, in order to invoke the AJAX call.
The interesting part is the content of the load callback function. This function expects the AJAX call to return XML in a format like the following: <?xml version="1.0" encoding="UTF-8"?> <result> <score>0.25714287</score> <advice>Don't use a dictionary word<br/> The password needs to be longer<br/> Include more uppercase letters<br/> Include more numbers or special characters<br/> </advice> </result>The score tag contains a floating-point number, between 0 and 1, which will be converted to the strength percentage and description. The advice tag contains any advice to the user on making a weak password more secure. The load function parses those values out of the XML DOM, converting the score value to an actual floating point number. It then locates the StrengthIndicator widget by its ID and, if it's found, sets the widget's percent and advice values. The widget will then automatically update.
ConclusionAnd that's all there is to creating a custom Dojo widget. Of course, the nitty-gritty complexity of creating a Dojo widget may vary, depending on the task at hand. But overall, any widget will simply require these steps:
Privacy Policy: Any information collected, whether from online help, registration, or any other means, will be used only for support purposes. Such uses are: to send customers registration information, to verify that a user is registered in order to provide support, and to notify the user if an important or useful bug fix or upgrade has been made. Under no circumstances will customer information be sold or otherwise given to any other party, person, or organization. We're in the business to develop software, not sell people's information. |
|||
| Articles | blog | index |