10 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 enable you to define how users interact with your web forms. This chapter assumes that you are familiar with the basics of Javascript.

This chapter contains the following sections:

10.1 Introduction to Form Rules

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 will 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.

Form rules are pieces of server-side Javascript code that enable 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.

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

10.1.1 Form Rule Javascript Syntax

In Oracle BPM, a form rule is a 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;   
    } 
}

10.1.1.1 Control Name

Form rules often need to 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 will 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 to. Form controls added to a form from 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's name will automatically be made unique. If you try to edit the name such that it would no longer be 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. Also two controls, one inside a section and another outside the section are also at different nesting levels. The web form designer enables you provide identical names to the controls.

However, 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 control names 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

10.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 rules 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 (grey out) 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 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 will be 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 checkbox 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 via 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 will return -1. For itemAdded events itemIndex will give 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 via form rules that you need to 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. We will discuss repeating controls in more detail below. 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.

10.1.1.3 Strings and Numbers

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

You may also encounter a situation in a form rule in which money controls perform a string concatenation rather than 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 a money controls, and then save the form again.

10.1.1.4 Writing Conditions

One of the most common conditions is a form rule that executes as soon as the user enters a value in 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;

10.1.1.5 Select Controls

Radio controls, dropdowns and checkboxes 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.

Checkbox controls are multi-select. Multiple items can be selected at any given time. Thus the ID.value of a checkbox is an array. Therefore on checkboxes 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 checkbox control with only one checkbox and that checkbox is unchecked, the array will contain no elements. For an checkbox control a useful expression is ID.value.length == 0. Note that ID.length is not a valid expression. Also since a checkbox control is an array it is not a valid expression to write ID[0].value == . Because if at option in the checkbox control is unchecked, it's value will not be an empty string. It will simply not exist in the array.

10.1.1.6 Initial Control State

Every control in your form has an initial default property states for the visible, expanded, value, valid and enabled properties. However you can change a controls initial state in the form designer Edit tab. A control's initial state 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 via the controls edit property panel. The edit property contains checkboxes for visible and enabled for controls on which those properties make sense like input controls.

10.1.1.7 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.

10.1.2 Using Dynamic Content in Form Rules

10.1.2.1 Dynamic Content

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

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, checkbox) 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;

Imagine another example 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/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 could 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 via a URL. Certain characters must be encoded in order to correctly pass through via 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 need 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");

10.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 needs to 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. Then you can 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 via the http.get():

jsonUserDate.value = x;

In other form rules that also need 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 will have the affect of setting x to the same string as if you had again fetched content from your back end system.

10.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.

10.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 - A 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.

10.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 timezone. 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 timezone. 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() will not work in a form.load form rule unless you specify a timezone on the form's Url via the _formTz Url parameter. This is because the form server needs to know the timezone in which to return the date and time. If you do not specify a _formTz the methods will return null and the control values will 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

10.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.

10.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 will be 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 will be triggered and executed.

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

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

Now let's assume a use case where you want to show a message if 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'. One could write the form rule:

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

The code above would not work as expected. 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 would still have the value 'false' and the form rule would not execute since there is no state change. The code below would work properly.

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

Now, 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 will be executed and the message will be shown.

10.1.4.2 Infinite Loops

It's 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 could continually trigger each other.

The Oracle BPM server will prevent this from happening by setting an execution time limit for each form rule. Any form rule that takes longer than 5 seconds to execute will be forcibly stopped. Note that this is a very lengthy period of time for most computational tasks and the vast majority of form rules will not be 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.

10.1.5 Debugging Form Rules

This section describes ways of debugging forms.

10.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 name 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 one below, it's 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.

10.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, checkbox), form rule profiling can help you identify if the db queries are taking too long. You will see HTTP calls taking a long time to execute.

10.2 Working with Form Rules

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

10.2.1 How to Create a Form Rule

Business Process Composer provides an editor that enables you to create and edit 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 following fields as required:

    Element Description
    Name Defines the name of the form rule. You should change this to something meaningful.
    Enabled Select to enable the form rule within your form. If you deselect this option, the form rule will not be applied to the form at runtime.
    Description Provides a description of the form rule. You should 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.

10.2.2 How to Test a Form Rule

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