Wednesday, February 28, 2007

Cross client (Web/Notes) validation using JavaScript

Show n' Tell Thursday

Update 02.03.07

I've been working on a simple method for cross-client validation ever since I discovered how powerful the JS-methods in the Notes client was. Of course, it can't compare to the JS-engines in the web-browser, but you can still have a lot of (geek-) fun with what's available.

Since this is a Show'n Tell Thursday post, it's only reasonable to post the validation-demo database for people to play with.

If you just want to se how it works, I've prepared two flash-animations demoing how it looks in Notes, and how it looks on the web.

In the Notes client

On the web

For the web, I'm using Dexagogo's lovely validation library. I've also overloaded Notes' _doClick function so that it only runs if the form is valid (look in the database for the code).

In notes, the DOM is too simple to be able to do any real animation. I've made a simple "yellow fade". To achieve this, and to always have the error-messages available to view for the user, I've had to use a frameset. The validation script in notes is pretty crude.

The script that both Notes and Web use

And finally, the database. In Notes, the demo is the "WithValidation" frameset. On the web, open the "FramedValidation" form (forgot to rename the form to a better name before I uploaded the zip). FramedValidation is also used in Notes as the main form.

Other fun things you can do with JS in the Client:

Run LotusScript by triggering Entering/Exiting events
document.forms[0].fieldName.focus();
document.forms[0].fieldName.blur();


Use LS libraries (JS2LS anyone?)

To do this, fill value of field equal to a function-call (e.g. ..value="Call refreshForm()").

In the Entering event on the field, execute the value. Or, you could have a field for the method you're after, and just focus() on the field. You can hide the field with a layer (put it in a subform to make it re-usable).

Fill data in fields across framesets
FrameName.do...rms[0].fieldName.value="value";

Fill data into computed fields
document.forms[0].fieldName.value="value";
(same with editable fields)

Use regular expression to validate fields.

Some of the stuff that I couldn't get to work:

Cross frameset event-triggering.

Using focus() to jump from a Native OS Field (with focus) to a Notes-field. Most people probably use either one or the other type, so this is probably not a big issue.

Click on buttons/actions using click(). They don't seem to be in the Notes' DOM.

Tell me the rest. I've probably (hopefully) just scratched the surface. :)

Update (02.03.07): Per Kevin's request, I've added a form where you can make per-form configurations. I placed a field on the demo-form with a @DbLookup that gets the configuration. Using eval, I convert the config-field to an object.

The structure of the validations is that of an Object literal. Once you understand how it works, it's really quite simple.

Do a google search on "Object literal" or "JSON" for more information.

I've also added a simple menu in notes, and a simple JS-testing frameset.


v0.001 (old)

v0.002 (new)

Saturday, February 24, 2007

Validation with "Yellow-fade" in the Notes Client

I'm not done yet with JavaScript in the Notes Client.. :D






(The fade is a lot smoother in reality. Low framerate on the flash.)

The yellow-fade can be done on the form you're typing in as well, but that wouldn't look nice. The first field with error gets focus. I'm using setInterval and an array of color-codes to animate the background.

There are advantages and disadvantages using framesets for debugging.
Advantages:
- Always visible (layers are absolutely positioned). If there are many invalid entries, it's easy to forget what was invalid (messagebox)
- You can "animate" the background-color independently of the other frames (FrameName.bgColor)

Disadvantages:
- Always visible (takes up screen estate)

What I don't seem to get around, is using notes-fields for the error messages. This means that you see that they are editable fields. Native OS-fields, which can be borderless doesn't seem to be able to have transparent background(?), and I don't have write-access to computed fields with JavaScript.

I could use a hack to execute Lotus-Script. An editable field hidden by a layer.
Set the value of the field with javascript, "status-header;status message", where status-header in the example above would be error. OnExit ( field.blur() ) on the "LS-field", set value of computed fields (split by ";"). The problem with this is that you have to move the "hiding-layer" every time you add content to the form.


27.02.07 Forget the above. I just tried again. It totally works setting computed field values using javascript.


I'm testing out the possibilities for cross-client (web/notes) for the most common validations. The scripts that gives functionality to the validation would be different according to the client (on the web, using framesets is not an option). What would be common would be the syntax for which fields should be validated, and how they should be validated.

To say which fields should be validated, in the current version, I only need to type this:

var doValidationOn = {
"notEmpty": {"ktype":"Customer Type", "forename":"First Name"}
}

The syntax is:
{
"name of validation" : {
"stored name of field":"Title to show",
"etc":"etcTitle","and so":"on.."
}
}

I think there are smarter solutions. I'm also going to try to add a className-attribute on all fields that's inn an array onload. Although these className's will have no effect on the field in Notes, they can be used on the web for "Really easy field validation", and since every element in the client is an object, I can (probably) use that property to control the notes validation.

var fieldValidation = {
//name of validation : {"stored field":"field title"}
"required" : {"first_name":"First name","last_name": "Last name"},
"date" : {"date_of_birth" : "Birthdate"}
}

By the way, I'm sorry that i have no structure in my posts.. It's all down to lazyness. My plan for this post was just to post the flash, but then my head started butting in.. :[

Tuesday, February 20, 2007

Javascript in Lotus Notes Applications

To get info about a "javascript object", for instance the form, make an action button (client javascrip) with this code:

var f = eval(prompt("State the object (e.g. window, document,forms[0])","document.forms[0]"));
var str="";
for(item in f){ str+=item+" = "+f[item]+"\n"; }
alert(str);

This code puts up a prompt asking for the name of the object.

Example (using document.forms[0]):
Using window:


As far as I can tell, you have access to the form elements as you would on the web. You can also override @Command([FileSave]) with the onSubmit event (client->javascript).

If you have five fields named field1, field2, field3, field4, field5.

You can have validate that all the field has values onsubmit:
var allHaveValues = true;
var f = document.forms[0];

for(var i=1;i<6;i++){
if(f['field'+i].value==""){
allHaveValues = false;
alert("field"+i+" doesn't have a value.");
break;
}
}
return allHaveValues; //false if one off them doesn't have value No formula or lotusscript needed. Now.. Someone tell me how to add events in an unobtrusive /dynamic way in notes-applications, and I'll be a happy young lad. :)

Update: It looks like there is full support javascript objects as well.

You can for instance write:
var Form = {
getValue : function(field){
switch(field.type){
case "text":return field.value;break;
case "radio":return getRadioVal();break;
}
},
setValue:function(field, value){
alert("Not yet implemented! You get the point..");
}
}

Thursday, February 8, 2007

Chaining methods in LotusScript - a practical application

Show n' Tell Thursday
When I first saw the proposal of chaining methods in LotusScript, I couldn't connect it with my current knowledge of LS. This probably has something to do with the fact that I'm a copy-paste/reinventing the wheel type of programmer. I just recently started making script libraries. Before that, most agents I wrote, contained all the code it needed.

We have had several internal discussions in our programming department about using classes/not using classes, using standard script libraries for the most common operations and so on. We mostly agree that it's a good idea, but we never seem to get any further than that.

Anyways, back to the topic (sorry).

I'm currently working on a new database for our company's website. One of the tasks that involved, was making a routine that copies documents about out employees from one database, to the website (to avoid doing updating the same information twice, if someone quits/we get a new employee).

When I started writing the agent that should do this (empty local view/copy from employee db) the website with the employee db, I remembered the article by Tim Tripcony.

“What if I could simplify this into one or two lines of code?”.

I first built a script library that contained the methods that I needed (I could probably just have asked a colleague, but ..reinventing the wheel). Then I made sort of an interface-class, that made all the functions in the script-library “chainable”.

What I ended up with was kind of mindblowing for a person that copy-pastes most of his code.
I added a line-break after each method-call, to increase readability.

Dim operations As New ChainedOperations

Call operations._
useCurDb()._ 'selects current db
useVw("someview")._ 'select view
remAllEntr(False)._ 'deletes all entries in view (don't force)
cleanUp() 'sets all temporary variables=Nothing

Call operations.useDb("someserver","somepath")._ 'select a specific db
useVw("someotherview")._ 'select view
copyAllWithFilter("Field|", s.CurrentDatabase)._ 'copy all except those with “Field”=“”
cleanUp() 'clean up </span>


Example Scripts (naming of script-libraries is to easier see structure):
Com
Com.ChainedOperations

The example agent