11 Working with Web Form Rules

This chapter describes how to create and edit form rules within a web form using Oracle Business Process Composer. Form rules are pieces of Java-script code that allow you to define how users interact with your web forms. This chapter assumes that you are familiar with the basics of Javascript.

This chapter includes the following sections:

11.1 Introduction to Form Rules

Form rules are pieces of server-side Javascript code that allow you to define how users interact with your web forms by defining how web form controls are displayed and defining the types of data users can enter.

You can use form rules to define the behavior of a web form. Typical uses of form rules include:

  • Adding dynamic behavior to a web form, including showing or hiding elements or enabling or displaying elements.

    For example, you can add form rules that show or hide certain form controls or entire sections based on the state of other form controls. Displaying form fields in a context-sensitive manner reduces clutter and makes it easier for users to navigate your forms.

  • Performing complex calculations.

    For example, you can compute the total invoice price on an order form from values of other form fields such as item quantity and price.

  • Performing complex validations.

  • Populating a dynamic drop down list.

The following sections describe the basic functionary of Javascript within form rules. See Web Form Rules Examples for information about specific use cases of form rules.

11.1.1 Form Rule Javascript Syntax

In Oracle Business Process Management (Oracle BPM), a form rule is expressed in JavaScript code. Form rules are saved and can be run after you save your BPM project.

Form rules generally have the following form:

if (condition) 
{    
    true actions;
} 
else 
{   
    false actions; 
}

You can create more advanced form rules primarily for the purpose of looping over repeating items. Following are some basic characteristics of JavaScript that you should be aware of when writing form rules:

  • Case-sensitivity: Javascript commands are case-sensitive.

    For example, var color1 and var Color1 are two different variables

  • Loosely typed variables: In Javascript variables are loosely typed.

    Variable types do not have to be explicitly declared. For example, var color = 'red'; and var num = 33 cause the variable color to be a string type and num to be a numeric type.

  • End of line semicolons: End-of-line semicolons are optional.

    However, you should use semicolons to terminate lines as part of good coding practice. This often prevents mistakes.

  • Comments: You can add comments to your code using either // or /* */ syntax

Oracle BPM does not support the following Javascript syntax:

  • switch statement

Additionally, Oracle BPM supports the following syntax with some limitations:

  • try-catch

For example, in the following example, the value of the control named FN is set to 'got exception' because the JavaScript variable named x is undefined in this form rule.

if (form.load) {   
    try {     
        x.randomproperty = 'blah';   
    } catch (e) {     
        FN.value = 'got exception';   
    } 
}

However, catching an exception when a form control is not defined is not supported and may behave unexpectedly. This is because Oracle BPM catches the problem while parsing the form rule before the JavaScript interpreter catches the error.

In the following example, the control named Color does not exist in the form.

if (form.load) {   
    try {     
        Color.value = "red";   
    }   catch(e) {     
        msg1.value = "error caught: " + e;   
    } 
}

11.1.1.1 Control Name

Form rules often must reference form controls. You must assign a control a name using the control's name property.

Names are case sensitive. If your control is named FirstName then you must write the form rule as FirstName.value. firstname.value does not work.

When using a control in a form rule, you must ensure that the control has a unique name. If multiple controls have the same name, the run time environment cannot determine which control the form rule refers. Form controls added to a form from the palette are usually guaranteed to have a unique name. The web form editor does not allow you to change the name of a control to one that already exists in your form.

However, there are several contexts where you can have multiple controls with the same name:

  • Controls added from XSD data sources

  • Controls added from the custom palette

  • Controls nested in sections

If there are two controls with the same label and at the same level, the control name is automatically made unique. If you try to edit the name such that it is no longer unique, the web forms designer prevents you from making the change.

When a control is dropped inside a section control, it is at a different nesting level then a control dropped outside a section. Two controls, one inside a section and another outside the section, are also at different nesting levels. The web form designer allows you to provide identical names to the controls.

If non-uniquely named controls are used in form rules there may unexpected results. If you encounter errors in a form, you can edit the names of control names to make them unique.

Note:

Editing the name of a from xsd schema control has no effect on the xml instance document created when the form is submitted nor on the xml validation

11.1.1.2 Form Rule Identifiers

Form rules refer to form controls using the Name property. If you have a control where the Name property is defined as MyControl, you can refer to properties of this control in form rules using the name as an identifier.

Form rule identifiers must always be of the following form:

Name.<property>

The following form rule identifier properties are supported:

  • visible: Set to false to hide a control and true to make the control visible.

  • value: Read or set the value of a control.

    This property is not applicable to sections, tabs and other controls that do not have values displayed to the user.

  • enabled: Set to false to disable a control so that a user can not change its value and true to enable it.

    This is not applicable to sections and tabs.

  • expanded: Set to false to collapse a group control (sections controls only) and true to expand a group control.

  • selected: Set to true to designate a tab as the selected tab. (tab controls only).

  • valid: The value of this property is true if the control contains a valid value otherwise its value is false.

    Validity is based on the control's type. For instance a numeric control is invalid if the user enters a string value that cannot be converted to a number. This property can be set and read.

  • required: Set to true to make a control required and display the red asterisk.

    This property only affects palette controls and not controls generated from XSD schema data source.

    This property is also valid for section controls. Setting a section required to false automatically sets all inner controls to not required.

  • options: Enables dynamic setting select control options (radio, dropdown and check box controls only).

  • label: Sets the label seen on any control including sections.

  • help: Sets the help text.

  • hint: Sets the hint seen on hover.

  • status: Sets the error message display whenever the control's value is invalid.

  • clicked: Used by the trigger controls. Its initial state is false.

    When a user clicks a trigger its state turns true.

  • printable: Set to false to remove the control from both the printable view and PDF submission document.

  • itemAdded: Used by repeat controls. Its initial state is false.

    When a user clicks "+" to add a repeat item AND when a repeat item is added through a Document URI as the form loads its state turns true.

  • itemRemoved: Used by repeat controls. Its initial state is false.

    When a user clicks "-" to delete a repeat item its state turns true.

  • itemIndex: Used by repeat controls.

    When an itemAdded or itemRemoved event fires, the value of itemIndex is set. For itemRemoved events itemIndex returns -1. For itemAdded events itemIndex gives you the index of the added item.

  • form.load: This property is true when the form is first loading.

    It is useful for setting default values through form rules that must be set before the user starts interacting with the form.

  • form.unload: This property is true when the users clicks the form's submit button.

    It is useful for setting control values just prior to the form's Doc Actions and Form Actions are executed.

Examples of identifiers used in form rules are:

  • FirstName.value

  • BillingAddress.visible

  • Email[1].value

  • Email[i].visible

The latter two are examples of repeating controls. repeating controls are discussed in more detail later. Note that the case of the properties is important. FirstName.value is a valid form rule identifier but FirstName.Value or FirstName.vAlUe are not.

11.1.1.3 Strings and Numbers

Because Javascript is a loosely typed language, there may be situations where you must add field values and the form rule performs string concatenation. There are several ways to tell the form rule to perform mathematical calculations instead of string manipulation. One simple way is by adding a *1 to the variable. id = id*1 + 1; ensures that id equals the current value plus one rather than the current value with a 1 appended. For example, if the current value was 4 and you didn't write id*1 the result would be "41" rather than 5.

You might encounter a situation in a form rule in which money controls perform a string concatenation rather than an addition. To correct this:

  • Open the form with the form rule in the designer, change the money controls to text controls, and then save the form.

  • Open the form, change the text controls back to money controls, and then save the form again.

11.1.1.4 Dates and Times

To initialize a date control, the data must be in the correct format.

Here are some examples of correct date formats:

  • d1.value = "2012-01-13"; //yyyy-mm-dd

  • d2.value = "01-13-2014"; //mm-dd-yyyy

  • d3.value = "jan 13 2014"; //using string for month

In general, it is not recommended to rely on either the browser's locale or the server's locale when writing date, time (time of day) and date/time literals in rules.

An example date and time rule follows:

if (form.load) {
  date.value = "2014-02-29";
  timeofday.value = "13:10:02";
  datetime.value = "2014-02-29T18:10:02Z";

This rule will initialize a date control in a form with 02-29-2014, a time control with 1:10 PM and a date and time control with 02-29-2014 in the date portion and 1:10 PM in the time portion.

Notice the special character "Z" at the end of the date/time value. This is equivalent to an offset of UTC-00:00 and indicates that the time of 18:10:02 is given in UTC. 18:10:02 UTC is equal to 1:10 PM Eastern Standard time.ind UTC.

Time and date/time controls also require correct formatting and an understanding of how web forms manages time zones. Web forms converts times into the browser's local time zone before displaying them in the form. However, if time data is not correctly formatted web forms will make multiple attempts to parse the time but will always display the time with NO time zone conversion. Here are examples of correct time formats:

  • t1.value = "20:30:00";

  • d2t2.value = "20:30:00Z";

  • t3.value = "20:30:00-04:00";

  • dt1.value = "2012-08-23T20:30:00";

11.1.1.5 Writing Conditions

One of the most common conditions is a form rule that executes as soon as the user enters a value in the control. The test for this condition depends on if the field is a string type or a numeric type.

String Types: text, textarea, date, phone

if (name.value.length > 0)

Numeric Types: money, quantity, number

if (name.value != null) or if (name.value > 0)

are both common test conditions.

Many times the condition name.value.length > 0 can be dropped altogether and the form rule can be simplified. This form rule executes whenever a user enters a value into either the control named firstname or lastname.

fullname.value = firstname.value + ' ' + lastname.value;

11.1.1.6 Select Controls

Radio controls, dropdowns, and check boxes are all examples of select controls. Radio and dropdown controls are single select. That is, if one item in the dropdown is selected then all other items in the dropdown are deselected. The same is true for a radio. Only one radio button can be depressed at any time. Thus the ID.value of radios and dropdowns are similar to the other input and output controls. The value is a single item.

check box controls are multi-select. Multiple items can be selected at any given time. Thus the ID.value of a check box is an array. Therefore on check boxes a valid expression is ID.value.length which returns the number of items in the value array. And ID[0].value would retrieve the 1st item in the list and ID[1].value the 2nd and so on. If you have a check box control with only one check box and that check box is unchecked, the array contains no elements. For a check box control a useful expression is ID.value.length == 0. Note that ID.length is not a valid expression. Also since a check box control is an array it is not a valid expression to write ID[0].value == . Because if at option in the check box control is unchecked, its value is not be an empty string. It simply does not exist in the array.

11.1.1.7 Initial Control State

Every control in your form has an initial default property state for the visible, expanded, value, valid, and enabled properties. However, you can change controls in the initial state in the form designer Edit tab. An initial state of a control can be modified several ways. One way is by simply tying a value into an input control. This sets the default value for the control when the form is first opened in use mode. Another way is by expanding or collapsing group controls. This sets the initial expanded state. The default state for the visible and enabled properties is set through the controls edit property panel. The edit property contains check boxes for visible and enabled for controls on which those properties make sense like input controls.

11.1.1.8 Form Rules and Repeating Controls

If you have a repeating control in a form that itself contains a repeating control, you cannot apply a form rule to the inner repeating control, since there's no way to tell which of the inner repeating items goes with which outer repeating item.

11.1.2 Using Dynamic Content in Form Rules

Real forms often require dynamic content in dropdown lists. Often based on the value entered into one form field, the values in other fields or options available in select controls must be dynamic.

11.1.2.1 Dynamic Content

Form rules enable invocation of http gets that return X-JSON headers with JSON objects. This allows complete flexibility in assignment of default values populated into form fields such as text controls and select (radios, dropdown, check box) controls. You can also use http.post(), http.delete(), and http.put() in form rules, although you must use URL parameters with them, as they do not all support payloads.

Here is an example that shows the syntax of the http.get. This form rule invokes the http get, which must return a JSON object. The method on the servlet can do whatever necessary such as querying a database given the itemName to retrieve the itemPrice. In this example the JSON object returned contains a field called price. The eval converts and assigns the JSON object to the javascript variable x. Then x can be used in the form rule as necessary. In this case it is used to set a value to the form field called Price.

eval('x=' + http.get('http://<webhost>/test/json/getPrice?itemName=' + itemName.value));
Price.value = x.price;

Another example is where the JSON object returned in the http.get response contains an array called clients. This array can then be used to set the options in a dropdown list.

eval('x=' + http.get('http://<webhost>/test/json/getClients')); 
Clients.options = x.clients;

Here is another example of a servlet that returns a JSON object after authenticating a user and password.

@Override
public void doGet (HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException 
{     
    try {  
        String u = request.getParameter("username");  
        String p = request.getParameter("password");
        if (authenticate(u, p) == null)   
                response.addHeader("X-JSON", "{auth:false}");  
        else   
                response.addHeader("X-JSON", "{auth:true}");   
    } catch (Exception e) {  
        throw new ServletException(e);   
    } 
} 
 

This servlet can be used in a form rule as follows:

if (signForm.clicked) 
{   
    eval('x=' + http.get('http://<webhost>/MYservices/signForm?username=' + u.value + '&password=' + p.value));    
    if (x.auth)   
    {     
        m.value = "<center>Authenticationn Succeeded</center>";   
    } else   
    {     
      m.value = "<center>Invalid username or password</center>";     
      } 
}

It is important to note that the http.get is accessing your http service through a URL. Certain characters must be encoded in order to correctly pass through an HTTP URL You may not always be able to control the values your users enters into your form fields. If the value may contain one of these characters that value must be encoded.

For example, this http.get contains a password parameter. Since user passwords may contain characters such as '#' you should encode the value. The built-in javascript method encodeURIComponent() makes this easy.

eval('x=' + http.get('http://<webhost>/MYservices/signForm?username=' +  
                         u.value + '&password=' + encodeURIComponent(p.value)));

You may also have to decode the URL parameter in your HTTP service. For example:

import java.net.URLDecoder;
String p = request.getParameter("password"); 
p = URLDecoder.decode(password, "UTF-8");

11.1.2.2 Reusing Dynamic Content

Fetching dynamic content from your back end system can be the source of degrading form performance. If your form must use the dynamic content multiple times you can improve performance by fetching the content once from the http.get() and saving the returned JSON string in a hidden text form control. You can then later read the text control value and eval it again rather than having to call your back end system multiple times.

For example, add a hidden text control to your form named jsonUserData. Add a single line to the form rule that retrieves the user data from your back end system through the http.get():

jsonUserDate.value = x;

In other form rules that also require the user data, rather than calling http.get() to your back end system again, add the following line to your form rule:

var val = jsonUserDate.value;   
    eval('x' = val);

This has the affect of setting x to the same string as if you had again fetched content from your back end system.

11.1.3 Using Data and Built-in Methods in a Form Rule

You can access built-in data and methods within your web form rules.

11.1.3.1 Built-in Data

Oracle BPM provides certain data to your form rules. This includes information about the person currently using your form, the tenant and form information. You retrieve this data in your form rule using the _data.getParameter('<data name>') syntax.

The following is a list of the available data:

  • subject.id - logged in user's username

  • subject.first.name - logged in users's First Name

  • subject.last.name - logged in users's Last Name

  • subject.roles - list of all the roles for the logged in user (available in v4.1.5)

You can use a form.load form rule to pre-populate fields in your form with information about the currently logged in user. For example, if you have controls in your form named ID, FirstName, LastName, Email and Roles.

Note:

Setting the value of a control to an array or to a random JavaScript object is not allowed.

11.1.3.2 Built-in Methods

Oracle BPM provides built-in helper methods for common functionality required by form rules. Here is the list of available methods for working with dates and times:

  • Time frevvo.currentTime(form) - returns the current time in the user's local time zone.

    This method should only be used to set the value of a Time control.

  • Date frevvo.currentDate(form) - returns the current date in the user's local time zone.

    This method should only be used to set the value of a Date control.

  • DateTime frevvo.currentDateTime(form) - returns the current date and time in the user's local time zone.

    This method should only be used to set the value of a Date/Time control.

Here is an example of setting a Time control named Tm, a Date control named Dt, and a Date/Time control named DtTm.

Tm.value = frevvo.currentTime(form);
  Dt.value = frevvo.currentDate(form);
  DtTm.value = frevvo.currentDateTime(form);

The currentTime(), currentDate() and currentDateTime() does not work in a form.load form rule unless you specify a time zone on the form's Url through the _formTz Url parameter. This is because the form server must know the time zone in which to return the date and time. If you do not specify a _formTz the methods return null and the control values remain blank. For example, to specify Eastern time: &_formTz=America/NewYork.

Use the following methods to work with users and roles:

  • boolean isUniqueUserId (String userId, String tenantId) - returns true if this user does not exist in the tenant and false if it does

  • boolean isUniqueRoleId (String roleId, String tenantId) - returns true if this role does not exist in the tenant and false if it does

11.1.4 Understanding How Form Rules Work at Runtime

When using form rules within a web form, it is important to understand how form rules behave at runtime.

11.1.4.1 When are Form Rules Executed?

When you create or edit a form rule, Oracle BPM determines the list of controls and properties of those controls that the form rule depends upon. The form rule is automatically executed whenever there is a state change that is relevant to the form rule. Form rules are also executed sequentially in a top to bottom order as they are seen in the form rules panel.

Note that form rules can trigger the execution of other form rules. So, if a form rule, R1, sets the value of a control with Name A, and there is a form rule R2, that depends on A.value, then form rule R2 is triggered and executed.

A form rule typically refers to one or more form controls and their properties, and it is executed if any of those properties change value. Note that form rules are not fired when the page is loaded. For example, the following form rule is only executed when N1.value changes its value.

if (N1.value > 0 || N2.value > 0) {   
    T.value = N1.value + N2.value; 
}

Now assume a use case where you want to show a message when a user enters an invalid email. The form has a required Email input control (Name=E) and an action should be executed if the email control 'valid' property is 'false'. You can write the form rule as:

if (!E.valid) { 
// code to show message here. 
}

The previous code does not work as expected as E is a required field and therefore E.valid initial value is 'false' since the field is in initially empty. When the user enters an invalid Email address, E.valid still has the value 'false' and the form rule can not execute since there is no state change. The following code works properly.

if ((E.value.length > 0) && (!E.valid)) { 
// code to show message here.
}

The form rule depends on both the value of E.valid and E.value.length and therefore, when a string longer than zero characters is entered the form rule is executed and the message is shown.

11.1.4.2 Infinite Loops

It is easy to create form rules that enter a loop condition. For example, a form rule that updates A.value based on B.value and another form rule that updates B.value based on A.value. They can continually trigger each other.

The Oracle BPM server prevents this from happening by setting an execution time limit for each form rule. Any form rule that takes longer than 5 seconds to execute is forcibly stopped. Note that this is a very lengthy period of time for most computational tasks and the vast majority of form rules are not impacted by this constraint. However, since Oracle BPM is a hosted site that may be simultaneously used by numerous users, there is a time limit imposed on these computations.

11.1.5 Debugging Form Rules

This section describes ways of debugging forms.

11.1.5.1 Debugging Duplicate Control Names

When you're designing forms, the forms designer generally prevents you from giving controls duplicate names, but you can give controls the same name if the controls are contained within different section controls. For example, you can have two sections in a form named Home and Office, and each section can have a text control named Address. However, if you want to use either of the Address controls in a form rule, you have to give them unique names.

One way to tell if a form rule is breaking because of duplicate names is to look at the log in the Tomcat console. If you see a message similar to the following, it is probably a duplicate control name problem.

16:50:35,390 WARN RuleObserver:455 - Error evaluating rule [Translate.Translate Form Rule]: Java arrays have no public instance fields or methods named "value." ([Translate.Translate Form Rule]#2) if (form.load) {Name.value = 'Nancy';}

The phrase Java arrays have no public instance fields or methods... means Java is interpreting the controls with the same name as an array, not single controls.

11.1.5.2 Form Rule Profiling

Form rule execution can be profiled to determine how much time each form rule takes to execute. This can help you tune and improve performance in forms that use a lot of form rules. To turn on profiling set the rule-debug=profile on the url.

If your form rules are executing database queries to populate dynamic select controls (dropdown, radio, check box), form rule profiling can help you identify if the database queries are taking too long. You may see HTTP calls taking a long time to execute.

11.2 Working with Form Rules

Oracle Business Process Composer provides an editor that allows you to create and edit a form rule.

The following procedures describe how to create and test form rules.

11.2.1 How to Create a Form Rule

To create a new form rule:

  1. Open the form where you want to create a new form rule.
  2. In the form toolbar, click the Rules button.
  3. Click Create New Rule.
  4. Click Edit, then edit the fields in Table 11-1 as required:

    Table 11-1 Form Rule Fields

    Element Description

    Name

    Defines the name of the form rule. Change this to something meaningful.

    Enabled

    Select to enable the form rule within your form. If you deselect this option, the form rule is not applied to the form at runtime.

    Description

    Provides a description of the form rule. Modify the default to describe the behavior of the form rule and how it functions within the context of the form controls and other form rules within the form.

    Rule

    Defines the form rule.

  5. After you have finished creating and editing the form rules, click Edit in the toolbar to return to the web forms designer.

11.2.2 How to Test a Form Rule

You can test the behavior of form rules while testing a web form. See How to Test a Web Form for more information.