Wednesday, February 06, 2008

SnTT : Dojo Dialogs for validation and help

Greetings,

It looks like 2008 started of nicely judging by the post and comments on the blogsphere in January. While most of you were getting insane brain dumps of all things Lotus and quite possibly bleeding yellow, I was doing the same as Stuart. I was also getting started on a new project, toiling away in the southern hemisphere - where we have experienced one of the wettest summers in the 10 years that I been in Sydney

One of the interesting announcements was the support for Dojo in the Domino 8.5 server (although seeing it is used in quikr I was expecting it at some point). Dojo has been on my list of things to look at for a while. I know that others prefer different tool kits and I've had a look at some of them. I guess knowing that dojo will ship with Domino is some indication that it will be better supported and less gotchas. So if you have only time to learn one, then dojo is a safe bet for Domino developers.

I've wanted to use dojo but never had a reason to use Ajax. For the new project there was specific functionality that I wanted and dojo looked like it could provide it. My requirements were that firstly I wanted to have a validation dialog that shows the user, in one hit, all of the fields that failed validation and why. I also needed to check that a user name wasn't already a registered user in the NAB (Domino Directory). I also wanted this to be cross browser and I also quite liked the way that lightshow and quickr fade the background and have a modal type effect where the user can't access the rest of the page.

This post explains the recipe and how to implement this is your own applications using dojo 1.0.2. If you want to see this in action I've linked to a flash movie. (my first effort in using Camtasia - thanks Carl)

Step1. download Dojo and mix with Domino.

Firstly, download the Dojo toolkit. I used 1.0.2. Then use the ridiculously easiest WebDAV method for uploading the toolkit into a database (really it's so easy that it almost hurts). Check out the instructions on the award winning bloggers website ( BTW, congratulations to Jake). Just make sure that 'design locking' is enabled.

Secondly, (and optionally) you can the run the Dojo test harness <dojo-root>\dijit\tests\runTests.html just to check that the installation is fine and dandy.


Step2. Libraries

Next we will need to include the appropriate dojo libraries for our use. You do this in the <HEAD> tag for your form.


</script>
<script type="text/javascript" src="http://dev.bonesbeyond70.com.au/development/bonesbeyond.nsf/dojo102/dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script>
<style type="text/css">
@import "http://server/development/bonesbeyond.nsf/dojo102/dojo/resources/dojo.css";
@import "http://server/development/bonesbeyond.nsf/dojo102/dijit/themes/dijit.css";
@import "http://server/development/bonesbeyond.nsf/dojo102/dijit/themes/tundra/tundra.css";
</style>
<script language="JavaScript" type="text/javascript">
<
!--
dojo.require("dijit.Dialog");
dojo.require("dijit.form.Button");
dojo.require("dojo.parser");
dojo.require("dijit.ProgressBar");
var baseurl = "http://server/development/bonesbeyond.nsf/";
// --
>
</script>


Step 3 - Presentation and Dialog place holders

You'll also need to add the dojo styling to the Body tag as a class attribute on the Domino Form

"class=\"tundra\""

Add your fields into the form as you would for a web application. The validation will be done in JavaScript so you can leave out validation formulas.

Then we'll need two define the actual dialogs html, in this case I need two. One for a progress dialog so that if the web server, the directory query or connection is slow the user knows that something is happening. The second dialog will then display the results of the validation. We do this by defining two div areas that are hidden and displayed by dojo. Take note of the dojoType and id attributes.

<!--
dialog box for the progress
-->
<div dojoType="dijit.Dialog" id="dialog1" title="Validating your registration" loadingMessage="loading" >
<div id="progress">
<center>
<p>Please wait while we check your registration details.</p>
<div dojoType="dijit.ProgressBar" style="width:300px" jsId="jsProgress" id="downloadProgress">
</center>
</div>
</div>
</div>

<!--
dialog box for the error messages
-->
<div dojoType="dijit.Dialog" id="dialog2" title="Validating your registration" loadingMessage="loading" >
<div id="errors">
<img src="design/bb70/$file/error.jpg"/>
<div id="errorContainer" >
</div>
<button dojoType="dijit.form.Button" type="submit">OK</button>
</div>
</div>

Finally we then need a button to call a JavaScript function that handles the validation.

<input type="button" class="login-button" value="Register" onclick="validateForm()">


Step 4 - Validation

The validateForm() functions, starts by showing the progress dialog (dialog1) so that immediately the users knows that something is happening. You remember that I need to check if a user is registered (or not!). In the script this only happens if the username field contains a valid value. If the user name is empty I add the mandatory error message to the stack, skip checking the NAB and then continue to validate the rest of the form.


/*
* function to check mandatory fields, field properties and username availability
*/
function validateForm() {
var un=document.forms[0].username.value;
var errors = false;
var isavailable = true;
var user_msg = "";

showDialog();

if (un=="")
{
errors = true;
validateRestForm(errors, isavailable, user_msg);
} else {
if (isValidUserName(un)) {
dojo.xhrGet({
url: baseurl+'validateUser?OpenAgent&username='+un,
handleAs: "json",
load: function(responseObject, ioArgs) {
isavailable = responseObject.user[0].isavailable;
validateRestForm(errors, isavailable, user_msg);
}
});
} else {
errors = true;
user_msg = "
<b>Username</b> contains one of the following invalid characters<br/> space / \ + & % @ # * ( ) ! $ [ ] <br/>";
validateRestForm(errors, isavailable, user_msg);
}
}
}

Step 5 - dojo.xhrGet and JSON

You might have noticed the dojo.xhrGet. In order to determine if a user is registered or not I have an agent that queries the Domino Directory ($Users) view and returns a JSON object that indicates if the user exists already. Note I had to add a 2 second delay in the agent to make the progress show.

{ "user": [{"isavailable": "false" }] } or { "user": [{"isavailable": "true" }] }

The validateRestForm() then continues to validate the form and the last steps are to open the dialog. I won't bother you with all the validation, but enclosed is the last field validation and the main flow. If there are no errors then the hideDialogs() function is called. If there are errors then the refreshDialogs() hides the progress dialog and display the error dialog (dialog2).

racgp = document.forms[0].racgpnumber.value
if (racgp == "") {
errors = true;
msg = msg + "RACGP/ACCRM "+mdt;
}
else
{
if(isValidRacgp(racgp)==false)
{
errors = true
msg=msg+"RACGP/ACCRM number is invalid
";
}
}

if (errors) {
var ob = document.getElementById("errorContainer")
ob.innerHTML = msg
refreshDialog();
}
else
{
hideDialog();
document.forms[0].submit();
}

}

Below are the functions that Hide, Show and Refresh the dialogs

/*
* show the progress dialog for slow connections and busy servers
*/
function showDialog() {
var dlg = dijit.byId('dialog1');
dijit.byId("downloadProgress").update({indeterminate: true});
dlg.show();
}

/*
* refresh the progress dialog with the error dialog
*/
function refreshDialog() {
var pg = dijit.byId('dialog1');
var dlg = dijit.byId('dialog2');
pg.hide();
dlg.show();
}

/*
* hide dialogs
*/
function hideDialog() {
var dlg = dijit.byId('dialog2');
var pg = dijit.byId('dialog1');
dlg.hide();
pg.hide();
}


Step 6 - You need HELP...(I've been told that before!)

The other requirement was that I had was to populate some context sensitive help. I used xhrGet and the dialogs to achieve this. The benefit of this approach is that users only pay the download penalty if the help is actually needed.

In terms of how this is implemented, the HTML dialogs are the same as the validations - with the exception of a help icon rather than an error icon and a different style. The help anchor link sends the unique helpid to a function that assembles it into a call.

<a href="#" onClick="getHelp('help+standard+drinks');return false">Standard Drink</a>

This time JSON is not required as the content is stored as HTML/Rich Text in the CMS (note the handleAs:text). The getHelp agent just queries the notes content document, gets the HTML and sends it to the dialog.

/*
* function to retrieve context sensitive help
* from the content, stored in the application
*/
function getHelp(helpid) {
showDialog();
dojo.xhrGet({
url: baseurl+'getHelp?OpenAgent&helpid='+helpid,
handleAs: "text",
load: function(response, ioArgs) {
var ob = document.getElementById("helpContainer")
ob.innerHTML = response
refreshDialog();
}
});
}


...and that's it.

When time permits, I'll en devour to produce a simple standalone database for download. I hope that entry will be of use to those of you that are hesitant to use dojo or ajax for your Domino web apps. I've shown that you don't need to use all of the dojo toolkit and widgits but that you can on a case-by-case basis use bits of the toolkits to enhance you web applications user experience. In fact I also used the JonDesign's Smooth Gallary 2.o (which is based on mootools) for the rotating images in the banner, so there is no reason why you can't mix and match toolkits when required.


4 comments:

  1. You'll also want to get Viktor Krantz's domino view grid (built as an extension to Dojo grid in 1.02). It interprets Domino view URL syntax. It was part of our session The Great Code Giveaway at Lotusphere. http://viktr.com

    ReplyDelete
  2. Rob , thanks for the tip. If I need to parse view URL's you can be sure that I'll be reusing Viktors code - I'd be crazy not too! :-)

    ReplyDelete
  3. Anonymous3:08 am

    hej, thank you for the article, it will help a lot jumping on the Dojo wagon.

    Ofcourse I am curious efter a working example as download.

    KR // Patrick

    ReplyDelete
  4. Richard6:27 pm

    Thanks for this - I took some of it to use in my login functionality, although I had my login form in a dialog to begin with.

    ReplyDelete