Funky Town: Inline Field Validation

This post is part of the Funky Town series of articles, where we take ridiculous User Centric Design (UCD) requirements and put them into practice.

In this article, I look at another outrageous business requirement - Inline field validation.

Imagine typing in an email address, and as soon as you step off, the field turns red, and a tooltip style message appears, stating that your input does not meet the proper standard for an email address.



Inline field validation provides many benefits

1. Immediate help for the user
2. Affirmation that the user is doing the right thing
3. Assists complex data input requirements
4. Improves user experience
5. No server round trip required
6. Makes Siebel a joy to work with!

This is the sort of thing you see on fancy web forms, and only BA's will dream this requirement up because Siebel is a simple web application right?. Lets see how we can provide this functionality for them.

[Solution]

This solution will only work for form applets, so open up any form applet, and designate a field that will act as your "email address", for this exercise your field dosnt actually be an email field.

1. Put the following code in your email field control OnBlur handler
validateEmail(applet,id);

2. Add the following browser script to your applet
function validateEmail(applet,id){
    try{
        var sValue = applet.FindControl(id).GetValue();
        var oDoc = applet.FindActiveXControl(id).document;
   var oToolTip = oDoc.getElementById("Tooltip");
        if(sValue.length>0&&sValue.indexOf("@")==-1){//insert fancy email validation routine here
       if(!oToolTip){
           var oToolTip=oDoc.createElement("div");
           oToolTip.innerHTML=" Please enter a valid Email address eg. [email protected]";
           oToolTip.id="Tooltip";
           oToolTip.style.backgroundColor="#fff";
           oToolTip.style.color="#000";
           oToolTip.style.padding="10px 40px";
           oToolTip.style.borderStyle="solid";
oToolTip.style.borderColor="#bbb";
oToolTip.style.borderWidth="10px";
           var oHeader = oDoc.getElementById("SectionHeaderId");
           if(oHeader) {
                oHeader.parentNode.parentNode.appendChild(oToolTip);
                applet.FindControl(id).SetProperty("BgColor", "#ff0000");
            }
         }
        }else{
            if(oToolTip){
                oToolTip.parentNode.removeChild(oToolTip);
                applet.FindControl(id).SetProperty("BgColor", "#ffffff");
            }

        }
    }catch(e){
    }

}

3. Create a section header for your form, add the following in the caption override
<span id="SectionHeaderId">My Funky Form</span>

Re-compile your applet, and type in some junk in your email field. As soon as you step off, your field will turn red, and a message will appear to notify of the error.

Suffice to say, the email routine that I used above is not foolproof ^_*, but you get the idea. A more wholistic solution would look at validation from a general perspective, but that is left to the reader to run with.

[Conclusion]

1. Will it perform well?
Yes, there are no server calls.

2. Is it maintainable?
Yes, if the code is made production ready, and all the validation routines are centralised.

3. Can it be upgraded?
Yes, as it does not use any undocumented methods

and

4. Is it sexy?
Yes, its a BA's dream come true

Funky Town: Sexy Salutation

This post is part of the Funky Town series of articles, where we take ridiculous User Centric Design (UCD) requirements and put them into practice.

[Background]

On the Application Home page screen, Siebel provides us with the ability to close individual Applets, and hide them from the home page. Applets can also be hidden by default in the View definition, and users can display the applets again through the Edit Layout view.

This is actually what a home page actually looks like when you hide all the applets.

To the uninitiated, it looks like the data has disappeared or deleted! Surely this cant pass UAT, yes laugh it off, give your users some training and tell them not to party on the weekends and come to work switched off. However you may take an important design principle to heart - Design for the lowest denominator.

How about we give our users a friendly message that reminds them their data is not goneski, its just not visible.

This message will dynamically appear when there are no applets displayed, and slide down smoothly like an accordian, just to give your users that feelgood feeling, and make them wonder why other parts of Siebel is not sexy like this view.

This solutions relies on a Javascript library called jQuery, which allows the developer to easily select and manipulate DOM elements. Never heard of jQuery? You are probably one of those people who are stuck in a web time warp from early 2000s. Circa 2003, there was an explosion of JS frameworks, that provided web developers a new level of abstraction on JS which provided developers with the tools to achieve easy cross browser compatability, stream line their code, and focus on functionality.

Is it production ready? Dont worry, you'll be in good company. Dont hesitate, get it now, and learn it.

Implementation

1. Download jQuery, and include it into your top object

This can be done in application start or a common Page container web template. If you are taking the latter approach back this file up.

2. Identify the web template that your Home Page View is based on

I should now stress that you should not put any javascript logic in your web templates. Any javascript logic should be offloaded into an external file for performance, maintainability and upgrade reasons (In this case the latter is more important than the rest). Plan this out carefully before you attempt to implement this solution.

3. Put the following code in the external file that handles all your home page behaviour

function j(expr){
return top.$(expr,document);//references the top.$ that was loaded in step 1.
}
j(document).ready(function(){//waits for the home page document to finish loading
try{
if(j("[class*='AppletStyle']").length===1){//if the number of applets rendered is 1 (ie Only the Edit Layout Applet is shown), display our fancy message.
var sText = "Your Home Page data is currently on holidays.

"+
"If you would like to see your data, click on the 'Edit Layout' button, then click ";
var sDiv="

";
j("div.Welcome").append(sDiv);//attach a fancy message
j("div#Msg").slideToggle();//use some jQuery magic to make the message animate by sliding down.
}
}catch(e){
}
});

If you loaded jQuery from your Application, you'll have to re-compile, otherwise, if the library is loaded from your web template, you'll just need to re-start your dedicated client, or restart the server, if you are implementing this on the server.

The "beauty" of this solution is that there is no clunky server side co-ordination, and all the logic is performed client side, so it performance translates to a silky smooth experience.

Conclusion

1. Will it perform well?
Yes, no server side components were abused to deliver this functionality

2. Is it maintainable?
Yes, just like web templates, images and other external configuration

3. Can it be upgraded?
Yes. Dont put your code in your templates. Make the effort to offload this to an external file, otherwise dont do it at all.

and

4. Is it sexy?
It is fun and sexy.

Funky Town: Custom UI Extension

This post is part of the Funky Town series of articles, where we take ridiculous User Centric Design (UCD) requirements and put them into practice.

[Background]

I was commissioned to design a custom Integration framework for a client. As part of this framework, the client needed to have a test harness that allows:

1. The developer to generate a pre-populated SOAP request for any predefined Siebel Web Service, with run time data and authentication headers.

2. Allow the developer to send the request and see the SOAP response in real time in the UI

3. They should also be able to modify the SOAP request on the fly and re-submit the message

4. and all of the above for XML over MQ as well

The design needed to load a run time data into the request, but the test data itself was transient and did'nt need to be saved. The size of the payloads are always unknown, especially when dealing with binary encoded data, the response would be massive, so I needed a way of storing unlimited data on the UI.

The closest thing that resembled what i wanted is the Script Editor Applet (Business Service Script Editor Applet2) under Business Service - Administration. Its just an applet with a massive text area, however the field that it maps to has a physical limit.

The idea was to design a custom IE window that could load the request message based on some predefined integration settings, allow the user to modify the request through a text area field, submit it to the transport, read the response and display it on the custom UI window.

This article will focus on how to build a custom window and handle the two way communication between the custom client and Siebel server. Details around integration points in Siebel will be glossed over.

[Design]

This is what the finished product looks like.



1. The chain of events starts when the user clicks on a button called "Launch Harness" which invokes "TestIntegrationCall" to launch our custom window.

a) On WebApplet_PreInvokeMethod of "TestIntegrationCall", we call a process that Generates Request message

In code it would look like this

switch(MethodName){
case "TestIntegrationCall":
//Generate a sample request XML and store it in a Profile Attribute that can be access by the browser
TheApplication().SetProfileAttr("REQUEST_MSG",[Your Request Msg]);
break;
}


b) On Applet_InvokeMethod TestIntegrationCall, we create an IE Window with the following code

//Browser Declarations
var results;

//Applet_InvokeMethod

switch(name){
case "TestIntegrationCall":
results=window.open('','Test Harness','height=600,width=900,scrollbar=yes,resizeable=yes');
var sendButton = "<input type='button' value='Send Request' onclick='opener.sendServerRequest(this)' /><br />";
var requestXML = "<textarea id='request' name='request' rows='30' cols='50'>"+ theApplication().GetProfileAttr("REQUEST_MSG")+"</textarea>";
var responseXML = "<textarea id='response' name='response' rows='30' cols='50'>"+ theApplication().GetProfileAttr("RESPONSE_MSG")+"</textarea>";

var html = "<title>Test Harness</title>"+
"<body style='background-color:#bbbbbb'>"+info+"<table><tr><td>"+requestButton+requestXML+"</td>"+
"<td>"+responseButton+responseXML+"</td></tr></table><br />"+sendButton+"</body>";
results.document.write(html);
results.focus();
results.document.close();
theApplication().SetProfileAttr("REQUEST_MSG","");
theApplication().SetProfileAttr("RESPONSE_MSG","");
break;
}


2. Using the "requestXML" variable in the step above, the Request message is injected into the 1st text area as part of the IE window creation process.

3. Applet_PreInvokeMethod SendRequest
Do nothing.

4. Applet_InvokeMethod SendRequest
Invoke a browser function that will invoke a server method that will initiate the integration call

This is initiated, from the "Send Request" button on our new created UI, which was created in Step 1b.

var sendButton = "<input type='button' value='Send Request' onclick='opener.sendServerRequest(this)' /><br />";

When the user clicks on "Send Request", it will use the "opener" method to lookup the parent window, which is the Form Applet and call the
sendServerRequest
browser function which is defined on the Form Applet as so.

function sendServerRequest(win)
{
var requestXMLStr = win.document.getElementById("request").value;
theApplication().SetProfileAttr("REQUEST_MSG",requestXMLStr);
applet.InvokeMethod("SendRequest");
theApplication().SetProfileAttr("REQUEST_MSG","");
}

Notice that we pass in "this", which is the handle to our custom IE window, this lets us read the values from our custom window.
The first line grabs the XML request from our text area, and calls a server side event.

case "SendRequest":
//Get Request String from our custom IE window
//Convert to correct format and calls the interface

5. The above code will make the interface call and return the results back to our Form Applet, which will convert the Output to a string and store it in a ProfileAttribute which will be accessed by browser script to inject back into our custom IE window as shown in the next step.

6. Get the handle for the IE window, and inject the results into the 2nd text area

case "SendRequest":
var responseXMLStr = theApplication().GetProfileAttr("RESPONSE_MSG");
results.document.getElementById("response").value = responseXMLStr;
theApplication().SetProfileAttr("RESPONSE_MSG", "");
break;


Here are the sequence of events again

Open test harness

1. Browser PreInvoke TestIntegrationCall - Do nothing
2. Server PreInvoke TestIntegrationCall - Generate Request message and convert to string
3. Browser Invoke TestIntegrationCall - Construct IE window and Request message
4. Server Invoke TestIntegrationCall - Do nothing

Send request

1. Browser PreInvoke SendRequest - Do nothing
2. Server PreInvoke SendRequest - Call interface
3. Browser Invoke SendRequest - Injects the response into our Custom IE window
4. Server Invoke SendRequest - Do nothing

[Conclusion]

We have just designed a complex, yet highly unique and functional test harness to assist developers in their day to day jobs, however the concept of a custom UI window can be extended to any part of the application that requires features that are not available out of the box.

Lets look at our criteria again

1. Will it perform well?
N/A. It is a fit for purpose utility

2. Is it maintainable?
Yes, if the code is made production ready

3. Can it be upgraded?
Yes, as it does not use any undocumented methods

and

4. Is it sexy?
It is a no contest, it is like beauty and the beast.

Funky Town: Friendly Siebel Applets

This post is part of the Funky Town series of articles, where we take ridiculous User Centric Design (UCD) requirements and put them into practice.

[Scenario]

The business wanted yet another button on a common applet, but the applet was already crowded with buttons (pictured below). The plan was to consolidate the functionality behind the different buttons, and reduce the clutter on this applet, as it was becoming a real eye sore.



I advised on removing the last two buttons, as they were too specific and its appropriateness was questionable, but i was told that users loved those buttons as they are the two most heavily used buttons in the application! Since the client was paying my wages, who am I to argue?

The second idea was to combine the 3 search buttons into one, since they all perform the same basic functionality "Search"

"Search Client Id"
"Search Client Name"
"Search Client Address"

The problem was, in the current design, the above buttons each invoked a separate VBC Search applet which called an external interface that only allowed mutually exclusive queries by Id, Name or Address, which is why they were designed separately in the beginning.

The agreed design going forward, was to combine all the fields of the above 3 applets into one super search applet, but we needed a way of preventing the user from entering data into different search categories on the same interface call.

The standard approach is to use validation to return an error message when the user hit the "Submit" button. This option achieves the requirement, but it provides a horrible user experience. Imagine the user filling in every section of the form, because it allowed them to, but when they click "Submit", an error pops up, the form is returned to the user, and they have to go through and re-fill the form correctly.

We should aim to design our application to be more intuitive (take inspiration from what Apple did with the Smart phone market =), so the proposed design was to have the fields in the relevant section dynamically became read only or read write as the user typed, this will block out fields that cannot not be filled in.

[Requirement]

a) There are 3 sections, A, B & C on a Search applet.

When the user begins typing in any one section, all the fields in the other two sections should be disabled.



Eg.
If a user types something in a field under section A, all the fields in section B&C would be disabled.

If a user types something in a field under section B, all the fields in section A&C would be disabled.

If a user types something in a field under section C, all the fields in section A&B would be disabled.

b) When the user clears the all field values from any section, all the fields in the entire applet should be re-enabled again.

[Solution]

In HI, there are two control events that are usable on form applets.

onBlur
onFocus

We will use onBlur, because when the user moves from one field to the next, we can implement logic to dynamically enable or disable the rest of the fields on the applet.

Firstly put in our trigger on each control that is editable by the user, put the following code in the onBlur handler

setSectionReadOnly( [section id] );

[section id] is a predefined identifier that you assign to each section in the form. It can be called anything. In our form applet, there are 3 sections, which we'll call a, b, c.

So for all the fields under section "a", you would put the following in each fields onBlur handler

setSectionReadOnly("a");

For every field under section "b", you would put

setSectionReadOnly("b");

and so on.. what this does, is tell our yet to be created function, that every time the user leaves an editable field, send the current field section to our function to determine the UI behaviour.

Now we define the function that is responsible fore enabling/disabling the fields dynamically.

declarations()
var thisApplet = this;

function setSectionReadOnly(sSection)
{
//control object defining allowable applet search sections
var oCTRL = {};
oCTRL.a = ["Id Type","Id Num"];
oCTRL.b = ["Family Name","Given Name"];
oCTRL.c = ["Address Line 1","Address Line 2","Address Line 3", "Address Line 4"];
var bNotEmpty = false;
try {
//check current search section for existence of a search value
for (var i=0;oCTRL[sSection].length>i;i++){
if (thisApplet.FindControl(oCTRL[sSection][i]).GetProperty("ReadOnly")=="true")
return;//abort if active field is RO
if (ctrl.GetValue().length>0){
bNotEmpty=true;//flag first non empty field
break;
}
}

//if current search section has a search value make every other section RO
if (bNotEmpty){
for (var key in oCTRL){
if (sSection!==key){
for (var i=0;oCTRL[key].length>i;i++){
thisApplet.FindControl(oCTRL[key][i]).SetProperty("ReadOnly", "TRUE");
}
}
}
} else {//if current search section is empty then make the whole applet RW
for (var key in oCTRL){
for (var i=0;oCTRL[key].length>i;i++){
thisApplet.FindControl(oCTRL[key][i]).SetProperty("ReadOnly", "FALSE");
}
}
}
}
catch(e) {
//handle error
}
}


Compile your super friendly search applet, and watch your users frustration disappear =). I'm sure you'll agree that dynamically disabling irrelevant fields, and preventing users from entering invalid data is much more friendly, than allowing them to enter invalid data, and complain that the user got it wrong afterwards.

The implementation in this example, was used to illustrate a working concept, there are numerous ways of optimizing this code to perform better, so it should not be copy and pasted across your application.

[Conclusion]

1. Will it perform well?
Yes, there are no server calls, and all the magic happens client side, saving a round trip to the server.

2. Is it maintainable?
Yes, if the logic is made production ready

3. Can it be upgraded?
Yes, as it does not use any undocumented methods

and finally the litmus test

4. Is it sexy?
Yes, and it is also very intuitive