You write formulas for two principal reasons: to take an action and to set or get a value (or list of values). Each formula consists of a statement or a series of statements. Statement types are listed in Table 12.4.
Table 12.4. Types of Statements Used in the Formula Language
Type | Examples |
---|---|
Assignment |
dTarget := @Adjust(dStart; 0; 1; 0; 0; 0; 0) Sets dTarget one month past dStart @SetField(cStatus; "New") Sets the value of cStatus to "New" |
Comparison |
cStatus = "Completed" Tests whether the value of cStatus equals "Completed" nRequest > 10000 Tests whether the value of nRequest is greater than 10,000 |
Conditional |
@If(cStatus = "Completed"; @Do(... If the value of cStatus is "Completed" , takes some other action @If(@IsNewDoc & @IsDocBeingSaved; "New"; cStatus) In a computed field value event, if the document is new and is being saved, the value of cStatus is set to "New" |
Control |
@Return("") Stops execution of the formula @Do(), @DoWhile(), @For(), @While() Executes a series of statements |
Action |
@MailSend(jcSendTo; jcCC... Sends a memo @Command([FileSave]) Saves the current document |
Each of the statements or statement fragments in Table 12.4 can be part of a more complex formula; a statement can also be the entire formula. A formula can combine any number of statement types, and in practice, they often do just that.
Assigning Values to Fields and Variables
You often write statements that assign values to variables and fields. There is only one assignment operator, := . However, there are three other ways to assign values:
Of the three, only DEFAULT can assign a value to a variable. As their names imply, the other two set the values for fields in documents.
You can assign values from fields to variables and from variables to fields. A value can be the result of an expression, an @Function, or a constant. By default, any value assigned to a variable is temporary, and its scope is limited to the formula. There is no persistence to a value unless you save it in a document field. You can also assign a value to a field in the Value event for a computed field, in the Default Value event for an editable field, or in the Choices event for a keyword field by entering a constant or a formula. In this case, the assignment is implicit and does not require an operator.
In Listing 12.6, examples of assignments are numerous . In the first assignment, the value of the cManager field is assigned to the variable jcSendTo . The variable jcSendTo is later used to send the notification using @MailSend() . Next, jcDocHistory is set using a combination of text constants and @Functions ( @Text and @Now ). Both jcSendTo and jcDocHistory are variables, and their value will be discarded as soon as the formula is completed. The value of jcDocHistory is stored in the cDocHistory field using @SetField() . The value assigned to jcDocHistory is concatenated with the existing value of cDocHistory, adding to the list of events that happened to the current document.
Listing 12.6 Code from an Agent Illustrating Assignments to Variables and Fields
REM "Send notification to the manager"; SELECT Form = "AR" & cStatus = "New" &! @Contains(cLastAgent; "Manager Notification"); REM "Write doc history"; jcSendTo := cManager ; jcDocHistory := "Notification of New Application Request emailed to " + jcSendTo + " on " + @Text(@Now;"S2") ; @SetField("cDocHistory"; cDocHistory : jcDocHistory) ; REM "Add agent tracking fields." ; FIELD dNotified := dNotified ; @SetField("dNotified"; @If(dNotified = ""; @Now; dNotified : @Now)); FIELD cLastAgent := cLastAgent ; jcLastAgent := "Manager Notification" ; @SetField("cLastAgent"; @If(cLastAgent = ""; jcLastAgent; cLastAgent : jcLastAgent)); jcCC := "" ; jcSubject := "New Application Request # " + cReqNumber ; jcBody := "A new Application Request has been added to the Applications database. Click the doclink at the bottom of this message, and when you have reviewed the Request, click the Sign button and either approve or deny the Request. Thanks!" ; @MailSend(jcSendTo; jcCC; ""; jcSubject; jcBody; ""; [IncludeDoclink])
Using Comparisons in Formulas
In the following SELECT statement, there are several comparisons:
SELECT Form = "AR" & cStatus = "New" &! @Contains(cLastAgent; "Manager Notification");
First, the statement tests the field Form to determine whether it is equal to "AR" and whether the field cStatus is equal to "New" . Next, the statement ensures that the field cLastAgent does not contain "Manager Notification" (you don't want to annoy the managers with numerous notifications!). This statement uses the logical operators & ( AND ) and ! ( NOT ) and the @Function @Contains to build a collection of documents on which the agent will run. This statement could also be used in an agent, a view selection formula, or a replication formula to select a subset of documents.
In addition to comparing values of fields to text or other constants, comparison statements often use conditional @functions, such as @If() , and logical @Functions, such as @IsDocBeingSaved . For example, you can load the values for a keyword lookup into a computed for display field at the top of a document. Typically, this would involve storing a list in the field using @DbColumn or @DbLookup whenever the document is opened. However, the only time you need this value is when the document is being edited, so there is no sense in looking up the value unless you need it. It's simply not efficient. The following statement retrieves values only when the document is new or is being edited:
@If(@IsNewDoc @IsDocBeingEdited; @DbColumn( ""; cHRLookupID ; "LUCost";1); "")
You can use a comparison statement to determine what appears in view columns , but you can also use it as a counter. Because there is no Boolean data type in Domino, you can take advantage of the fact that a true comparison returns a 1 and a false returns a . Setting the column to total produces a count of all documents that meet the comparison. Using the previous example, a view column formula of @Contains(cLastAgent; "Manager Notification") provides a convenient counter by totaling the number of notifications sent to the manager.
Working with Conditional Statements
The Formula language has really only one conditional construct: @If() . The basic form of @If() has three parts : the condition, the true statement, and the false statement. The syntax for this form of @If() is as follows :
@If( condition; statement if true; statement if false )
The statements for the true or false condition can be any type of statement, including the assignment statement. An example of this simple form is the following:
@If( cType = "Lesson"; "Lesson " + nLesson; "Appendix: " + cAppendix)
If used in a view column formula, this displays the word Lesson plus the lesson number, or the word Appendix plus the appendix letter, depending on the value of cType.
But what if you have more than one value for cType? Perhaps you have three types of documents: introductory documents, lessons, and appendixes. The construct @If() can extend the arguments in the following form:
@If( 1stCondition; 1stTrue; 2ndCondition; 2ndTrue; [...] FalseStatement )
If the first condition is not true , the statement evaluation passes to the second condition. If there are more conditional statements, evaluation continues from left to right. The construct @If() must always have an odd number of arguments, no matter how many conditions you have in the statement. In effect, this is like a CASE statement in other languages.
You can also nest @If() statements within @If() statements. For example, you can write the following value formula for a computed field:
@If(@IsNewDoc & @IsDocBeingSaved; "Blue"; @If(cColor = "Green"; "Purple"; "Mauve"))
This sets the value to Blue if the document is new and is being saved. If this isn't the case and the value of cColor is Green , the value of the computed field becomes Purple . If the value of cColor is not Green , the value becomes Mauve . Nesting @If() statements is a very powerful tool, but they can be difficult to write and debug.
Using Control Statements
No method exists to pass the control of a formula to another formula or to a subroutine. However, some @Functions can control a formula to a limited extent. The following @Functions provide control logic:
You learned about @If() in the previous section. The function @Return() stops the execution of a formula and is very useful. You can use @Do() to execute a string of statements from left to right. Both @Return() and @Do() are often combined with @If() statements, which can give your formula a great deal of flexibility. The following code fragment checks to see whether the user really wants to deny the request. If not, it uses @Return("") to stop the execution of the remainder of the formula. If the user decides to proceed, a dialog box is opened into which the user enters a reason.
REM "Deny the request, and enter a reason for the denial."; jnAreYouSure := @Prompt([YESNO]; "Are you sure?" ; "Are you sure you want to deny this request?"); @If(jnAreYouSure; @Success; @Return("")); FIELD cDenial := cDenial; jcDialog := @DialogBox( "dBoxDenial" ; [AutoHorzFit] : [AutoVertFit] ; "Deny TSR" ); @SetField("cDocStatus"; "Denied"); @SetField("nStatusSort"; 99)
The @Function @Do() enables you to execute a series of statements following a conditional statement. In the following example, the user is asked if he wants to release the document. If so, the value of two fields is set. Ordinarily, there is no way to execute more than one statement in the true condition of an @If statement. However, using @Do() enables you to string together a series of statements; in this case, two @SetField statements are used to mark the status of the document:
REM "Release to approver"; jnYesNo := @Prompt([YESNO]; "Are you sure?"; "Do you want to release this document?"); @If(jnYesNo; @Do(@SetField("cDocStatus"; "Pending"); @SetField("nStatusSort"; 4)); "")
Another way to use @Return is in combination with @Success to stop the execution of a formula at the beginning if a certain condition exists. The @Function @Success evaluates to true (or 1 ). You can use it with @If to cause the execution of the formula to continue. In the following example, instead of including @Do within the @If , @Success is used in the true condition. When the user answers Yes, the formula continues to execute, saving values to cDocStatus and nStatusSort . If the user answers No, @Return stops the execution of the formula.
REM "Release to approver"; jnYesNo := @Prompt([YESNO]; "Are you sure?"; "Do you want to release this document?"); @If(jnYesNo; @Success; @Return("")); @SetField("cDocStatus"; "Pending"); @SetField("nStatusSort"; 4)
Although they are few, control statements are a vital part of the Formula language. You must learn how to use them to write effective formulas.
Using Iterative Statements
Three iterative @Functions were added to the Formula language in Designer 6:
These are similar to Do , DoWhile , For , and While statements in procedural languages such as LotusScript. However, these new @Functions operate only within the context of the formula, so although they're certainly welcome and useful, they do not provide the same level of looping capability that LotusScript and Java have. @DoWhile and @While execute a series of statements while the condition you provide evaluates to True . The syntax of both statements is @DoWhile( condition; statement; [statement[...]] ) . The syntax of @For is @For( counter; exit condition; increment; statement, [statement[el]] ) . Listing 12.7 combines many new Designer 6 features:
Listing 12.7 Using @For to Loop Through a List
REM {This code demonstrates nested assignment}; REM {list subscripting, and an iterative loop using @For}; REM {Assign values to a list}; cTextList := "January" : "February" : "March" : "April" : "May" : "June" : "July" : "August" : "September" : "October" : "November" : "December"; REM {For Loops have three initial arguments}; REM {The first initializes the counter variable (j :=1)}; REM {The second sets the exit condition (j <= elements in the list)}; REM {The third sets the increment (j := j+1)}; REM {The @Prompt displays the list members in a prompt box}; @For(j := 1; j <= @Elements(cTextList); j := j+1; @Prompt([Ok]; "Element Number " + @Text(j); cTextList[j]) )
Using Action Statements in Formulas
Action statements and formulas can have certain side effects or cause some event to occur outside the formula itself. Action formulas themselves do not typically return any useful value. For example, @MailSend() is an action statement because it causes mail to be sent. It does have a return value, but the return value itself is not important. What is important is that it sends mail, an action that takes place outside the immediate formula, which is, therefore, a side effect. @Commands often take actions because they are based on the menu structure. For example, @Command([EditProfile]) opens a database profile document but does not return any value. Action formulas can be written for the following:
For example, agents are often used to mail notifications to users. Typically, the agent tests for the document's status, determines a list of recipients for the notification, and finally creates a mail message containing a subject, body text, and a link to the document. The following code fragment illustrates this technique:
REM "Send notification of Pending requests to the Approver"; SELECT Form = "SR" & cDocStatus = "Pending" &! @Contains(cLastAgent; "Approver Notification"); jcSendTo := @Name([Abbreviate];cApprover); jcCC := "" ; REM "For testing, add a blind carbon copy" ; jcBCC := ""; jcSubject := "Pending Service Request for " + cAssociate ; jcBody := "Click this doclink to open the request --> " ; @MailSend(jcSendTo; jcCC; jcBCC; jcSubject; jcBody; ""; [IncludeDoclink])
The last statement, @MailSend , is an action statement within the formula itself. The formula is used to populate the values for the parameters for @MailSend , and the router on the server then sends the mail.
Part I. Introduction to Release 6
Whats New in Release 6?
The Release 6 Object Store
The Integrated Development Environment
Part II. Foundations of Application Design
Forms Design
Advanced Form Design
Designing Views
Using Shared Resources in Domino Applications
Using the Page Designer
Creating Outlines
Adding Framesets to Domino Applications
Automating Your Application with Agents
Part III. Programming Domino Applications
Using the Formula Language
Real-World Examples Using the Formula Language
Writing LotusScript for Domino Applications
Real-World LotusScript Examples
Writing JavaScript for Domino Applications
Real-World JavaScript Examples
Writing Java for Domino Applications
Real-World Java Examples
Enhancing Domino Applications for the Web
Part IV. Advanced Design Topics
Accessing Data with XML
Accessing Data with DECS and DCRs
Security and Domino Applications
Creating Workflow Applications
Analyzing Domino Applications
Part V. Appendices
Appendix A. HTML Reference
Appendix B. Domino URL Reference