Friday, November 21, 2008

Discovering Dojo for Domino: Part 4 - Event Handlers, the DOM and Data Stores

Work and family have been pretty busy, so this next post has taken a little longer than I expected

OK, so dijits are pretty and all web 2.0-ish, and the date picker is nice, but there is more to dojo than that. The whole point of a javascript framework is to simplify common tasks and allow you to extend your application.

Basic event handling


Firstly, a common task on a web form: the user clicks a radio button and some of the fields are hidden or shown, depending on the choice. No too difficult, but tedious if you want to change the properties on multiple items. With the dojo.query method, this becomes alot simpler:

Step 1  Set the event handler for dojo. Your field HTML Attributes become:
"dojoType='dijit.form.RadioButton' onclick='clickRadioButton' "
Notice that I have assigned a text string to the onclick event, not called a function directly. This is because dojo provides event handlers which can override and extend the default event handlers. This is the function which will be called when the event fires.

Step 2  Load the dijit code in your JS Header
dojo.require("dijit.form.CheckBox");

Step 3  Add a function to your script library to handle the onClick event. For example:
function clickRadioButton(  ){
if( this.getValue == 'Hide' ) {
dojo.query('.myClass').style('display', 'none' );
} else {
dojo.query('.myClass').style('display' , '' );
}
}
this within the function is the dijit object, and has access to all the dijit functions, including getValue. This useful function gives you pretty much a value you can test as you would in Notes, so this.getValue will give you the following:





dijitEventReturns
TextBoxonChangeText within the field
DateTextBoxonChangea Javascript date/time object
TimeTextBoxonChangea Javascript date/time object
CheckBoxonClickCheckbox label if being selected, false if being unselected
RadioButtononClickthe label of the selected radio button
FilteringSelectonChangethe alias of the selected option


Accessing the DOM


The dojo.query function takes any valid CSS3 selector (e.g. '.myClass', '#myDiv' , 'TD' etc) and returns an array of node elements. You can chain queries to get items nested within others. Dojo has functions which modify element properties and these can modify all node elements in a collection without the need to iterate over each item in turn. So
dojo.query('#myDiv').query('.myClass2').style('display' , 'none' );
would get all elements with the class 'myClass' inside the div 'myDiv' and set they display property to 'none'.

There are a range of functions for getting and setting various node attributes which can be used in this way.


Filtering Selects and Data Stores


A Filtering select is a dojo dijit like a combo box. As discussed in my previous post, there are problems with using a filtering select with a Domino dialog lists because they generate incomplete HTML.

What dojo actually does is parse the DOM and load your select options into an internal data store and point the dijit to that data store. Examining the dijit object using Firebug, I found that the options are loaded, but they contain a trailing end of line character (\n), which breaks the select in firefox but appends a trailing space in IE. Neither is pretty. I wrote the following function to 'fix' the data in the data store:

function fixSelect( dijitID ){
// get the data store used by the select
var thisStore = dijit.byId( dijitID ).store;

// function to update store items
var itemUpdate = function(listItem){
if( listItem.name[0].indexOf( "\n" ) > 0 ){
listItem.name[0] = listItem.name[0].split( "\n" )[0] ;
}
};
// function to handle errors
var gotError = function() {
// your error handling goes here ;
}

// now call the fetch.
var items = thisStore.fetch( {
onItem: itemUpdate,
onError: gotError
});
}


This looks a little complicated, because the fetch method is asyncronous. This means we cannot simply call store.fetch, assign the results to a variable, then iterate over the results. Instead we pass fetch(() the function to call as each item is returned (there are also ways to work on the complete item set). The first parameter passed to the function is the returned item, which our function can then do something with.

The other alternative is to use a text field, define it as a FilteringSelect dijit in the HTML Attributes and point it to a data store you have created yourself. This is what you will need to do to use AJAX to replace your list choices.

Data stores are a powerful construct with what seems like a whole api of their own and I have only just skimmed the surface so far. I intend to post more when I get my dynamic picklists working (by this I mean AJAX updates of the select choices depending on a choice in another field).

Some more on using DateTextBox


The date format used by dojo is yyyy-mm-dd, and dates are stored in the text field (and returned to Domino) are in that format. You can change the display format or let dojo detect your users locale and use the appropriate format, but you can't change the format in which the data is submitted.

If you want to pre-populate your field with a date (such as @Today) you actually need to split the date and re-format it into the appropriate string first

Monday, November 17, 2008

Discovering Dojo for Domino: Part 3 - Dijits for Domino

The next thrilling episode in my dojo adventures ...

Firstly, some references: the three primary references I used are The Book of Dojo - Part 2 which gives you an overview of dijits and how they work in general, the Dojo Campus - Feature Explorer which has examples and shows the related code, the dojo API Reference. I also found the following part of the dojo campus through Google: http://docs.dojocampus.org. I can't find a link to it on the main DojoCampus page, but it's very useful.

To create digits, you can add to your http markup, or you can create components programmatically.

Example through markup (from Dojo Campus):
<input id="q01" type="text" name="firstname"
value="testing testing"
style="width: 100px;"
dojoType="dijit.form.TextBox"
uppercase="true" />
For now, I'm going to leave programatic creation alone. While this would create the visual elements, I'm not sure they would be saved to disk if you are submitting a standard form to your server (rather than POSTing it to an agent). I haven't investigated though and this may be a valid alternative in some circumstances.

The API reference lists all the methods and parameters. Methods include standard event handlers (onClick, onChange, etc), and there are a whole lot of parameters, but some of the most useful are things like: class, style, uppercase and trim. For example, setting trim=true will have the text box automatically remove leading and trailing white space - no extra coding required.

As with the core, you simply need to reference the dojo script libraries in your form's HTML Head Content (see my previous post. You also need to 'dojo.require' the appropriate libraries for the widgets in your JS Header (see Part 1).

For widgets, the only extra part is the style sheets. There are three common themes, the most common of which is the Tundra theme. These come already installed with 8.5, and are located in [program folder]\Domino\data\domino\js\dojo-1.1.0\dijit\themes.

You need to reference the themes in your HTML Head Content along with the script libraries, so my 8.5 test form now contains:
"<link rel="stylesheet" type="text/css" 
  href="/domjs/dojo-1.1.0/dijit/themes/tundra/tundra.css" />" +
"<script type="text/javascript"
  src="/domjs/dojo-1.1.0/dojo/dojo.js"
  djConfig="parseOnLoad:true, isDebug:true"></script>"
The doc.dojocampus.org site also recommends:
It is recommended you include the theme CSS file before dojo.js to avoid any potential latency issues.
If you use the Tundra theme, you also need to include the class in you form's HTML Body Attributes
"class='tundra' "
First, I created a simple text field. You can add the additional dojo code to either the field properties box (HTML Tab - Other) or the HTML Attributes of the field in the design pane. Both work. The properties box is very small and in general the design pane is easier to work with. The only advantage of the properties box is you don't have to change code if you paste it in there, whereas with the design pane you need to enter a formula which returns a text string. This means you may need to escape double quotes with a backslash, or change double quotes to single quotes.

For the text field, I added the following to the design pane:
"dojoType='dijit.form.TextBox' uppercase='true' trim='true' "
I added the appropriate 'require' statement to the JS Header of the form and I now have a working dijit which automatically converts the text to upper case on exiting the field and trims white space from both ends (and is styed quite nicely too).

Other basic dijits I've tested (check out the Dojo Campus - Feature Explorer for the relevent require function and html attributes):

  • Date picker - just a text field, but can be trimmed or a constraint applied
  • Time picker - I'm not sure I like this widget much, but again you can apply constraints to define the format
  • Number Spinner - Allows user to either type a number or click Up/Down to adjust. Max, Min and number of decimal places can be define.
  • Combobox - like a standard html combo box but users can enter values not in list. I had problems with this - see below.
  • Filtering Select - like a standard combo box but uses 'Google Suggests' style interface. Again this doesn't work 'out of the box' with Domino, unlike most other widgets



Filtering Select & Combo box

Neither of these work properly with Domino. The reason is that Domino (prior to version 8.5) does not generate valid HTML for a <Select> element - it does not close the <Option> tag with </Option>.

The Combobox will work in IE, but not Firefox. I'm not sure about other browsers. The Filtering Select will work in IE if you add an alias to your picklist (so that Domino generates a value property for the <Option> tag. I am working with IE on an intranet for my current project, so I don't need to worry about the other browsers for now. For filtering selects that don't need an alias I have modified the formula to be something like:
list:="":@DbColumn(....)
list + "|" + list
That way I get the alias, IE is happy and the dijit works, and I get the label returned in my field. If you need to (and are braver than me), you can apparantly modify the dojo files to handle the bad HTML. The Dojomino blog explains how. This is a good reference site, but currently a bit out of date. They have announced that they are releasing an update but it hasn't been posted yet.

All of the dijits include the ability to validate the field. Field prompts are styled as a small tool-tip to the right of the field, error messages are displayed as the user leaves the field, and dijits such as the date and time text boxes handle all the heavy lifting around validating the content. However this is UI level validation only, and does not prevent the form from being submitted with incorrect values.

My next challenges:
  1. Modifying the options of combo box 2 when the user makes a selection from combo box 1 and
  2. Form level validation

Stay tuned ..

Saturday, November 15, 2008

Discovering Dojo for Domino: Part 2- Runing Dojo on Domino 8.5

Part one of my adventures in dojo can be found here

Next, I tried to run this on my 8.5 Beta 1 Domino server at home.

First problem: the dojo files are not installed under the /html folder but in a new folder in the file structure /js. I could not load the javascript file at all. After searching through the N&D 8.5 Beta forum I found that you can reference this folder using /domjs.

So my HTML Head Content was changed to
<script type="text/javascript" 
 src="/domjs/dojo-1.1.0/dojo/dojo.js"
 djConfig="parseOnLoad:true, isDebug:true"></script>"

And, of course, the files shipped with Beta 1 are dojo 1.1.0. Fortunately the Dojo Quick Start Guide also links to the earlier version (not that I could spot any differences!)

Everything worked OK until I tried to run the example which retrieves text as json. I kept getting a 404 - file not found error. Rather than wasting time trying to find the typo in the file path I created a simple test agent which output the example json text, because agents and views (using ?ReadViewEntries) are the two most common ways of generating json in Domino.

The only thing I had to do was find out the appropriate mime type for the output. I initially used text/html and everything worked. Firebug helpfully displayed the following message:
Consider using mimetype:text/json-comment-filtered to avoid
potential security issues with JSON endpoints
(use djConfig.usePlainJson=true to turn off this message)

So I modified the HTML head content again, removing the debug parameter and replacing it with the suggested json parameter:
<script type="text/javascript" 
 src="/domjs/dojo-1.1.0/dojo/dojo.js"
 djConfig="parseOnLoad:true, usePlainJson:true"></script>"

and set the content type. My agent is simply:
Sub Initialize

Print "Content-type: text/html"
Print |
{
foo: "bar",
name: "SitePen",
aFunction: function(){
alert("internal function run");
},
nested: {
sub: "element",
another: "subelement"
}
}
|
End Sub

Sweet!

Next - on to digits

Thursday, November 13, 2008

Discovering Dojo for Domino: Part 1- The basics

My journey in learning to use Dojo with Notes and Domino web enabled applications.


I have recently taken over supporting some Domino applications that use dojo, so I thought I had better learn a thing or two about it. I have never used any of the javascript frameworks, and my javascript knowledge is patchy. It's all been learned through 'copy and paste' from other peoples work and the web. Some things I think I know pretty well but there are gaps.

I'm going to write up my experiences as I go. So this is not a structured tutorial and may lead off down the wrong path at times. Feel free to comment on better approaches if you see me doing something wrong.

So - where to start? The best place I found is the Dojo Quick Start Guide

Now I don't just want to learn dojo, I want to learn dojo for use with Domino. And if your are a Notes / Domino developer you know that some things work just like in the web examples and some things don't.

The first challenge is always, where to put the various code snippets that are shown in the examples. I started by creating a form and a javascript script library to go with it. I am going to have a 'dojo template' form with the basics in it, and a template javascript library. Each time I need a new form, I will copy the templates and start from there. In this post I'll describe how to get to the basic 'yeah it works' stage.

The basics: Getting the code


Well I haven't actually done this myself. The environment I am working in has the dojo scripts on the server already (Domino 7.x). You can download the source files and place them on your server, you can put them in your database as a file resource, or you can reference them directly from a CDN - instructions on the download page.

Dojo comes in three parts -
  • core: the basics. Event handlers, DOM manipulation, graphics effects
  • digit: 'Great interface widgits'. This is what I am really after
  • dojoX: complex widgits, graphing and charting

I'll deal with the core first, then move on to digits. I probably won't get to dojoX

How to add dojo to Domino


The Quick Start guide has some simple HTML and scripts you can then play with. I put the example HTML on the form and marked it up as Pass-thru HTML. I could have left the script directly on the page as well. However the point of this excercise is to learn how to use dojo with Domino so I put some of the code in the in a script library and some in the Javascipt Header section of the form.

The following is included on the tutorial first page:
It's important to note that you should not set on an onLoad function directly on the tag when using Dojo. dojo.addOnLoad(someFunc) is prefered over and window.onload = someFunc;
and provides examples such as the following:
dojo.require("dijit.form.Button");
dojo.require("dijit.TitlePane");
dojo.addOnLoad(function(){
dojo.byId("testHeading").innerHTML = "We're on our way!";
console.log("onLoad fires after require() is done");
});

I put the dojo require() and addOnLoad() functions in the js header (passing a funtion name init() to the addOnLoad function), created an init() function in my scipt libary and put any example code in the init() function. I also created a style sheet and embedded it on the form for the simple styles used in the examples.

So the break-up went like this:

HTML Head Content
<script type="text/javascript" src="[relatve file path]/dojo.js"
djConfig="parseOnLoad:true, isDebug:true"></script>


JS Header
[Resource: "libJSMyTestForm"]
dojo.require("dijit.form.Button");
dojo.require("dijit.TitlePane");
dojo.addOnLoad(init)

Script library libJSMyTestForm
function init() {
dojo.byId("testHeading").innerHTML = "We're on our way!";
console.log("onLoad fires after require() is done");
}

I added the HTML to the form, style to my style sheet, opened the form in a browser et voila - working dojo!

Moving on.. just a little


I was able to work through the tutorial fairly easily. It quickly demonstrates getting parts of the DOM and modifying them (throwing in a fade-out here and a slide-left there as well as the standards of replacing innerHTML and classes). It moves through basic event handling (registering an onClick function for a single element and using dojo.connect to register the event for multiple elements) to animation effects (slides, fades, etc) and the events that these animations themselves have. For example animations have beforeBegin and events, allowing you to easily chain animations.

The tutorial then covers basic Ajax - fetching text, posting a form, and fetching json. By the end of an hour or two, you are flying like a pro: dynamically changing your form, adding animations, playing with event handlers and Ajax.

But this is where my javascript knowledge starts to hit it's boundaries. The examples are full of code like this:

var init = function(){
var contentNode = dojo.byId("content");
dojo.xhrGet({
url: "js/sample.txt",
handleAs: "text",
load: function(data,args){
// fade out the node we're modifying
dojo.fadeOut({
node: contentNode,
onEnd: function(){
// set the data, fade it back in
contentNode.innerHTML = data;
dojo.fadeIn({node: contentNode}).play();
}
}).play();
},
// if any error occurs, it goes here:
error: function(error,args){
console.warn("error!",error);
}
});
};
dojo.addOnLoad(init);

Now I know there are different ways of declaring functions - using the FUNCTION statement, assigning a function to a variable and using the new function() constructor, and in-line as shown above.

But why? And where do you use one instead of the other? What is the scope of in-line functions like those above? What if I want to use an existing function in one of my script libraries? simply adding the function name followed by () did not seem to work. Why?

So now I think I have to divert from my journey to dojo enlightenment and seek a side-road through Advanced Javascript.

Can anyone recommend a good reference?

UPDATE: fixed the HTML Head Content - I had closed off my <Script> tag in the wrong place. Sorry for anyone who tried to copy it earlier (note to self: do not manually type code from memory. Always copy and paste working code!)

Monday, November 3, 2008

A New Job - and how to debug javascript in IE

I've been very quiet, because I haven't spent much time on-line in the last month.

It's not so much that I haven't been using a computer but a new job has meant some other priorities in the initial few weeks.

My new job is in many ways similar to my last one - Notes & Domino development. However my new job involves no admin or support work. While I didn't mind the admin work which was strategic - involvement in upgrade or roll-out planning, etc, I don't miss the day-to-day fixing mail routing / reviewing DDM alerts etc.

Other big advantages of my new job:
1) It's closer to home, so travel time is 1/2 hr to 1 hr less per day
2) The standard work day is shorter (7.5 hrs instead of 8 ) and
3) I now catch public transport to work so my husband has taken over the school / kindergarten drop-off and pick-up duties.

All this has meant more time for the family, and less stress on me.

My previous employer was strictly a Notes / Domino shop. The new employer is a larger professional services company, of which the IT consulting group is about 20, with a Notes & Domino team of 2.

Technically, my first challenge with the new job was learning how to drive Outlook. Yes, I have gone over to the dark side. At first, I found it shiny and new, and liked it a lot. It was quick. In Notes, I was nearly always running with the latest beta version, and my last laptop had exceeded the 'you have tweaked too much please re-build me now' stage that Windows XP seems to reach every 2 years or so. On a shiny new laptop with nothing else installed Outlook flies by comparison. And I like changing the color of my calendar entries, little things like that.

But I really miss my conversation threads. And Sametime. My new employer has no live chat services or similar services at all. And I don't like the way you have to turn off the reading pane on *every* folder individually. And the search is pretty ordinary - I don't think attachments are indexed, but I'm not sure.

Actually I miss Notes 8 altogether. The projects I have worked on so far have been general Notes client work for a customer with version 7 and changes to a web app for a customer with 6.5.

What is good is that the company has a strong .Net team and I'm hoping for a little cross-polination: sharing of javascript tools and libraries, standard approaches to UI, things like that.

Latest technical tip: I finally found out how to debug properly in IE. I usually build and debug in Firefox with Firebug, but the web app I was built for IE only, and some of the code was working fine in FF but not IE. So after much searching I found this page which explains how to use Visual Web Developer Express (free version) to connect to IE. It's a pretty awesome debugger. One thing I did find using VWD with Domino is it doesn't automatically recognise javascript libraries referenced from the jsheader on Notes forms. All you need to do to make it work is add a foward slashes and a script tag at the top of your script library and it then works a charm.
//<script>