JavaScript in Domino – Unobtrusive JavaScript and mandatory fields

Finally I found a name for it. Chris Blatnick named it on his blog Interface Matters.

The technique was presented to me by Patrick Kwinten a while back. He as also blogged about it here.

Since then I find the technique really appealing since it in my opinion makes the coding in domino a lot cleaner. You can create a javascript library and put all the event management in there instead of having it in forms and subforms. Personally it goes along very well with my belief in separating style and content.

Here’s an example how to dynamically change the behavior on mandatory fields. In this case it uses css to show a red border around the field which is mandatory and is missing a value. I have not included the css here. But it is basic knowledge so it should not be hard to create one your self.

This example requires prototype.js

First a function is used to get a Hash of mandatory fields. In this case the mandatory fields are different depending of a previous selection in a field. The selections are “IBAN”, “US-ABA”, “SE-BG”…
There is a function for this so that the source of the information easily can be changed. Later its easy to implement a configuration document and fetch the information via ajax.

function getMandatoryFields(){

var mandatoryFields = {

"":{"mandatory":["Tx_BankName","Tx_BankID","Tx_AccountNumber
"IBAN":{"mandatory":["Tx_BankName","Tx_BankID","Tx_AccountNumber"]},
"US-ABA":{"mandatory":["Tx_BankName",
"Tx_BankID", "Tx_AccountNumber"]},

"SE-BG":{"mandatory":["Tx_AccountNumber"]},
"SE-PG":{"mandatory":["Tx_AccountNumber"]},
"SE-Clearing":{"mandatory":["Tx_BankName",
"Tx_BankID","Tx_AccountNumber"]},

"DE-BLZ":{"mandatory":["Tx_BankName", "Tx_BankID","Tx_AccountNumber"]},
"CANADA":{"mandatory":["Tx_BankName",
"Tx_BankID", "Tx_AccountNumber"]}};

return mandatoryFields;

}

Here’s the code:
Function refreshMandatoryBankInfo(accountType, mandatoryFields){

//Get the list of fields to the specific account type
mandatoryList = mandatoryFields[accountType].mandatory;

//This is not very modular for now.
var allFields = ["Tx_Recipient","Tx_BankName","Tx_BankID","Tx_AccountNumber"];

//Now we reset all fields. Removing onFocus and onBlur events and styles for mandatory fields
for (var i = 0; i < allFields.length; i++){

$(allFields[i]).setAttribute('onFocus','');
$(allFields[i]).setAttribute('onBlur','');
$(allFields[i]).removeClassName('mandatoryField');
$(allFields[i]).removeClassName('required');

};

//Go through all mandatory fields and assign an onFocus and onBlur event.
for (var i = 0; i < mandatoryList.length; i++){

Event.observe($(mandatoryList[i]), 'focus', mandatoryOnFocusEvent);
Event.observe($(mandatoryList[i]), 'blur', mandatoryOnBlurEvent);

//Set initial class for the field.
if (IsEmpty($(mandatoryList[i]))){

$(mandatoryList[i]).addClassName('mandatoryField');
$(mandatoryList[i]).addClassName('required');

}else{

$(mandatoryList[i]).removeClassName('mandatoryField');
$(mandatoryList[i]).removeClassName('required');

}

}

}

//Function to check if field is empty
function IsEmpty(aTextField) {

if ((aTextField.value.length==0) || (aTextField.value==null)){

return true;

}else {

return false;

}

}

//The onFocus function
function mandatoryOnFocusEvent(e){

//Find source element (field which event was triggered) read more about the technique here. The difference is due to different handling in ie and mozilla.
if (typeof e == undefined) {

var e = window.event;

}
var source;
if (typeof e.target != undefined) {

source = e.target;

} else if (typeof e.srcElement != undefined) {

source = e.srcElement;

} else {

//Since it is a target type we do not handle, exit function
return true;

}

//Set class for active field (You can define a special style to show in which field the cursor is placed).
source.addClassName('activeField');

}

function mandatoryOnBlurEvent(e){

if (typeof e == undefined) {

var e = window.event;

}
var source;
if (typeof e.target != undefined) {

source = e.target;

} else if (typeof e.srcElement != undefined) {

source = e.srcElement;

} else {

return true;

}

//Remove class for active field
source.removeClassName('activeField');

//Different handle for text and textarea and a select object
if (source.type =="text" || source.type =="textarea"){

if (IsEmpty(source)){

source.addClassName('mandatoryField')

} else{

source.removeClassName('mandatoryField')

}
source.value = Trim(source.value);

//Check if select statement is empty. Select-one is for on selection if you have a multiple selection object test for select-multiple. The test if it is empty may then have to be rewritten to work.
}else if (source.type =="select-one"){

if(source.options[source.selectedIndex].text == ""){

source.addClassName('mandatoryField')

}else{

source.removeClassName('mandatoryField')

}

}

}

How the function is called:
refreshMandatoryBankInfo($F("Tx_AccountType"), getMandatoryFields());

Advertisements

Easy implementation of multiple configurable global stylesheets

The concept is to have a profile document with a multi value field containing the stylsheet path. First entry has the lowest priority the last the highest priority.

First create a profile document and a multi value field
In my example the profile form is named “(DbSetup)” and the field is named “globalCSS”.

In the Form you add the following code into to the “HTML Head Content” form object. Explanation follows.

startTagCSS := "<link rel=\"stylesheet\" type=\"text/css\" href=\"";
endTagCSS := "\">";
s := @Name([CN]; @ServerName);
@If( s= "" ; varGlobalCSS:="css/global.css";varGlobalCSS:=@GetProfileField("(DbSetup)"; "GlobalCSS") );
@Implode((startTagCSS) + varGlobalCSS + (endTagCSS);@NewLine) + @NewLine

First I define the start and end tags. I then try to find out if it runs local or on a server. If it is local it gets the css path from the local application it self, otherwise it gets it from the profile document. Needed that to be able to debug without network access.

Then, on each entry in the GlobalCSS field, the start and end tag is added in front and back of the path. By using @Implode and @NewLine each entry will be separated with a new line. Last but not least I add an extra new line after. To make it look more readable.

If the field GlobalCSS contains [http://myserver.com/css/global.css”, http://myserver.com/css/second.css, http://myserver.com/css/third.css%5D we would get the following in the “<head>” tag on the web page.

<link rel="stylesheet" type="text/css" href="http://myserver.com/css/global.css">
<link rel="stylesheet" type="text/css" href="http://myserver.com/css/second.css">
<link rel="stylesheet" type="text/css" href="http://myserver.com/css/third.css">

This is only for absolute paths, since the relative path changes depending on if the document is new or already exists.
In this case each entry has to be tested for the existence of http or “//” in the beginning of the path and add “../” in the beginning if it is not a new document. Hint use @IsNewDoc in combination with @Transform.

Logout button

When using session based authentication on the web, it is possible to create a logout button in a simple way.

Just append ?logout to the URL path. By using redirectto it is possible to forward the user to a logout page. Only catch, since you then are logged-out, the page you want to show must be available for the user anonymous.

Just create a button with the the following syntax in the onClick event:
window.open(pathToDb + "?logout&redirectto=//" + logoutURI ,"_self")

“_self” opens the url in the current window.

Pitfalls:
If you want to redirect to the same application (logout and get the login dialog directly) don’t forget to end logoutURI with a slash “/”.

This would logout and direct you to the login page again:
window.open("//ldserver.com/webapps/testdb.nsf?logout
&redirectto=//ldserver.com/webapps/testdb.nsf/","_self")

One line @ReplaceSubString for lotusscript

[Update: Read the comments for a better solution with short strings (unverified)!]

Had this problem removing single characters in a string. I don’t like the idea to use strRight() and strLeft() since they can only handle a single entry of the search string.

There are a lot of different solution for this. I have also made my own lotusscript function for @replaceSubString, as every one else. All to complex since Release 6.

Some split and join will do the trick. This is my one liner:
resultStr = join( split( sourceStr, fromStr ), toStr )

The replace is case sensitive.

Example replacing word:
ReplaceSubString Example1

Button code:

Dim ui As New NotesUIWorkspace
Dim uidoc As NotesUIDocument
Dim doc As NotesDocument
Set uidoc = ui.CurrentDocument
Set doc = uidoc.Document
doc.result = Join(Split(doc.text(0),doc.Replace(0)),doc.replacewith(0))

Example removing character:
ReplaceSubString Example2

Or make a function out of it to make it more readable:

Function replaceSubString(sourceStr As String, fromStr As String, toString As String) As String

replaceSubString = Join(Split(sourceStr,fromStr),toString)

End Function

Custom twistie size

A lot to do lately. No time to blog.

But this short tip I’ll manage to squeeze in.

From designer help 7.0.1:

The triangular icons that indicate a row or a section may be expanded or collapsed are called “twisties.” Twisties are green in Notes and blue on the Web. You can customize these icons by importing your own images. You import two images into one graphics file. The first image replaces the “expand” twistie and the second replace the “collapse” twistie. A pair of images for customized twisties should result in a .gif file that is 33 x 16 pixels — that is, two 16 x 16 images, with a 1-pixel divider.

Creating an images with that size will not produce the twisitie look you expect.
Expecting something like this Twistie 33×16 You will get this: Bad Twisitie!

The size does not match. The correct size should be 25 x 12 two 12 x 12 with a 1-pixel divider.

Why I like Domino

My colleague Patrick sent me a very interesting blog entry about Sharepoint and what it takes to master it.

Interesting reading for every one who wants to know a little more about the product and what it is like to work with it.

Read it?… Ok!
Now you now why I’m happy to be a Domino developer. I suppose we can expect another rip and replace version from Microsoft.
I can’t figure out, why developer keep up with a product management like that? You constantly have to relearn. The experience and knowledge you get is partly made useless with every new version forcing you to learn the basics again. It’s a waist of resources!
I do like challenges. But I do not like to have to throw away my knowledge every time a new release gets out.
No, better stick with things that tends to evolve instead of revolving.

I know a customers developing there own products to overcome these obstacle. After a Biztalk upgrade all their plug-ins were useless, since the api was rewritten. So the decision was made to develop something more independent. Nice to see that Microsoft value them so much, that they get an award for it. But it also shows that some customer are aware of the risks with Microsoft development , you should always consider the fact that you may loose your development investments, when upgrading.
Is that something that is considered in TCO and ROI calculation? Apparently not, the install base would be considerably smaller then.

Microsoft seams to make everything they touch to a complex non transparent issue at the moment. Take the OOXML draft with 6000+ pages. Why so complex?
Microsoft had a very good start with DOS. It’s was simple and easy to use. Basic was also easy to use, not very useful, but simple.
I believe Microsoft lost the word simple after Word 2.0.

In the blog entry there is a list on technologies you have to master to customize Sharepoint. I wounder how that list would look for Domino and Quicker.

First from the Real World software development blog listing:

1. SharePoint
2. SQL Server
3. Internet Information Server
4. Active Directory
5. File Stores
6. Indexing
7. Software Development
8. Search Engines (To help customize the brutal search built into it)
9. Database Design and Development
10. XML
11. .NET 2.0
12. ISA Server
13. Master Pages

And for Domino with Quickr:

  1. Domino server
  2. LotusScript
  3. Formula
  4. Java
  5. JavaScript
  6. XML

(Comparing Domino and Quickr and Sharepoint with each other is not fair, but comparing Microsoft products against Lotus products have never been fair, either way)

What struck me most is not the fact that there is a big difference in the number of lines, but he fact there is only one product/system to learn. No SQL server, no IIS… only Domino!

Now I know why I like Domino, it is simple to manage and easy to use. Developing application can be quick and simple or hard and complex. You as a developer have the choice.

At the moment I feel lucky not having to learn another system, being able to concentrate on things I find interesting. Namely perfecting my Java, JavaScript and the new (not rewritten) functionalities in Domino 8.

Maybe that’s why so many Domino developer have time to blog 🙂

Some issues when installing the Quickr QIssues from SNAPPS

Tried to install QIssues from SNAPPS this week at a business partner event (Moment Quickr Partner Teknik utbildning) in Kista, Sweden.

Focus off the event was to learn how to install and customize Quickr.

The course were held by Rob Enright a great teacher even though his music taste needs getting used to 😉 . A lot of hands on, which I like a lot. Good step by step instructions even though the material had some small obstacles to overcome, as typos and some order issues.
Nothing really big and nothing that reduced the overall experience. On the contrary, in this case, the faults enriched my overall experience. Finding and correcting simple faults is a great way to learn.

During the class, which was the first time i installed Quickr and the first time I tried to install the free Quickr templates from SNAPPS, I did have some problems installing it. The documentation did not contain all the necessary steps to get it to work on my Quickr installation.
Here’s what I did!

Now I only have to learn how to create my on templates.

Another thing I learn is that the user experience for quicker on Domino and WebSphere shows some big differences. I believe it would have been a great idea to put the developer in the same room from the beginning, and use the same team for defining usability and functionality.

It is a little bit sad that when trying to explain Quickr for a customer, there is going to be a lot of “but” and “will be better soon”.
Something that is confusing for the customer is hard to explain and harder to sell. Great product great vision, but not 100% ready in that respect.

Hope the usability and functionality will near each other sooner then planned.