ShowTable of Contents
Introduction
Have you ever tried to populate a dialog list based on a user's selection on another field? In IBM Lotus Notes, you could use a computed dialog list field with an @DBLookup/formula code and refresh the field's values.
On the Web (prior to XPages), however, performing such action is not as simple. One way to do this is to have multiple dialog list fields (one for each choice) and hide/unhide these fields based on the user's selection. This approach is very tedious and, if you have multiple selection fields, it becomes almost impossible.
A much cleaner approach is to use Ajax, a Web technology that allows you to interact with the server without refreshing/submitting the entire page. This article presents several Ajax examples in a traditional Domino Web applications. It is intended for Domino Web developers with JavaScript, LotusScript, and Formula language programming experience.
Sample Ajax function
First, let's take a look at the skeleton sample Ajax function shown in listing 1. We won't go into all the details here; however, if you want further information, you can read the Ajax topic on w3schools.com.
Listing 1. Sample Ajax function
function ajaxSample()
{
var xmlHttp;
try {
xmlHttp=new XMLHttpRequest();
}
catch (e) {
try {
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {
alert("Your browser does not support AJAX!");
return false;
}
}
}
xmlHttp.onreadystatechange=function() {
if(xmlHttp.readyState==4) {
var s = new String(xmlHttp.responseText);
}
}
xmlHttp.open("GET", url, true);
xmlHttp.send(null);
}
Within this skeleton code, there are two lines we need to understand to use this function. The first is
var s = new String(xmlHttp.responseText);
In this line, the variable s contains the response from the server. You will need to parse the response and perform the desired action with the response value. Below is an example response:
<html>
<head>
</head>
<body text="#000000">
Response Text
</body>
</html>
The second line is
xmlHttp.open("GET", url, true);
This is the line that makes the server request. You will need to pass a valid URL. In the examples presented below, we'll use a LotusScript agent to perform the server work so the URLs will be in this format:
http://server_hostname/database_name.nsf/agent_name?OpenAgent
The response from the agent is passed back by use of the 'Print' LotusScript statement.
Now let's take a look at a few examples within Lotus Domino. The examples below are contained in the attached AJAX.nsf file for your convenience.
Example 1 Now
'Now' is a LotusScript function that returns the server's current date and time. In this example, we populate a field on a Web page using Now. The agent required for this is quite simple so let's begin with that first. The required code consists of one statement:
Name: AjaxNow
Trigger: Agent list selection
Target: None
Code:
Sub Initialize()
Print Now
End Sub
Next, let's review the Web form and Ajax function. The form (Example1 in AJAX.nsf) contains a field called 'Now' and a hotspot button to trigger the Ajax call. The hotspot button simply calls the Ajax function: ajax();
The Ajax function populates the 'Now' field with the results. In the JS Header section of the form, we're using the Ajax skeleton code from above. However, there are two sections we need to implement: parsing/outputting the response, and adding a valid URL.
First, let's start off by adding the URL:
Next, we need to parse out the response (using the body tags) and write out the value to the 'Now' field:
var s = new String(xmlHttp.responseText); //skeleton code
This line will set 's' to the following string below:
<html>
<head> </head>
<body text="#000000">
3/15/2010 8:54:21 AM
</body>
</html>
var temp = s.substring(s.indexOf("<body"), s.indexOf("</body"))
This line grabs the body text and places it into temp:
<body text="#000000"> 3/15/2010 8:54:21 AM
Now to remove the opening body tag and grab the date/time text. If you use 'temp = temp.substring(temp.indexOf(">"), temp.length)', you end up with:
So, to discard the “> “, let's add 2 to the index:
temp = temp.substring(temp.indexOf(">")+2, temp.length)
Finally, assign the temp value to the 'Now' field:
document.forms[0].elements['Now'].value = temp;
Listing 2 shows the Ajax code for this example.
Listing 2. Ajax code for Example 1
function ajax()
{
var xmlHttp;
try {
xmlHttp=new XMLHttpRequest();
}
catch (e) {
try {
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {
alert("Your browser does not support AJAX!");
return false;
}
}
}
xmlHttp.onreadystatechange=function() {
if(xmlHttp.readyState==4) {
var s = new String(xmlHttp.responseText);
var temp = s.substring(s.indexOf("<body"), s.indexOf("</body"))
temp = temp.substring(temp.indexOf(">")+2, temp.length)
document.forms[0].elements['Now'].value = temp;
}
}
xmlHttp.open("GET", "http://server_hostname/ajax.nsf/AjaxNow?OpenAgent", true);
xmlHttp.send(null);
}
To see the end result, open the Example1 form on a web browser (
http://server_hostname/AJAX.nsf/Example1?OpenForm) and click the Ajax hotspot button to execute the Ajax call (see figure 1).
Figure 1. Ajax button
NOTE: If you're using AJAX.nsf (the attached sample database), be sure to correct the URLs to match your server's hostname, and to sign the agents with an Notes ID that has rights to run agents on your server. The Ajax functions are defined in the JS Header section of the example forms.
Also, a caching issue occurred when the examples were run using Internet Explorer 6. If you use this browser, you must modify the method code to send a unique URL with each request, to work around the caching issue. One way to accomplish this is to append the current time to the URL and just ignore this value in the agent:
Example 2 Passing a parameter to the agent
In the workaround above, we mentioned appending the time to the URL, but let's explore what that actually does and how to use it to our advantage. Example 1 demonstrated how to run code on a server and send back a response to the Web page. However, if you want your agent to do something specific to the current Web page, you must be able to pass parameters to the agent.
In this example, we'll append a parameter to the OpenAgent URL to show how the agent can grab the parameter using a Common Gateway Interface (CGI) variable.
The Example2 form consists of two fields:
• 'Parameter' for the parameter we'll pass to the agent
• 'Result' to write the response from the agent
Similar to Example 1, we'll use a hotspot button to call our Ajax function. In addition to the Ajax function call, we'll grab and pass the value of the 'Parameter' field to the function.
Here's the hotspot button code:
//assign the value from the Parameter field to par
var par = document.forms[0].elements['Parameter'].value;
//call ajax function passing par
ajax(par);
There are three differences (with respect to Example 1) in the Ajax function:
1. The function accepts a parameter:
2. The results are written to the 'Result' field:
document.forms[0].elements['Result'].value = temp;
3. The parameter is passed in the OpenAgent URL:
Listing 3 shows the Ajax code for this example.
Listing 3. Ajax code for Example 2
function ajax(par)
{
var xmlHttp;
try {
xmlHttp=new XMLHttpRequest();
}
catch (e) {
try {
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {
alert("Your browser does not support AJAX!");
return false;
}
}
}
xmlHttp.onreadystatechange=function() {
if(xmlHttp.readyState==4) {
var s = new String(xmlHttp.responseText);
var temp = s.substring(s.indexOf("<body"), s.indexOf("</body"))
temp = temp.substring(temp.indexOf(">")+2, temp.length)
document.forms[0].elements['Result'].value = temp;
}
}
xmlHttp.open("GET", "http://server_hostname/ajax.nsf/AjaxParameter?OpenAgent" + "&par=" + par ,true);
xmlHttp.send(null);
}
The agent for this example uses the Query_String CGI variable to grab the parameter. For more information about the available CGI variables, see the topic, Table of CGI Variable names, in the Domino Designer 8.5 information center.
The LotusScript agent below demonstrates one way of parsing the parameter out of "&par=parameter". The response sent by the agent depends on whether a parameter was passed or not (see the example code in listing 4).
Name: AjaxParameter
Trigger: Agent list selection
Target: None
The code is shown is listing 4.
Listing 4. LotusScript agent code
Sub Initialize()
Dim s As New NotesSession
Dim begin As Integer
Dim query As String
query = s.DocumentContext.Query_String(0)
begin = InStr(query, "=")
query = Right\$(query, (Len(query) - begin))
If (Len(query) = 0) Then
Print "AjaxParameter ran; no parameter passed"
Else
Print "AjaxParameter ran; passed it " & query
End If
End Sub
Figure 2 shows this form in a Web browser.
Figure 2. Example2 form in Web browser
Example 3 More complex scenario with dialog list fields
Now that we know how to pass a parameter to the agent, let's look at a more complex example. In AJAX.nsf, there is a view named 'People' with employees and their contact information categorized by their work area (see figure 3).
Figure 3. People view
In this example, we have two dialog list fields and three text fields, as shown in table 1.
Table 1. Field types, names, and what they display
Field | Name | Displays |
Dialog list 1 | Type | Categories/employee work area |
Dialog list 2 | Name | Names of employees |
Text field 1 | Address | Employee's address |
Text field 2 | Email | Employee's email address |
Text field 3 | Phone | Employee's phone number |
To populate the 'Type' field, we can use an @DbColumn, since that field will be populated when the page is generated:
@Unique(@DbColumn("";"";"Type";1))
Now for the Ajax part. We want to populate the 'Name' field based on the area selected in the Type field. We also want the employees to be updated whenever the selection is changed, so the Ajax method will be called by the onChange event of the Type field. The Ajax method will take the work area as a parameter.
Here's the 'Type' onChange event code:
//set the type variable to the value selected in the Type field
var type = document.forms[0].Type.options[document.forms[0].Type.selectedIndex].text;
//call the ajaxGetEmployee function passing the type variable
ajaxGetEmployees(type);
Since we'll be passing back a list of names, let's use a comma to separate the names. To parse the results, we use the split method and then loop through the array to populate the dialog list field. Listing 5 shows the code for this example.
Listing 5. Example Ajax code
function ajaxGetEmployees(par)
{
var xmlHttp;
try {
xmlHttp=new XMLHttpRequest();
}
catch (e) {
try {
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {
alert("Your browser does not support AJAX!");
return false;
}
}
}
xmlHttp.onreadystatechange=function() {
if(xmlHttp.readyState==4) {
var s = new String(xmlHttp.responseText);
var temp = s.substring(s.indexOf("<body"), s.indexOf("</body"))
temp = temp.substring(temp.indexOf(">")+2, temp.length)
var array = temp.split(",");
for (i=0; i<array.length; i++)
{
var tempOption = new Option(array[i], array[i]);
document.forms[0].Name.options.add(tempOption);
}
}
}
xmlHttp.open("GET", "http://server_hostname/ajax.nsf/AjaxRetrieveEmployees?OpentAgent" + "&par=" + par ,true);
xmlHttp.send(null);
}
The AjaxRetrieveEmployees agent is fairly straightforward. The agent generates a document collection, using the work area and the
GetAllDocumentsByKey method, and then builds a comma-separated list from the value of the Name field from each document.
Name: AjaxRetrieveEmployees
Trigger: Agent list selection
Target: None
Listing 6 shows the code for this agent.
Listing 6. AjaxRetrieveEmployees code
Sub Initialize()
Dim s As New NotesSession
Dim db As NotesDatabase
Dim view As NotesView
Dim dc As notesdocumentcollection
Dim doc As notesdocument
Dim begin As Integer
Dim query As String
Dim result As String
result = ""
query = s.DocumentContext.Query_String(0)
begin = InStr(query, "=")
query = Right\$(query, (Len(query) - begin))
'query should never be zero unless the database does
'not contain any documents (there will be no types)
If (Len(query) = 0) Then
Print "Error: You did not pass in a work area"
Exit Sub
End If
Set db = s.Currentdatabase
Set view = db.Getview("Type")
Set dc = view.Getalldocumentsbykey(query)
Set doc = dc.Getfirstdocument
While Not doc Is Nothing
If (Len(result) = 0) Then
result = doc.Getitemvalue("Name")(0)
Else
result = result + "," + doc.Getitemvalue("Name")(0)
End If
Set doc = dc.Getnextdocument(doc)
Wend
Print result
End Sub
In addition to the two dialog list fields, three text fields are included to display the employee's information. It's possible to populate these fields using the onChange event of the Name field; however, we choose to manually populate them using a hotspot button (to add some user interaction).
The hotspot button code simply grabs the value from the Name field and passes that value to the Ajax method:
//set the name variable to the value selected in the Name field
var name = document.forms[0].Name.options[document.forms[0].Name.selectedIndex].text;
//call the ajaxGetEmployeeInfo function passing the name variable
ajaxGetEmployeeInfo(name);
Since the agent (AjaxRetrieveEmployeeInfo) for this example will again return multiple values, we use commas to separate the values. After calling split, we populate the 'Address', 'Email,' and 'Phone' fields accordingly (see listing 7).
Listing 7. Example Ajax code
function ajaxGetEmployeeInfo(par)
{
var xmlHttp;
try {
xmlHttp=new XMLHttpRequest();
}
catch (e) {
try {
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {
alert("Your browser does not support AJAX!");
return false;
}
}
}
xmlHttp.onreadystatechange=function() {
if(xmlHttp.readyState==4) {
var s = new String(xmlHttp.responseText);
var temp = s.substring(s.indexOf("<body"), s.indexOf("</body"))
temp = temp.substring(temp.indexOf(">")+2, temp.length)
var array = temp.split(",");
document.forms[0].elements['Address'].value = array[0];
document.forms[0].elements['Email'].value = array[1];
document.forms[0].elements['Phone'].value = array[2];
}
}
xmlHttp.open("GET", "http://cortana.austin.ibm.com/ajax.nsf/AjaxRetrieveEmployeeInfo?OpenAgent" + "&par=" + par ,true);
xmlHttp.send(null);
}
The AjaxRetrieveEmployeeInfo agent calls
GetDocumentByKey using the name we passed it and builds a comma-separated string with the values of the 'Address', 'Email', and 'Phone' fields.
Name: AjaxRetrieveEmployeeInfo
Trigger: Agent list selection
Target: None
Listing 8 shows the code for this agent.
Listing 8. AjaxRetrieveEmployeeInfo code
Sub Initialize()
Dim s As New NotesSession
Dim db As NotesDatabase
Dim view As NotesView
Dim dc As NotesDocumentCollection
Dim doc As NotesDocument
Dim begin As Integer
Dim query As String
Dim result As String
result = ""
query = s.DocumentContext.Query_String(0)
begin = InStr(query, "=")
query = Right\$(query, (Len(query) - begin))
'again, query should never be zero unless the database contains
'no documents
If (Len(query) = 0) Then
Print "Error: You did not pass in an employee name"
Exit Sub
End If
Set db = s.Currentdatabase
Set view = db.Getview("Name")
Set doc = view.Getdocumentbykey(query)
If Not doc Is Nothing Then
result = doc.Getitemvalue("Address")(0) + "," + doc.Getitemvalue("Email")(0) + "," + doc.Getitemvalue("Phone")(0)
Else result = "Error: did not find document for that employee" End If
Print result
End Sub
Figure 4 shows this example form in a Web browser.
Figure 4. Example3 form in Web browser
Conclusion
After reviewing the examples above, you should be able to add Ajax functionality to your traditional (non-XPages) Domino Web applications. Using the powerful Ajax technology allows your Web applications to interact with your Web users much more efficiently.
Resources
Lotus Domino Designer 8.5 information center:
http://publib.boulder.ibm.com/infocenter/domhelp/v8r0/topic/com.ibm.designer.domino.main.doc/H_WHAT
developerWorks Lotus Notes and Domino product page:
http://www.ibm.com/developerworks/lotus/products/notesdomino/
Download: IBM Lotus Domino Designer:
http://www.ibm.com/developerworks/downloads/ls/dominodesigner/learn.html
Ajax Tutorial:
http://www.w3schools.com/ajax/default.asp
About the author
Oscar I Hernandez is a Staff Software Engineer with the IBM Lotus Technical Support organization. He is a member of the Application Development team for Lotus Notes/Domino and is an IBM Certified Advanced Application Developer. You can reach Oscar at
oihernan@us.ibm.com.