Skip to main content

skip to main content

developerWorks  >  Lotus  >

Using JavaScript in IBM Lotus Component Designer

developerWorks
Document options

Document options requiring JavaScript are not displayed

Discuss


Learn and share!

Exchange know-how with your peers -- try our new Pass It Along beta app


Rate this page

Help us improve this content


Level: Intermediate

Robert Perron, Documentation architect, IBM Corporation

27 Feb 2007

This article provides a working introduction to the programming capabilities of IBM Lotus Component Designer, focusing on the JavaScript interpreter that is part of the host server runtime environment.

IBM Lotus Component Designer supports JavaScript for application programming. Implementation occurs through two general mechanisms:

  • Events. You can use JavaScript to implement control events such as onclick, onchange, and onblur and page events such as afterPageLoad. Events can also be implemented with simple actions such as Open page and Save document, so programming is not needed for some common tasks. Additionally, simple action groups allow for the serial and conditional processing of multiple simple actions and scripts.
  • Dynamic values. You can use JavaScript to provide dynamic values for control and page properties. In these cases, the script ends with a value or a return statement.

JavaScript is a standard, syntactically simple, object-oriented, interpretive programming language supported by browsers and other processors. The source statements are embedded in the XML, HTML, or other source language and are executed by an interpreter in the processor.

Browsers automatically make available a set of objects called the document object model (DOM) that represents the page being processed. The window object provides access to the elements on the page. A description of standard JavaScript and the DOM is beyond the scope of this article, but tutorial and reference material abound. Search the Web for general subjects such as javascript dom, javascript tutorial, javascript reference, or more specific subjects such as javascript switch statement.

Lotus Component Designer provides a JavaScript interpreter as part of the host server runtime environment, but this is not your father's JavaScript. This interpreter includes built-in variables and class libraries that access the runtime context and backend data store, handle data of various types (string, numeric, Boolean, regular expression), invoke Web services, and emulate IBM Lotus Domino @functions. This interpreter also allows calling into Java archives on the host server and Java code in the Lotus Component Designer Eclipse environment.

Scripts can be processed server-side with the Lotus Component Designer interpreter or client-side with the browser interpreter.

Scripting case study

Here we present a simple case study that illustrates client-side and server-side scripting.

A page has two buttons, each with an onclick event and a computed field. Figure 1 shows the Design page highlighting the Events tab for the first button, hello client.


Figure 1. Client-side script
Client-side script

The following options are selected on the Events tab:

  • Web Client. This means that the event runs client-side using the standard JavaScript interpreter in the user browser.
  • No Submission. No interaction occurs with the server. For a client-side event, you probably don't want the expense of a trip to the server.

The code is a window.alert method. When the runtime user clicks the button, the browser immediately displays the window shown in figure 2.


Figure 2. Client-side script at runtime
Client-side script at runtime

The second button, hello server, works in tandem with the computed field, whose value in the Properties tab is highlighted in figure 3.


Figure 3. Computed field for server-side script
Computed field for server-side script

The value of the field is bound to a request-scope (more on this later) global variable named hello.

The second button and the Events tab are shown in figure 4.


Figure 4. Server-side script
Server-side script

Here, the options selected are:

  • Server. The event runs server-side using the enhanced JavaScript interpreter in the Lotus Component Designer runtime.
  • Full Update. The browser sends a request to the server, which processes the request and responds with HTML to repaint the entire page.

The script assigns a value to the global variable bound to the computed field. When the user clicks this button at runtime, there is a pause as the browser sends the request to the server and the server responds with a fresh page. The repainted browser screen appears as shown in figure 5 (with adjustments to the font properties for size and color).


Figure 5. Server-side script at runtime
Server-side script at runtime

To recapitulate, you create pages, controls, and other artifacts with Lotus Component Designer. These design elements have an XML representation that you can see and (with care) adjust in the Source tab. Figure 6 shows the Source tab for the hello world sample page.


Figure 6. Source tab for sample page
Source tab for sample page

A deployment process generates and/or transfers application files to a server's file system. Currently, components can be deployed to IBM WebSphere Portal as applications. You use the server's interfaces to set up the application, for example, as a portlet on a page. A user can then start the application, say, by visiting the page containing the portlet. For deployment instructions, see the Lotus Component Designer User Guide Help.

Initially, the server generates and sends HTML for the default page to the user's browser. The user views the page, enters data, clicks buttons, and so on. Events triggered by user activity may or may not cause interaction with the server, depending upon the option selected at the bottom of the Events tab:

  • Full Update. Sends a request to the server, which executes the request and responds with new HTML for the entire page.
  • Partial Update. Sends a request to the server, which executes the request and responds with new HTML for selected elements on the page. Use the Specify button on the Events tab to select the elements (see figure 7).
  • No Update. Sends a request to the server, which executes the request but does not respond. The trip is one way.
  • No Submission. Sends no request to the server.

When a page is updated, formulas marked as "Compute Dynamically" are recalculated (see figure 7). Formulas marked as "Compute on Page Load" are calculated only once when the page is first sent.


Figure 7. Example formula computed dynamically
Example formula computed dynamically


Back to top


Frontend data

You can work with frontend data -- that is, the data the user places on the current page in the browser -- immediately through the standard client-side JavaScript and the client-side DOM. This approach has the virtue of not requiring transfers between the client and server machines. On the other hand, you do not have access to the server resources.

The name of a client-side DOM element does not match the name assigned to the corresponding control at design time. It is not known until generated by the runtime prior to loading the browser page. The generated name, however, can be passed from the server runtime to the browser DOM. For example, the following code is the onchange event for an edit box named inputText1:

var e = window.document.getElementById("#{id:inputText1}");
e.value = e.value.toUpperCase()

The option Web Client was selected so that it runs in the browser and No Submission so that it does not cause interaction with the server. When the user changes the value of the edit box, the value immediately goes to uppercase.

The notation #{id:inputText1} is a server-side script that executes before the page is sent to the browser. This embedded script gets the runtime name (ID) for the element specified by its design-time name. The client-side DOM can then get the element by ID.

In general, you can pass information from the server runtime to the browser by embedding a server-side script in the client-side code. The following is a client-side onclick event for a button:

window.alert("#{javascript:session.getCommonUserName()}")

Before loading this JavaScript to the browser, the runtime executes the embedded server-side script so that the common user name appears as a string constant in the client-side alert method.

Walking through arrays

You can also walk through the forms array, and within the forms array the elements array, until the element whose name contains "inputText1" is found.

var e;
for(var i=0; i<window.document.forms.length; i++) { for(var j=0; j<window.document.forms[i].elements.length; j++) { e = window.document.forms[i].elements[j]; if(e.name.indexOf("inputText1") > -1) break; } } e.value = e.value.toUpperCase()

NOTE: This code won't work for a repeated control because all repetitions will contain the name. The #{id:...} syntax is preferred.

The thrust of programming in Lotus Component Designer is server-side, where the resources are. Server-side processing, however, requires transmissions between browser and server.

The following four global objects support user-created variables and are very useful for server-side processing:

  • requestScope variables last for the duration of one browser request.
  • sessionScope variables last across requests until the server session ends. On a portal server, this is typically when the user logs off, times out, or closes the browser. On WebSphere Portal, sessionScope variables are per portlet, so they can't be used for sharing data between portlets.
  • applicationScope variables last across sessions as long as the application exists.
  • viewScope variables last for the duration of the current page.

The user variable is created and referenced by appending a period and name to the name of the scope object. For example, if a button has the following server-side onclick script:

requestScope.myvar = "My variable"

and a label on the same page is computed with the following script:

requestScope.myvar

when the user clicks the button at runtime, "My variable" appears at the position of the label.

You can also use the put and get methods of the scope object. Notice that the variable names are specified as strings:

requestScope.put("myvar", "My variable")
requestScope.get("myvar")

and the following syntax works:

requestScope["myvar"]

To work with user-entered values on the server side, you get and set the data elements bound to the controls. For example, an edit box may be named inputText1, but to get at its server-side value, on the Properties tab, go to the Data binding field of the Data section. This can be a schema element, a Web service adapter, or some other data store definition, but it can also be a global variable.

Suppose you specify a server-side onchange event for this input box with the following script and full or partial update in effect:

requestScope.field1 = requestScope.field1.toUpperCase()

When the runtime user makes a change in the input box, the browser sends a request to the server. The server runs the script, changing the data bound to the field to uppercase. The HTML sent back to the browser reflects the change.

A word about performance. If you put two edit boxes on a page, each with a server-side onchange event, then when the user changes the first edit box, a hesitation occurs as the request goes to the server and the HTML comes back. When the user changes the second edit box, the same thing happens for the second box.

A typical technique to avoid multiple interruptions is to use one event for all updating on a page. For example, instead of an onchange event for each edit box, use the onclick event of one button. The user clicks the button after updating both fields. The button code is as follows:

requestScope.field1 = requestScope.field1.toUpperCase();
requestScope.field2 = requestScope.field2.toUpperCase();

A requestScope variable is good across pages if you go to the other page programmatically. For example, on page1 an edit box has the following data definition:

requestScope.field1

and the following onchange server-side event:

requestScope.field1 = requestScope.field1.toUpperCase();
context.redirectToPage("page2")

On page2, a computed field has the following value:

requestScope.field1

At runtime, the user changes the edit box on page1, which triggers the event. The browser sends a request to the server, and the event code executes. The first line of code changes field1 to uppercase. The second line sends HTML for page2 back to the browser -- context is a built-in variable that points to an object of type XSPContext, and redirectToPage is a method of XSPContext that loads the specified page. Because all processing is within one request, the value of field1 on page2 gets the change made on page1.

If instead the preceding code did not contain line 2 and if you used a second button with an Open Page simple action to go to page2, the computed field would come back empty. This happens because you are completing one request (triggered by the onchange event) and then starting a new request (triggered by the button onclick event), so the requestScope variable is no longer valid. This latter technique is also less efficient, requiring two requests instead of one.

A sessionScope variable lasts until the user logs off or is otherwise disconnected from the server. For example, if the computed field on page2 and the data definition on page1 are:

sessionScope.field1

and the onchange event is:

sessionScope.field1 = sessionScope.field1.toUpperCase()

then no matter how the user goes to page2, the computed field contains the uppercase of the last value placed in the edit box on page1. Note that variables of different scopes with the same name are not the same variable. The variable requestScope.field1 is not the same as sessionScope.field1.

One other caveat: Make sure the display data type for a field matches the scripting use of the data. For example, if you have an edit box whose data is defined as:

requestScope.field2

and have the following onchange event:

requestScope.field2 = Math.sqrt(requestScope.field2)

then the intent is to take the square root; however, this works only if the display type of the field is Number as shown in figure 8. The default display type is String, so this is one area to check if your numeric evaluations are not yielding the proper results.


Figure 8. Display type of Number
Display type of Number

Scripting for both the client side and server side in one event is possible by using simple action groups.

A Confirm Action simple action puts up a client-side dialog box with OK and Cancel buttons. If the user clicks OK, execution of the simple actions in the group continues; if the user clicks Cancel, execution of the group terminates. The design looks like figure 9.


Figure 9. Simple action design
Simple action design

An Execute Script simple action runs a server-side script, which is specified in the Edit Simple Action dialog box (see figure 10).


Figure 10. Execute Script simple action
Execute Script simple action

When the user clicks the button, the first simple action runs, displaying the confirmation box. If the user clicks OK, the second simple action runs, executing the script. If the user clicks Cancel, the second simple action does not run.

Scripting for both the client side and server side in one event is also accomplished by attaching both types of script. If an event has both client- and server-side scripts, the client-side script executes first. You can prevent the server-side activity from running by returning false from the client-side script, for example, if a button has the server-side script shown in listing 1:


Listing 1. Example server-side script
if(session.getCommonUserName() == "wpsadmin") {
	requestScope.field1 = requestScope.field1.toUpperCase();
	requestScope.field2 = requestScope.field2.toUpperCase();
}

and the following client-side script:

if(window.confirm("Changing fields to upper ... OK?") != true)
return false

When the user clicks the button, the client-side script runs, displaying the confirmation box. If the user clicks OK, the server-side script runs. If the user clicks Cancel, no further action occurs.



Back to top


Backend data

The non-programming methods of dealing with data storage are as follows:

  • Associate a schema with a page.
  • Associate fields on the page with elements of the schema. This is done through the data binding mechanism described previously, only this time you are actually binding to backend data elements. The data source is document, which is the backend data source maintained by the Lotus Component Designer runtime (IBM Cloudscape, Oracle, IBM DB2, or Microsoft SQL Server). You can have other data sources such as XML documents, Domino databases, and Web services, but these are outside the scope of this article.
  • Put buttons of type Submit and Cancel on the page. These are special buttons; both cause the browser to send a request to the server. The Submit request causes the data on the page to be saved to the bound data elements. The Cancel request does not validate or save the data.

Both buttons then send back HTML for the page defined as "Next page (success or cancel)" in the properties for the current page.

Other options in Lotus Component Designer control various aspects of dealing with the backend data store, such as whether documents are being created or updated. You can use View controls containing view queries to display multiple documents from the backend data store.

Now we discuss the programming methods to deal with data storage. Server-side JavaScript has access to a large set of classes for accessing the backend data store. The primary scripting interfaces to the backend data store are these built-in global variables:

  • Database points to a DBDatabase object representing the current database. You can get the database's properties and drill down to documents.
  • Document points to a DBDocument object representing the current document, that is, the document associated with the last page sent to the browser. This variable is available only when the current page is associated through a schema with a backend data source.
  • Session points to a DBSession object representing the connection to the server.

For syntax and details regarding the classes supported by server-side JavaScript, see the "Lotus Component Designer Scripting Reference" help that comes with the product.

Listing 2 is some code for the onclick event of a button. On the same page as the button are two edit boxes bound to the requestScope variables field0 and field1. When the user clicks the button, the values entered in those edit boxes are used to create a new document in the database.


Listing 2. Saving documents to backend data store
ar doc = database.createNewDocument();
doc.setStringValue("/schema1/element0", requestScope.field0);
doc.setIntValue("/schema1/element1", requestScope.field1);
doc.save(false);
context.redirectToPage("page2")

Each line of the code is described as follows:

  • Line 1 creates a new document in the current database and returns a DBDocument object representing it.
  • Line 2 creates a string data element based on the definition for element0 in schema1 and the content of field0.
  • Line 3 creates a numeric data element based on the definition for element1 in schema1 and the content of field1.
  • Line 4 saves the document changes to the backend data store.
  • Line 5 sends page2 to the user's browser.

The code in listing 2 is roughly analogous to presenting the user with a page for a new document and the user filling in fields bound to the backend data and clicking a Submit button.

Listing 3 is an example of getting documents from the backend data store. Again, the page has two fields bound to the requestScope variables field0 and field1, and the code is in a button onclick event. When the user clicks the button the first time during a session, a page is populated from the first document in the current database and sent to the browser. Subsequent clicks get the next document wrapping after the last document.


Listing 3. Getting documents from backend data store
var doc = null;

// Initialize and get first doc first time
if(sessionScope.dc == null) {
	sessionScope.dc = database.getAllDocuments();
	doc = sessionScope.dc.getFirstDocument();
} else { // or get next doc
	doc = sessionScope.dc.getNextDocument();
}

// At end of collection, get first doc again
if(doc == null) {
	doc = sessionScope.dc.getFirstDocument();
}

if(doc == null) { // If 2 nulls, collection is empty
	requestScope.field1 = "No documents";
} else { // Set fields to element values
	requestScope.field1 = doc.getStringValue("//element0");
	requestScope.field2 = doc.getIntValue("//element1");
}

context.redirectToPage("page2")



Back to top


Error handling and content assist

Lotus Component Designer catches basic syntax errors in server-side scripts. For example, you enter the following as the onclick event of a button, mistyping var as va:

va doc = database.getDocumentById(sessionScope.id);
requestScope.x = doc.getCreationDate()

The line containing the error is marked in the embedded editor.

The error, though, is not marked in the modal editor (the editor in the window in which you click OK or Cancel). You do not see the error until you close the window, and then it is easy to miss. But if you try to deploy a component containing errors, you receive a message.

The Problems tab lists errors in the component as shown in figure 11. If you double-click an error in the Problems tab, you go to the code containing the error but in the Source tab, not the Design tab. If you find this uncomfortable, open the specified resource (for example, page6) in the Design tab and look at the specified line (for example, line1). The error message is not specific, but at least you know where it is.


Figure 11. Problems tab
Problems tab

It is a good idea to check the Problems tab periodically as well as when deployment reports an error. You enable this tab by choosing Window - Preferences in the menu, expanding IBM Lotus Component Designer, choosing Default Options, and checking the Problems box. In this same window, it is also a good idea to select All under "Select which simple action categories to display" (see figure 12).


Figure 12. Preferences for tabs and simple actions
Preferences for tabs and simple actions

Similarly, you should enable the "Show advanced JavaScript classes and methods" option. To do so, choose Window - Preferences in the menu, expand IBM Lotus Component Designer, choose Script Editor, and select "Show advanced JavaScript classes and methods." Otherwise, when you look at the reference pane or use content assist, you get a subset of the available methods.

Figure 13 shows the reference pane on the left-hand side of the editor window, containing all syntax arranged by library and class. Content assist displays when, by default, you type a period and wait half a second, or you press Ctrl + space. If you are at the end of an object, the display is the syntax for available methods.


Figure 13. Typing the global variable "database" (a DBDatabase object) followed by a period
Typing the global variable database (a DBDatabase object) followed by a period

Double-click syntax in content assist or the reference pane to copy it into the editor. Note, however, that copied syntax contains only the method name followed by empty parentheses, so you must remember the parameters or return to content assist or the reference pane.

Server-side errors other than simple syntax errors exhibit themselves at runtime as exceptions or aberrant behavior. In the preceding example, suppose we get var right but omit the e in database. This passes the design-time syntax check, but the runtime interpreter does not find "databas" to be a known object. By default the runtime sends this page shown to the browser:

Script interpreter error, line=1, col=19
Error calling method 'getDocumentById(java.lang.String)':'databas' is not a valid object

A try...catch structure changes the runtime exception behavior to what is specified in the catch clause. The default action exiting the catch clause is the same as for a normal exit, for example, to post the current page back to the browser for Full Update or Partial Update. Listing 4 shows the preceding code in a try...catch structure. If an exception occurs, its text is posted to the msg requestScope variable.


Listing 4. Try...catch structure
try {
	var doc = databas.getDocumentById(sessionScope.id);
	requestScope.x = doc.getCreationDate();
} catch(e) {
	requestScope.msg = e;
}

The code in listing 5 shows what the runtime might generate for the exception if the msg requestScope variable is bound to a computed field at the bottom of the page being processed.


Listing 5. Runtime generated exception
set x
go p 1
com.ibm.jscript.InterpretException: Script interpreter error, line=2, col=27 
Error calling method 'getDocumentById(java.lang.String)': 'databas' is not a valid object

Server-side exceptions go to the system log file. For WebSphere Portal, the log file is PortalServer/log/SystemOut.log in the WebSphere directory. You should examine the log from the bottom up or search on the name of the portlet.

Client-side errors -- even simple syntax errors -- are not reported at design time. Moreover, they tend to fail silently at runtime unless you turn on the Java console or debugger. For example, the following client-side button code doesn't close a string constant:

window.alert("hello client world)

yet the line is not flagged as a problem, and deployment occurs without error. When the button is clicked at runtime, nothing happens.

To see the problem, turn on the browser's JavaScript or Java console by using one of the following three menu selections: Tools - Web Development - JavaScript Console, Tools - JavaScript Console, or Tools - Sun Java Console.



Back to top


Data types

JavaScript implies data types from context -- for example, if listing 6 is the value of a computed field:


Listing 6. Value of computed field
var s = "This is a string."
var x = 99.9;
var b = true;
s + " " + x + " " + b

the variable s is taken as a string, x as a number, and b as a Boolean. This is what the field looks like in runtime:

This is a string. 99.9 true

A data type can be expressed with the colon notation shown in listing 7. The notation, however, is advisory and has no effect on actual types and values.


Listing 7. Colon notation
var s:string = "This is a string."
var x:double = 99.9;
var b:boolean = true;
s + " " + x + " " + b

Server-side JavaScript supports the following standard library classes: String, Number, and Boolean (as well as Array, Date, and others, plus a Math library). These classes provide various methods for manipulating those data types. Moreover, primitive data types automatically become objects. For example, the following is permissible:

var s = "This is a string."
s.toUpperCase()

And is equivalent to:

var s = new String("This is a string.");
s.toUpperCase()

The former is more efficient, though, because the latter creates two strings in memory.

Finally, note that this is also permissible:

"This is a string.".toUpperCase()

Numbers are all in IEEE double precision format. In the reference pane, content assist, and the documentation, you see numbers labeled as int, long, and double using the colon notation. Again, this notation is advisory, and those designations show intent only. In double precision format, integers have a range of 2**-53 through 2**53.



Back to top


Additional programming capabilities

Listed here are additional programming capabilities in Lotus Component Designer. Detailed descriptions are beyond the scope of this introductory article.

Java objects

Server-side JavaScript can call Java classes in Java libraries on the server. See the developerWorks article, "Extending the power of IBM Workplace Designer with custom Java code" for an in-depth discussion. In addition, server-side JavaScript can call static Java classes in the component. As a trivial example, you might switch to the Java perspective and create the code that appears in listing 8:


Listing 8. Code for the Java class
                package foo;

public class bar {
	public static String doit() {
		return "foobar";
	}
}

Then as the value for a computed field you might code the following JavaScript (which displays foobar):

return foo.bar.doit()

Log file

A print statement is an enhancement of server-side JavaScript. This statement writes to the Web server's log file. As mentioned previously, for WebSphere Portal, the log file is PortalServer/log/SystemOut.log in the WebSphere directory. For example, if you code the beforePageLoad page event as follows:

print("<<< Application started >>>")

whenever the page loads, something like the following goes to the end of the log file:

[1/24/07 12:24:31:828 EST] 00000044 SystemOut O <<< Application started >>>

The example places the characters <<< at the beginning of the log text so the entry can be found easily with a search. Note that the log file contains many entries, and note that the window.alert method is not available and does not make sense in the server-side JavaScript.

Debugger

The debugger is the remote Eclipse Java debugger enhanced to support Lotus Component Designer and JavaScript. You need to do some setup in Lotus Component Designer and on the server running the deployed component. In the product help, see the "IBM Lotus Component Designer User Guide" and the "Java Development User Guide." Note that the setup instructions for the server have an omission (see the Release Notes) in that you must add -Denable.jsdebugger=true as a JVM debug argument.

Web preview

The Preview - Web tab displays a picture of a page as it appears from a browser. You may be able to interact with this display somewhat, but for the most part, scripts do not work because there is no server runtime. You have to deploy, to test your code.

@functions

Server-side JavaScript supports a number of functions that emulate IBM Lotus Domino Designer @functions. For example, the following can be entered for the value of a computed field:

@If(@Contains(@UserName(), "admin"), "Administrator", @UserName())

The syntax is slightly different using commas instead of semicolons to separate parameters.

Script libraries

JavaScript statements can be kept in client and server script libraries on a per-component basis. A script implementing an event or dynamic value can insert the statements from a library with the import statement. The use of libraries, typically for common functions, is straightforward. See the "IBM Lotus Component Designer User Guide" in the product help.



Back to top


Conclusion

IBM Lotus Component Designer provides a JavaScript interpreter as part of its runtime. This interpreter is enhanced with built-in variables and other features, and it has access to JavaScript libraries as well as Java code in the source Eclipse environment and the Java archives on the server housing the runtime. The JavaScript libraries include classes to access the runtime context and backend data store. Script processing takes place when the user's browser sends a request to the server, usually by triggering an event. In addition, processing can take place client-side using the standard JavaScript interpreter in the browser.

We hope that this article has provided you with a working introduction to the programming capabilities of Lotus Component Designer.



Resources

Learn

Discuss


About the author

Robert Perron is a documentation architect with Lotus in Westford, Massachusetts. He has developed documentation for Lotus Notes and Domino since the early 1990's with a primary concentration on programmability. He developed the documentation for the LotusScript and Java Notes classes and coauthored the book 60 Minute Guide to LotusScript 3 - Programming for Notes 4. He has authored several LDD Today articles. He also authored "A Comprehensive Tour of Programming Enhancements in Notes/Domino 6" for The View.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top