frevvo v11.1 is no longer supported. Please visit the Documentation Directory for our current Cloud Release and other versions.

Rule Examples

Overview

This chapter contains numerous real-world samples of the custom dynamic behaviors you can add to your forms and workflows. Many of the business rules described below are easily created with the frevvo Visual Rule Builder by simply selecting appropriate functions or controls from your forms/workflows using a visual wizard. Rules can still be written with JavaScript in order to build any degree of complex, powerful business logic and integrate with your Web Services and frevvo connectors.


Navigating this Page

This page is divided into the following sections. To search the headings and content, type Ctrl-f and enter a keyword.

SectionSubsectionsExamples
Rule Events

Rule Examples#form.load | Rule Examples#form.unload | Rule Examples#form.activate

Rules by Function



Rule Examples#Calculations

Control Types for Calculations | Rule Examples#Addition | Rule Examples#Multiplication | Calculate a Subtotal | Calculate a Grand Total | Round a Number | String Concatenation

Show/Hide Rules

Show/Hide based on a Control Value | Show/Hide if another Control is Filled | OR Conditions | Multiple Conditional Statements (if-else if) | Show a Tab on a Workflow Step | Show/Hide Submit and Cancel Buttons | Show/Hide Approval Sections on Workflow Steps | Switch Form View in a Workflow

Control Property Rules 

Rule Examples#Printable | Enable/Disable Submit and Continue Buttons | Enable/Disable a Control | Set Valid/Invalid Property | Set Error Message | Set One or the Other Control to Required |  Required Field Status in Accessible Forms | Dynamic Label, Help or Hint | Expand/Collapse Section | Select Tab | Next Tab | Rule Examples#Pattern

Formatting Rules

Format Money Values to Display in a Message Control | Format Money in Text Controls | European Number Format | TextArea Max Length | Convert TextArea newline to HTML break | TextArea Wrap Long URL in PDF Snapshot |     Split Phone Number

Prefilling Rules

Tenants, Roles, and Users | Initialize Form with Current User Info | Populate Form with Any User's Info | Populate Dropdown with Users/Roles | Populate Dropdown with Roles Assigned to a User | List Users in a Role | List Users in Multiple Roles | List a User's Roles | Verify User | Verify Role | Unique and/or Sequential ID

Rule Examples#Signatures

Signature via Web Service | Set Signed Date | Enable Click to Sign for Anonymous User

Doc Action, ADA, Precondition-Related

Conditionally Send Email

Rules by Control TypeSelection Controls

Options | Set Options | Set Options to Control Values | Default Selection | Display Selected Option Label | Clear Selections | Randomize Options | Dynamic Options | Options from a Database | Combobox Options from a Webservice | Options from a Google Sheet | Synchronized Dropdowns | Find a Selected Options Index | Filter Dynamic Options | Triggers & Dynamic Options | Search a JSON String | Cascading Dropdowns | Dynamic Control Initialization with JSON

Comment Property Rules | Set Comment Field | Retrieve Comment Value | Set Max Length for Comment Field

Checkbox Control Checkbox Initialization | Display Selected Checkbox Labels | Checkbox isFilled Condition | Selected Checkbox Options in Conditions | Many Checkbox Comments | Set Options for a Checkbox to Values of Another Checkbox | Count the Options Selected | Repeating Checkboxes

T/F Boolean Control | T/F Value as Condition | Set a T/F Value | Clear a T/F Value


Date and Time Controls 

Working with Date and Time Rules

Set Dates and Times | Today's Date and Time | Date/Time Stamp | Create a Date, Time or DateTime Value | Set a Future Date | Set a Future Date Based on Five-Day Work Week | Get Day of Week from DateCalculate a Return Time | Set a Pay PeriodTime Zone Adjusted for Daylight Savings

Rule Examples#Duration | Rule Examples#Age | Stay Duration (Days) | Duration Including Start & End | Duration (Working Days) | Duration between Date/Time | Calculate Hours Apart | Calculate Time Between

Date Conditions Date No More Than x Days From Today | Date No More Than x Days Ago | Show Error Message if Date1 is On or Before Date2 | Display a Date in a Message Control

Checking a Date for Extra Digits | Copy Date, Time and DateTime Values


Rule Examples#Repeats 

Show/Hide Controls in a Repeat | Repeat itemAdded Condition | Collapse Prior Items When Item is Added | Dynamically Set Min and Max in a Repeat | Repeat Item Initialization | Repeat itemAdded by Init Doc | Repeat Item Increment


Rule Examples#Tables

Table Column and Cell Properties | Show/Hide a Column in a Table | Hide the Minus Icon on a Table Row | Dynamically Setting Min/Max in a Table | Populate Table Rows based on Query Results | Clear Values in a Table | Clickable Links in a Table | Set a Value Based on Table Rows | Count the Number of Items in a Table | Sort a Table by a Column Value


Upload Control

Display Uploaded Image | Dynamically Set Min/Max Properties | Upload Control Required/Optional | Count Uploaded Files | Display Uploaded File Names in a Dropdown | Rename an Uploaded File | Set Max Total Size for Submission


Message Control

Display Form Data | Display a PDF in a Message Control | Method 1: Host the PDF on your website | Method 2: Upload the PDF in frevvo's PDF Mapping tool | Display PDF with mapped fields | Embed a Video in a Message Control

Rules for Integration


Rule Examples#Geolocation | Use Google Maps API to Calculate Mileage | Add a Calendar Event LinkSearch Popup | LDAP Custom Attributes | REST Web Services

Rule Events

Whether you are using the Visual Rule Builder or editing Rule Code, it is helpful to understand some of the common events that you will use to trigger rules as they can affect the behavior of your forms/workflows. The following examples illustrate the use of form.load, form.unload, form.activate, and form.deactivate events. 

Refer to the When Do Rules Execute section for more information.

form.load

Rules can be used to initialize field values. This is a very useful feature and is often used to dynamically populate dropdown options from a database. Rules using form.load are triggered when a form first loads and when a workflow is loaded from a task list.

Rules using itemAdded only execute for repeat items added when the user clicks +, and for those added from an initial instance document (See Document URIs). It does '''not''' execute for those items that you have added to your form in the Form Designer. You can either add defaults directly via the form designer or add a 2nd rule to your form as follows.

These two rules together initialize the dropdown fields inside a repeat that is already in the form via the Form Designer, as well as those added each time a user clicks "+" on the repeat to add a new item & via initial documents. These controls are initialized based on a value set in another field.

Rule Code
//1st Rule - Default Items
if (form.load) 
{ 
    // The form contains two repeat items by default.
 
    if (department.value === 'Marketing') { 
        Managers[0].options = ['Joe', 'Mary', 'Stan', 'Cindy'];
        Managers[1].options = ['Joe', 'Mary', 'Stan', 'Cindy']; 
} 
    if (department.value === 'Sales') {
        Managers[0].options = ['Lou', 'George', 'Wendy']; 
        Managers[1].options = ['Lou', 'George', 'Wendy'];  
    } 
}
 
//2nd Rule - Items Added
if (Erepeat.itemAdded) 
{
    var index = Erepeat.itemIndex; // which item is this in the list  
    ES[index].value = 'Day'; // default the employee shift to day
 
    // Use options already selected in form.load rule 
    Managers[index].options = Managers[0].options; 
}

In the Rule Builder, designers can specify at the rule level whether or not a given rule is intended for initialization or not. Checking the Initialization Only checkbox on the wizards marks a rule as an initialization rule. If checked, the generated rule will be wrapped in an if (form.load) statement. The rest of the generated rule (conditions, actions, etc) will be contained within this if statement. The rule shown sets the field named name to "Paul" if the value of the Gender dropdown is "male" and the workflow is on the second step.

Rule Code
if (form.load) {
  if ((gender.value === 'male') && (frevvo.step.on('_8yjc0OSvEeafOKEJqSXZcw'))) {
    name.value = 'Paul';
  } else {
    name.value = null;
  }
}

 If this same rule is not marked for initialization, it will behave like an anytime rule which means that it will fire when the form loads as well as a control value change. The generated JavaScript will include:

var e = form.load;

form.unload

Rules including the form.unload event are not yet supported in the Visual Rules Builder and thus still requires some JavaScript. Rules can be used to finalize field values after the users clicks the form's submit button but prior to the Form and Doc Action execution. Rules using form.unload are triggered when the form user clicks the submit button and for workflows when the user clicks continue to go to the next activity or finish to complete the workflow.

One common example use is for an order form order number. You may only want to assign a unique sequential order number to each order submission. You could initialize the form's order number when the form loads using form.load. However, if someone starts filling in the order form but never submitted it you do not want to consume the next order number in sequence if it will never be used. Using form.unload you can assign the number after the submit button click but before the form data is submitted.

Here OrderNum is the name of invisible control.

Rule Code
/*member num */
var x;
 
if (form.unload) 
{ 
    eval('x=' + http.get('http://(your webhost)/json/getNextOrdernum'));  
    OrderNum.value = x.num;
}

If you have a rule in your form that changes any control(s) after clicking Submit, thereby making the form invalid, the form will no longer be submitted and the invalid form will redisplay. This change avoids creating an invalid xml for a successful submission.

This feature is implemented for forms only.

The form.deactivate event is identical to the form.unload event. Whenever a step gets deactivated in a screenflow or multi-user workflow, it is unloaded as well. form.deactivate was added for completeness. The rule in this example will also work if it was written using form.deactivate instead of form.activate.

form.activate

In a desktop browser, users can navigate back and forth between the steps of a frevvo screenflow or multi-user workflow using the Navigation toolbar. Previous and Next buttons are available on mobile devices for this functionality. Workflow designers should consider this navigation when writing business rules. Steps in a screenflow are read/write since they are all performed by the same user and editing is allowed.

form.load is only triggered when the step is loaded for the first time. This is not very efficient. form.activate triggers every time a read/write step is displayed. This makes form.activate and form.deactivate events that give designers a more reliable way to set properties for steps in a screenflow.

Let's take a look at a two step screenflow designed using Linked forms to illustrate these points. Step 1 has 2 text fields, Text 1 is visible and not required. Text 2 is initially not visible and not required. We added a rule in the Visual Rule Builder that sets Text2 to visible and required on Step 2. 

Rule List

Notice that by default the VRB creates the rule on the form.load event.

Rule Code Generated by Rule Builder
var event = form.load;
if (frevvo.step.on('_4neSkLpxEeu1dMbj8N29dg')) {
  Text2.visible = true;
  Text2.required = true;
} else {
  Text2.visible = false;
  Text2.required = false;
}


Let's see what events occur when Step 1 is loaded.

  • LOAD Step 1 – the form.load is triggered since step 1 is being run for the first time. Text 1 is visible and required and Text 2 is not visbile and not required
  • ACTIVATE Step 1 – activate triggers because the step is loaded for the first time.

The user clicks continue and the workflow navigates to Step 2. Note Step 1 is deactivated and unloaded and Step 2 is loaded and activated:

  • DEACTIVATE Step 1
  • UNLOAD Step 1
  • LOAD Step 2 – the workflow is on step 2 – the form.load triggers because step 2 is being loaded for the first time. Text 2 is now visible and required.
  • ACTIVATE Step 2 – this runs because step 2 is displayed.

The user navigates back to Step 1 without filling in Text 2. Note Step 2 is deactivated and unloaded and step 1 is activated.

  • DEACTIVATE Step 2
  • UNLOAD Step 2
  • ACTIVATE Step 1

form.load is not triggered, so Text 2 is not visible (due to initial property value) but it is still required because Step 1 did not LOAD again. When the user clicks Continue to move to Step 2 the screenflow does not move forward.

If the rule is written based on the form.activate event instead, here’s what happens. (In this case, we edited the rule code and changed form.load to form.activate.)

  • The user initiates the screenflow.
    • LOAD Step 1 – the form.load is triggered since step 1 is being run for the first time. Text 1 is visible and optional  and Text 2 is not visbile and not required
    • ACTIVATE Step 1 – activate triggers because the step is loaded for the first time
  • The user clicks continue and the workflow navigates to Step 2. Note Step 1 is deactivated and unloaded and Step 2 is loaded and activated.
    • DEACTIVATE Step 1
    • UNLOAD Step 1
    • LOAD Step 2 – the workflow is on Step 2 – the form.load triggers because Step 2 is being loaded for the first time. Text 2 is now visible and required. 
    • ACTIVATE Step 2 – this runs because step 2 is displayed.
  • The user navigates back to Step 1 without filling in Text 2. Note Step 1 is only activated.
    • DEACTIVATE Step 2
    • UNLOAD Step 2
    • ACTIVATE Step 1
  • The form.activate event is triggered, so the rule runs. The visible and required properties for Text 2 evaluate to false.
  • When the user clicks Continue to move to step 2 the screenflow moves forward. The user fills in Step 2, clicks Finish and completes the screenflow.

For multiuser workflows, form.load and form.activate are triggered when the step is displayed for the first time. form.activate is not triggred when navigating backwards via the Task List as these steps are Read Only. The Activity assigned to the current user will load only once but will activate every time they are loaded in a particular session.

Rules by Function

The examples in this section are organized by the rule's business function, i.e. calculations/mathematical rules, show/hide rules, and rules for various control properties.

Rule Examples#Calculations | Show/Hide Rules | Control Property Rules | Formatting Rules | Prefilling Rules | Doc Action, Activity Doc Action, and Precondition-Related Rules

Calculations

Business logic that automatically calculates subtotals, total, and other math functions is easier for your users and prevents mathematical mistakes. Here are several common examples:

  • Expense Report Subtotals and Grand Totals
  • Time Sheets Hours per task, per day and per week
  • Purchase Order line item costs

Control Types for Calculations

When using the Visual Rule Builder for calculations, the expressions must have the same value type as the control. For example, math functions are only available on number, quantity, and money controls. If there is a type discrepancy, the VRB Help Mode will display a "Field type mismatch" error:

Let's say you have two Number controls that take a decimal, and you want to set the total in a Quantity control, which only takes a whole number. This rule is valid in the Visual Rule Builder. However, if the user enters values such that the sum is a decimal (such as "2.5"), the rule will set the value as requested, but will truncate the decimal in the total (Quantity) control, showing the value "2" instead of "2.5".

The Visual Rule Builder supports text-to-number conversion with the function number(). For example, you may want to calculate a total from two Text controls that contain numbers. Set Total to sum(number(Text1), number(Text2)) to convert the Text control strings to numbers.

Addition

Adding two or more values is a common and simple calculation. Let's take a form that has three Quantity controls named Q1 (Quantity 1), Q2 (Quantity 2) and T (Total). We want to add the values in Q1 and Q2 and display the result in the Total field. This rule will automatically fire whenever the user types something in Q1 or Q2 and will set the value of Total to their sum.

Action Wizard

You can use the sum function or just type Q1 + Q2 in the expression field.

Rule List

or...


How it works

 Click to learn how it works...

Click Rule Code to view the Javascript set by the Visual Rule Builder. It uses the standard Javascript assignment operator (=) to set the value of T, and the addition operator (+) to sum the values of Q1 and Q2.

Rule Code
var event = form.load;
T.value = Q1.value + Q2.value;

Multiplication

Multiplication is a common and simple calculation. Let's take a form that has a Price, Quantity and Total controls. We want to multiply Price times Quantity and display the results in the Total field.

Action Wizard

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. It uses the standard JavaScript assignment operator (=) to set the value of Total, and the multiplication operator (*) to sum the values of Q1 and Q2. Notice that in this case the VRB has placed the expression inside parentheses. While not strictly necessary in this simple rule, the parenthesis act as a grouping operator to ensure the correct operation precedence for more complex calculations.

Rule Code
var event = form.load;
Total.value = (Price.value * Quantity.value);

Calculate a Subtotal

You can also use multiplication in a Table Control to display a subtotal for each row. The Visual Rule Builder automatically creates the code to handle adding/deleting table rows. Imagine a Purchase Order workflow with a Table control to list the items being ordered. The Table has columns for Price, Quantity and Subtotal. You want to multiply Price times Quantity and display the results in the Subtotal fields for each row in the table. 

Action Wizard

Rule List


How it works

 Click here to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. The Visual Rule Builder uses an if else statement (condition ? Value if True : Value if False) to assign a value to variables (Subtotal_start_index and Subtotal_end_index) whenever a row is added. Then it uses a for loop that iterates over the table setting Subtotal in each row ('i') to the result of the multiplication expression for that row. 

Rule Code
// if a row is added, set Subtotal_start_index to the index of that row. Else, set it to 0.
var Subtotal_start_index = Table1Repeat.itemAdded ? Table1Repeat.itemIndex : 0;
// if a row is added, set Subtotal_end_index to the Subtotal_start_index plus 1, else set it to the number of rows that have a value in the Subtotal column.
var Subtotal_end_index = Table1Repeat.itemAdded ? Subtotal_start_index + 1 : Subtotal.value.length;
var event = form.load;
for (let i = Subtotal_start_index; i < Subtotal_end_index; i++) {
  Subtotal[i].value = (Price[i].value * Quantity[i].value);
}



The sum() function mentioned above will calculate over the entire repeat/table. To use addition to subtotal individual rows, use the "+" operator, i.e. Set Subtotal to Item1 + Item2. 

Calculate a Grand Total

Consider a Purchase Order form with a table that calculates subtotals as described above. Now you want to add a Grand Total field to your Purchase Order workflow. The Grand Total field contains the sum of the Subtotal fields in each row of the table. Add a Money control to your form/workflow and name it Grand Total. Use the Rule Builder to create the rule. The Rule Builder automatically creates the code to handle adding/deleting table rows.

Action Wizard

Rule List

How it works

 Click here to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. The Visual Rule Builder uses a variables (sum_result) in a for loop to get the calculated total for the number of rows that have a value in Subtotal. The value of GrandTotal is set to that variable, updating whenever a row is added or removed.

Rule Code
var sum_result;
//run this rule whenever a table row is added or removed or the form loads
var event = ItemsTable.itemAdded || ItemsTable.itemRemoved || form.load;
sum_result = 0;
//loop through the table when Subtotal has a value, adding each Subtotal value to the variable sum_result
for (let i = 0; i < Subtotal.value.length; i++) {
  sum_result += Subtotal[i].value;
}

Round a Number

A common requirement is to set a display a number control with a specific number of decimal places. Let's say you have a Time Sheet where you collect the number of hours worked per day and total them in a Line Total field. Your form has a table with Number controls for the daily hours and a Number control for the total. When testing your form, you enter values with decimal points and notice the total unexpectedly calculates to a number with many decimal places. This is JavaScript behavior when calculating decimal point addition. You can use the round() function in the Visual Rule Builder to prevent this and round the Total Hours value to two decimal places.



Action Wizard

The round() function takes two parameters - number and decimal digits. In this example, we're adding several control values to get the number so we enter the addition operation inside parenthesis. The decimal digits parameter is 2.

At runtime, notice that Total Hours is now correctly rounded to two decimal places.

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. Because this example is a table, this rule loops through the table to set values on each row. It uses the JavaScript Math object with round function, which is a method for rounding to whole numbers. It then uses exponential notation ('e' + 2, 'e-' + 2) to round to the correct number of decimal places. 

Rule Code
var TotalHours_start_index = TimesheetTableRepeat.itemAdded ? TimesheetTableRepeat.itemIndex : 0;
var TotalHours_end_index = TimesheetTableRepeat.itemAdded ? TotalHours_start_index + 1 : TotalHours.value.length;
var event = form.load;
for (let i = TotalHours_start_index; i < TotalHours_end_index; i++) {
  TotalHours[i].value = Number(Math.round((Mon[i].value + Tue[i].value + Wed[i].value + Thu[i].value + Fri[i].value) + 'e' + 2) + 'e-' + 2);
}


You can also handle this in the Rule Code editor. Use the built-in toFixed(n) function to truncate the result to n number of decimal places.

Rule Code
var x;
x = (Mon.value + Tue.value + Wed.value + Thu.value + Fri.value);
LineTotal.value = x.toFixed(2);

Another approach would be to assign Patterns that limit the number of decimal places.

String Concatenation

The Visual Rule Builder can also handle string concatenation in the function concat(), which takes as many expressions as you want, each separated by a comma. Any literal values used as expressions should be placed inside quotes. For more information, read our documentation on writing rules with strings vs. numbers. In this example, we will create a rule with the Visual Rule Builder to concatenate the First Name and Last Name and display the results in the Fullname field.

Condition Wizard

Action Wizard

Else Action Wizard

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. Notice that each expression on the right side of the value assignment is separated by a '+' operator. In JavaScript, the + operator will add numeric values but concatenate string values. See the Operators documentation for more info. The VRB uses the Boolean check in the if condition, but this is not strictly necessary. Writing if ((FirstName.value) && (LastName.value)) would work the same way.

Rule Code
var event = form.load;
if ((Boolean(FirstName.value)) && (Boolean(LastName.value))) {
  fullname.value = FirstName.value + ' ' + LastName.value;
} else {
  fullname.value = null;
}


Show/Hide Rules

Often forms/workflows need fields that are used conditionally, depending on the answers to other fields. For example, if your form requires both a Shipping Address and Billing Address but the user has checked "Shipping Address is the same as Billing Address" then it's nice to not clutter the form with the unnecessary Shipping Address input fields. You can use rules to hide the Shipping Address and show it only when the form user says they are different.

The easiest way to create a Show/Hide rule is to use the frevvo Visual Rule Builder. Here are common reasons for using Show/Hide:

  • Show/Hide a section
  • Show a Message to the user to alert them to either an error or success condition
  • Show a Details Text Area when as user clicks Yes to "Would you like to describe your issues in detail?"
  • Show a Signature or Signed Section when the workflow reaches the approval step
  • Show/Hide a Tab on a Workflow Step

See the documentation for Data Sources and Schemas for details on implementing a Show/Hide rule with XSD controls.

Show/Hide based on a Control Value

Consider a Purchase Order form that contains a Billing Address section, a hidden Shipping Address section a Radio control named DiffShip that asks the question, "Is the Shipping Address different from the Billing Address?" If the Billing and Shipping addresses are the same there is no need to clutter the form with unnecessary Shipping Address input fields. You can use a rule to hide the Shipping Address and show it only when the form user says they are different. This rule will automatically fire whenever the user changes the response to DiffShip and will show/hide the shipping address section. In this example, an Else Action is helpful so that when the form loads, and if the user changes a Yes response back to No, the section will be hidden.

Condition Wizard

The condition for this rule will be the value of the control DiffShip is the literal value "Yes". Be sure to click the toggle to "Enter a literal value" and use the Form Outline to be sure the value you enter matches the control's option value exactly (it is case sensitive.)

Action Wizard

Else Action Wizard


Rules List

How it works

 Click here to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. The Visual Rule Builder creates an if-else statement. Note that the control name "ShippingAddress" is following by the property indicator ".visible", which takes a boolean value of true or false.

Rule Code
var event = form.load;
if (DiffShip.value === 'Yes') {
  ShippingAddress.visible = true;
} else {
  ShippingAddress.visible = false;
}

Show/Hide if another Control is Filled

This rule makes the message control NicknameMsg visible when the user enters a value into the Nickname input text control. It also hides the message control if the user deletes the value in Nickname.

Condition Wizard

This condition makes use of the built-in function "is filled". It will run whenever the control Nickname has data entered in it.

Action Wizard

Else Action Wizard

Rules List

How it works

 Click here to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. The Visual Rule Builder creates an if-else statement using "Boolean" to check if Nickname has a value. Boolean returns TRUE if Nickname has a value, and FALSE if it does not. The VRB uses the Boolean check in the if condition, but this is not strictly necessary.

Rule Code
var event = form.load;
if (Boolean(Nickname.value)) {
  NicknameMsg.visible = true;
} else {
  Nickname.visible = false;
}

OR Conditions

Here's another show/hide example where we are setting multiple conditions to show/hide a message control. This form has a radio control named Facility and a second radio control named CompanyFacility. This rule makes a message control named FacilityMessage visible depending on the selected options. If Boston is selected for the Facility control OR New York is selected for the CompanyFacility control, the hidden message control will display. 

Condition Wizard

Remember to change the "and" to "or" in the Logic expression so the rule will execute if either condition is met.

Action Wizard


Else Action Wizard

Rules List

How it works

 Click here to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. The Visual Rule Builder creates an if-else statement. Notice the use of double pipe characters in the if condition to represent "or". If you were to use the logic "and", you would see double ampersands, "&&".

Rule Code
var event = form.load;
if ((Facility.value === 'Boston') || (CompanyFacility.value === 'New_York')) {
  FacilityMessage.visible = true;
} else {
  FacilityMessage.visible = false;
}

Multiple Conditional Statements (if, else if)

Perhaps you want to show and hide different controls for each option selected from a dropdown or radio control. The Show/Hide rule structure is very similar to above. However, since selection controls can have many options, you need multiple Visual Rule Builder rules. Alternatively, you can use a JavaScript rule to put all of the switch cases in a single rule using if and else-if statements. It's a good idea in this case to set the values to null on the same switch case where they are hidden. Otherwise, if a user enters data and then changes their original selection, unwanted data may be passed via the hidden controls. 

Rule Code
if (SearchBy.value === 'Organization') 
{
  OrganizationName.visible = true; 
  FirstName.visible = false; 
  LastName.visible = false; 
  ClientID.visible = false; 
  FirstName.value = LastName.value = ClientID.value = null; 
} else if (SearchBy.value === 'Individual') 
{ 
  OrganizationName.visible = false; 
  FirstName.visible = true; 
  LastName.visible = true; 
  ClientID.visible = false; 
  OrganizationName.value = ClientID.value = null;
} else if (SearchBy.value === 'Client ID') 
{ 
  OrganizationName.visible = false; 
  FirstName.visible = false; 
  LastName.visible = false; 
  ClientID.visible = true; 
  FirstName.value = LastName.value = OrganizationName.value = null; 
} 

To achieve the same result using the Rule Builder, create 3 separate rules - one if the choice is Organizations, a second rule if the choice is Individuals and a third rule if the choice is Client ID.

 Click here to see the rules in the Rule List


Show a Tab on a Workflow Step

Tabs are often used like sections to show controls in a grouped view. You may want to display a particular tab only on the relevant workflow step. In this workflow, there is a Tab control with two tabs: Employee and Review. This rule makes the Review tab visible only when the workflow is on the Review step (Step 2).

Condition Wizard

This condition makes use of the built-in condition "current step", which has several options available such as "is", "is on or after", or "is before". The expression field will show a list of available linked steps in your workflow. 

Action Wizard


Else Action Wizard

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. In this if-else statement, notice the use of the special frevvo function "frevvo.step.on()". The value in parenthesis is the step's GUID. 

Rule Code
var event = form.load;
if (frevvo.step.on('_b8Le0KLjEeumC5vM65nqTg')) {
  Review.visible = true;
} else {
  Review.visible = false;
}

It can be a little tricky to find the step's GUID if you are writing a rule in JavaScript from scratch. One option is the start the rule in the VRB (to get the GUID) and then switch to Rule Code to finish it. Another is the use built-in data and a variable so you can specify the step name rather than GUID.

Rule Code
var event = form.load;
var an = _data.getParameter("flow.activity.name");
if (an === "Review") {
  Review.visible = true;
} else {
  Review.visible = false;
}

Show/Hide Submit and Cancel Buttons

You may want to hide the submit and/or cancel buttons on your form under certain conditions. For example, consider a form with multiple tabs. The main tab contains the form and the other tabs contain reference information users may need when completing the form. You only want the submit and cancel buttons visible when the user is in the main tab. This rule hides the submit and cancel buttons when the reference tabs are selected. The control name of the main tab is MainTab. Be sure to select the Main Tab in the designer and then save the form. This ensures the Main Tab will be selected when the form loads and the rule will run.

There are two ways to write this rule.

  1. You can write the rule using an if...else conditional statement.

    Rule Code
    if (!(MainTab.selected)) {
        Submit.visible = false;
        Cancel.visible = false;
    } else {
       Submit.visible = true;
       Cancel.visible = true;
    }
  2. Or, you can use a shortened syntax as in this example.

    Rule Code - Shortened Syntax
     Submit.visible = Cancel.visible = (MainTab.selected);

You cannot hide the Continue and Finish buttons on workflows.

Show/Hide Approval Sections on Workflow Steps

A very common workflow pattern is an approval workflow that routes a single linked form to one or more approvers. In this case, you most likely want to show each approver's section only on their step. 

When you create a workflow with approval steps using the Workflow Design Wizard or Guided Designer, approval sections and their associated rules are automatically created for you.

You are designing an Expense Report workflow that has a total of 3 steps. Steps 2 (Manager Approval step) and step 3 (Accounts Payable step) are Linked Steps.

  • Step 1 is filled in by the employee. When it is completed, the workflow is routed to the employee's manager for Approval/Rejection
  • Step 2 is performed by the Manager. If the Manager approves, the workflow is routed to the Accounts Payable group for final processing.
  • Step 3 is performed by the first person in the Accounts Payable group to click the perform icon on their Task List for this workflow.

Step 1 has a Section named Manager and a section named Accounts Payable. The Visible property on both these sections is unchecked so they are hidden by default.  You want to hide the Manager and Accounts Payable sections for the first step, show the Manager Approval section when the second step is performed by a manager and show the Manager Approval and Accounting sections when the third step is performed by an Accounting department employee.

These two rules will show the Manager Approval section on both the Manager and Accounts Payable step. Note the use of the When current step is on or after Manager condition.

These two rules show the section named Accounts Payable only if the workflow is on the Accounts Payable step. It should not be visible when the workflow is on the steps performed by the employee or the manager.

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. In this if-else statement, notice the use of the special frevvo function "frevvo.step.on()". The value in parenthesis is the step's GUID. 

Rule Code
var event = form.load;
if (frevvo.step.on('_4jCkoKeQEeumO8tqYAdRDQ')) {
  Manager.expanded = true;
  Manager.required = true;
  Manager.enabled = true;
} else {
  Manager.expanded = false;
  Manager.required = false;
  Manager.enabled = false;
}

It can be a little tricky to find the step's GUID if you are writing a rule in JavaScript from scratch. One option is the start the rule in the VRB (to get the GUID) and then switch to Rule Code to finish it. Another is the use built-in data and a variable so you can specify the step name rather than GUID.

Rule Code
var event = form.load;
var an = _data.getParameter("flow.activity.name");
if (an === "Manager") {
  Manager.expanded = true;
  Manager.required = true;
  Manager.enabled = true;
} else {
  Manager.expanded = false;
  Manager.required = false;
  Manager.enabled = false;
}

Switch Form View in a Workflow

Consider a workflow with several different forms the user will perform as a screenflow. The Navigation Toolbar provides one method for the user to navigate back to a prior form, but in some cases, you may want even greater flexibility in allowing the user to view a different form. In this case, we recommend using a single form where each individual form's fields are contained in Section controls. Then, you'll add a dropdown control and a business rule like this example to show/hide the sections on specific steps, and also allow the user to show any other section (form) on demand. This example is a Real Estate Listing workflow, where each section ("form") collects different information about the listing.

Switch Between Form Views
var activity = _data.getParameter("flow.activity.name");
var aborSelected = false;
var defectsExist;
var i, marketingMaterialsRequested;
var nonMlsAustin, nonMlsAustinTypeTemp, nonMlsSanAntonio;
var nonMlsSanAntonioTypeTemp, sellerInfo, signatureCtrlsRequired, yardSign;
var saborSelected = false;
var seller2;

// Amenities
AmenitiesSection.visible = (ViewPrevious.value === "Amenities") ? true : false;

// Final Review
FinalReviewSection.visible = ViewPrevious.value === "Final Review" ? true : false;

// Heating & Cooling
HeatingCoolingSection.visible = (ViewPrevious.value === "Heating & Cooling") ? true : false;
CentralACType.visible = (CentralAC.value === "Y") ? true : false;
CentralACUnitCount.visible = (CentralAC.value === "Y") ? true : false;
if (CentralAC.value != "Y") {
  CentralACType.value = [];
  CentralACUnitCount.value = null;
}
HeatingDescription.visible = (HeatingType.value === "Other Heat") ? true : false;
if (HeatingType.value != "Other Heat") {
  HeatingDescription.value = "";
  HeatingUnitCount.value = null;
}
HeatingUnitCount.visible = (HeatingType.value != "Other Heat") ? true : false;

// Listing Info
ListingDetailsSection.visible = (ViewPrevious.value === "Listing Details") ? true : false;

// Marketing
MarketingSection.visible = (ViewPrevious.value === "Marketing") ? true : false;

// Upload Documents
UploadDocumentsSection.visible = (ViewPrevious.value === "Upload Documents") ? true : false;

PropertyFeaturesSection.visible = (ViewPrevious.value === "Property Features") ? true : false;

// Property Info
PropertyInfoSection.visible = (ViewPrevious.value === "Property Info") ? true : false;
PropertyUse.visible = (PropertyType.value === 'Single Family Home') ? true : false;
SellersCurrentMailingAddress.visible = (PropertyUse.value !== 'Owner Occupied') ? true : false;
if (activity != "Seller Info") {
  Address.required = true;
  City.required = true;
  State.required = true;
  ZipCode.required = true;
}

// Rooms
RoomsSection.visible = (ViewPrevious.value === "Room Dimensions") ? true : false;

// Seller Info
sellerInfo = (ViewPrevious.value === "Seller Info") ? true : false;
SellerInfoSection.visible = sellerInfo;

// Showing Instructions
ShowingInstructionsSection.visible = (ViewPrevious.value === "Showing Instructions") ? true : false;

Control Property Rules

Business rules can be used to dynamically set many control properties. See the Accessing Control Properties chapter for a list of properties that can be set via rules. Here are few common examples.

Printable

Business rules often are used to control what is printed to the final form PDF. This ensures only the data you want is printed, and can help keep the PDF snapshot as simple and easy to read as possible. This form has a radio control named DescribeInDetail and a section control named Details. The Details section is hidden by default and made visible if the user selects 'Yes'.

When the form is submitted we've configured frevvo to send an email with the form PDF. We only want the Details section to appear on the PDF when the user selects 'Yes'. In design mode, uncheck the printable property on the section control. This property will apply to the section and all controls inside the section, so you do not have to uncheck printable on the inner controls. Then create this business rule with the Visual Rule Builder. When the section is visible we also set it to be printable. When the section is hidden we also set it to be not printable.

How it works

 Click to learn how it works...

Click Rule Code to view the Javascript set by the Visual Rule Builder. This is a straightforward if-else statement utilizing the frevvo .printable property.

Rule Code
var event = form.load;
if (DescribeInDetail.value === 'Yes') {
  Details.visible = true;
  Details.printable = true;
} else {
  Details.visible = false;
  Details.printable = false;
}

Enable/Disable Submit and Continue Buttons

A great feature of frevvo is the fact that a form cannot be submitted or a workflow cannot be continued to the next step until all required fields are filled and all filled fields have valid data (e.g. a date field must contain a valid date). Sometimes it might not be as obvious to the form/workflow user why the form/workflow will not submit or continue to the next step. frevvo provides a built-in feature to show validation errors at the time of submission. Refer to the Invalid Form Notification for Users  topic to see how it works.

Enable/Disable a Control

There are many times you may want to conditionally enable or disable controls in your form at run time. Let's say you have a form with two controls named Attending and Presenter respectively. Attending is a checkbox with a single option, Yes. If checked, you wish to ask the addition question, Presenter. 

Use the Rule Builder to create this rule. This rule will automatically fire whenever the user checks or unchecks Attending and will enable/disable Presenter. In this example, you would typically set the checkbox Attending to be initially unchecked and the radio control Presenter to be initially disabled.

Condition Wizard

Be sure to slide the toggle icon to enter the literal value Yes on the Condition wizard.


Rule List


How it works

 Click to learn how it works...

Click Rule Code to view the Javascript set by the Visual Rule Builder. Notice the special built-in method "frevvo.multiValueEquals" which takes two parameters (array, value). Since checkbox values are arrays, this looks at the array to see if it contains the specified value. Even though this particular checkbox only has one option, the checkox control type's value will always be an array. See this documentation and the examples below for more info on writing rules for selection controls.

Rule Code
var event = form.load;
if (frevvo.multiValueEquals(Attending.value, 'Yes')) {
  Presenter.enabled = true;
} else {
  Presenter.enabled = false;
}

Set Valid/Invalid Property

If a control has a pattern or a built-in type (like email, phone or date), it will automatically show as valid when the correct value format is entered, and invalid if an incorrect value is entered. However, you can also control validity dynamically at run time using a business rule. 

This example shows a control named NetWorth, and a rule that checks the value of NetWorth. If NetWorth is less than 0, the rule displays an error message and sets NetWorth to invalid, which also prevents the form from being submitted. When a rule sets the invalid property for a <control>, the background is highlighted with a color (configurable), the error message displays, and the form will not submit just as if the user had entered an invalid value into a phone control. This is a good way to dynamically control your form's valid state. Refer to the Invalid Form Notification for Users for the frevvo built-in method to prevent the submission of forms/workflows with invalid data.

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the Javascript set by the Visual Rule Builder. This is a straightforward statement that accesses the NetWorth control's "valid" property, which takes a boolean true/false value.

Rule Code
var event = form.load;
if (NetWorth.value < 0) {
  NetWorth.valid = false;
} else {
  NetWorth.valid = true;
}

Set Error Message

Oftentimes when you are setting a control's valid property by a business rule, you also want to customize the error message. This can be done in the Visual Rule Builder using the "error message to" function or in JavaScript by accessing the control's status property.

Action Wizard

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. Notice that the error message is set by the controls 'status' property.

Rule Code
var event = form.load;
if (NetWorth.value < 0) {
  NetWorth.valid = false;
  NetWorth.status = 'Liabilities exceed Assets. Please correct.';
} else {
  NetWorth.valid = true;
}

Set One or the Other Control to Required

Consider a health insurance policy request form in which Employee election or the Family election is required, but not both. Additionally, if one is selected, you want to clear any value that might be in the other (e.g. if the user selected Employee, then changed their mind and selects Family, you must clear the Employee selection.)

In this example we are using two radio controls in the form; one for “Employee Only” and the other for “Family”. The options available for both controls are Accept and Waive. The user must select either one of the radio control to proceed.

You can initially set both controls to required, then use business rules to set one control to optional when the other is filled, and vice versa. To use the Visual Rule Builder, you would need two rules. In JavaScript rule code, you could combine them if desired.

Rule 1: Set Employee-Only to Optional & Empty if Family is selected. If the user selects family, this rule sets the employee-only radio control to optional and empty. The else action sets the employee-only radio control to required.

Condition Wizard

Action Wizard

Else Action Wizard

Rules List

Rule 2, "Set Family to Optional & Empty if Employee is selected", is similar and is shown in the Rules List below. If the use selects employee-only, this rule sets the family radio control to optional and empty. The else action sets the family radio control to required.

At runtime, both controls are required at first. As soon as one control is filled the other control is set to optional and vice versa. Notice that if you change your selection, the opposite control value is cleared.

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. These are both straightforward if-else statements utilizing the frevvo .required property. Notice that we can clear the value of the opposite control by setting the .value property to null.

Rule 1 Code
var event = form.load;
if (Boolean(Family.value)) {
  EmployeeOnly.required = false;
  EmployeeOnly.value = null;
} else {
  EmployeeOnly.required = true;
}
Rule 2 Code
var event = form.load;
if (Boolean(EmployeeOnly.value)) {
  Family.required = false;
  Family.value = null;
} else {
  Family.required = true;
}

Required Field Status in Accessible Forms

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

You can build forms/workflows in frevvo that meet Section 508 and WCAG 2.0 accessibility standards. Accessible forms/workflows can assist users with visual and motor impairments. When the Accessible property is enabled for a form/workflow, the error, "You can't leave this empty <control name>"  displays if users move ahead from a required field without filling it. The status property for the empty control becomes invalid and sets this special error message. Normally, the status property can be used in a business rule to set (or retrieve) the error message displayed whenever the control's value is invalid. 

For example, let's say a form has a text control named 't', and a message control named "m". If you write a rule to update 'm' with the status of the required/invalid control 't', as shown below, the message control will show the Error Message only when an invalid value is entered. It returns a blank value if the required control was left empty.

Rule Code
if(!t.valid)
  {
    m.value = t.status;
  }


Similarly, this rule will not display the "You can't leave this empty" message for a required control in a form with Accessibility enabled, because that message is not treated as the control's status. However, the following rule will fill the message control with the literal text specified here (which matches the Accessibiliy message) when the required control is left empty, and the Error Message text if it's filled with an invalid value.

Rule Code
if (!t.valid) {
  if (t.value.length === 0) {
    m.value = "You can't leave this empty.";
  } else {
    m.value = t.status;
  }
}

Dynamic Label, Help or Hint

You can set the value of control labels, help and hint dynamically in a rule. For example imagine you have a form that either volunteers or staff might complete. You'll ask which role they have in the Radio control "Role." Then, dynamically set the label, hint and help messages of the "Name" control to customize it for their role. Create this rule using the Rule Builder. Even though the label of the Name control will look different to the user, the entered data is all present in the same control in the submission.


Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. In this example, the values set are literal values in quotes. Notice that the apostrophes are comments out with a backslash (\) character. This is not strictly necessary in this type of rule since labels, hints and help text do not get saved in the submission document or passed through integrations, but it is a good practice in general if setting control values.

Rule Code
var event = form.load;
if (Role.value === 'Volunteer') {
  Name.label = 'Volunteer Name';
  Name.help = 'Enter Volunteer\'s Last Name, First Name';
  Name.hint = 'This field is for the Volunteer\'s Full Name';
} else {
  Name.label = 'Staff Name';
  Name.help = 'Enter Staff\'s Last Name, First Name';
  Name.hint = 'This field is for the Staff\'s Full Name';
}

Expand/Collapse Section

Another useful control property to access dynamically is the Section control's expand/collapse feature. Collapsing sections that are not actively in use reduces clutter in your form and makes it easier for the user to navigate, while still allowing them to expand and see info in the section if they need it.

This example form has three sections. The first section is expanded and the 2nd and 3rd are collapsed. When the user fills in the 1st section they click a "Next" trigger control which causes that section to collapse and the next section to expand. The trigger controls are named next1 and next2. And the sections are named: step1, step2, step3. Use the Rule Builder to create these rules. You will have to create 2 separate rules - one for Step 1 and one for Step2.

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. This is straightforward if statement that accesses the trigger control's special property 'clicked' and the section control's 'expanded' property which takes a boolean value of 'true' or 'false'.

Rule Code
var event = form.load;
if (Next1.clicked) {
  Step2.expanded = true;
  Step1.expanded = false;
}

Tab Selection

One unique thing about Tab controls is that you can dynamically design which tab is "selected", or shown, to the user. These rules explain how to use this feature.

Select Tab

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

This rule makes a specific tab the selected tab based on the choice of a radio control. The radio is named SelectTab and has three options: Person, Auto, Home. The tabs are named personTab, autoTab and homeTab. Tabs also can be selected based on trigger controls or other input controls using the same method show here. 

Rule Code
if (SelectTab.value.length > 0)
{ 
    autoTab.selected = false; 
    homeTab.selected = false; 
    personTab.selected = false;

    if (SelectTab.value === 'Auto') {
        autoTab.selected = true; 
    } else if (SelectTab.value === 'Home') {
        homeTab.selected = true;
    } else {
        personTab.selected = true;
    }
}

Next Tab

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

This form contains a trigger control at the bottom of each tab labeled "Next". When "Next" is clicked the trigger rule executes and makes the next tab the selected tab. This assists the user in navigating through the form. The Tabs are named T1, T2, T3, T4. The trigger controls are named C1, C2, C3

Rule Code
// Navigate Tabs 
if (C1.clicked) { 
    T2.selected = true; 
} else if (C2.clicked) {
    T3.selected = true; 
} else if (C3.clicked) { 
    T4.selected = true; 
} 

Pattern

In most cases, the Pattern property can be set directly in the form designer's control properties panel. However, consider a case where you have an existing workflow with pending tasks that contains a control with no pattern set on the first step. The data in the in-flight flows may or may not be consistent with the pattern. If you edit the production workflow to set a pattern on the control, that control could become invalid on in-flight workflow instances that are on later steps and can't be edited. This will prevent those in-flight tasks from being submitted. In this case, you can follow the Best Practices for Updating Workflows in Production and use a business rule to set the pattern on this control only when the workflow is on the first step. This rule will enforce the pattern on all new workflow interviews but will not break in-flight workflows.

You might also use this method when a control's pattern has an either/or scenario. In this example, the control should be valid if four numerical digits are entered, or if the word "NONE" is entered in any letter case, e.g. "none" or  "None".


var event = form.load;
var uc = SSN.value;
var res = uc.toUpperCase(); //change text to uppercase to simplify value comparison

if (frevvo.step.on('_72odfp6qEeuqr9Bz_J-qDw')){ //if the current step is Step 1

  if (res === 'NONE'){
    SSN.valid = true;
  } else if (res !== 'NONE'){
    var pattern = /^[0-9]{4}$/g;
    SSN.valid=uc.match(pattern)?true:false;
  }
}

How it works

The Pattern property is not directly accessible by business rules. However, you can set the desired pattern in a variable and use the JavaScript match method with the conditional operator '?' to compare the control's value to the desired pattern. Set the control's valid property to the result of the match condition, which is true if the variables match and false if they do not match. Be sure to set a meaningful Error Message, which will appear whenever the valid property is false and prompt the user to enter a valid value.

Formatting Rules

There are several cases where you may want to format the data entered into your form in a particular way, such as money or number formatting. You may also wish to set specific patterns on fields dynamically using a business rule based on run-time conditions. The examples in this section will help you customize your data's format.

Format Money Values to Display in a Message Control

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Let's say you have Number control in your form and you want to display it in a Message control formatted as a money value with commas and decimal places. Add a Number control named Num and a message control named Message to your form. Add a rule and edit the rule code, pasting this rule into the code box. This rule will display the number entered in the number control with commas. If the user enters 5600.44 in the number field then the result in the message control would show "$5,600.44".

This rule uses variables to take the value of Num, convert it to a string, split it at the decimal, and add a comma every third digit from the right of the decimal. It then sets the message value to concatenate the dollar sign, the number with commas added, and the decimal.

Rule Code
	var x, x1, x2;
if (Num.value > 0) {
    var nStr = Num.value.toFixed(2);
    nStr += '';
    x = nStr.split('.');
    x1 = x[0];
    x2 = x.length > 1 ? '.' + x[1] : '';
    var rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1)) {
 x1 = x1.replace(rgx, '$1' + ',' + '$2');
    }
    Message.value = '$' + x1 + x2;
}

Format Money in Text Controls

This rule is not yet supported in the Visual Rule Builder and thus still requires some JavaScript.

You may want to format money with currency signs, commas, decimals and rounded to two decimal places. This example uses a text control for input and a business rule to format the money value.


This rule declares variables and defines the functions numberWithCommas and formatMoney. numberWithCommas converts the number to a string and adds commas every third digit from the right. formatMoney uses the JavaScript Math object to round the number to two decimal places. The last five lines of this rule take the Amount value, strips special characers the user might have entered, and runs these two functions on them. Then it sets Amount value to concatenate the dollar sign and the newly formatted money value.

Rule Code
var x, x1, x2;

var numberWithCommas = function(x) {
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
var formatMoney = function(amt) {
  var decimalAmount = Math.round(amt * 100) / 100;
  return numberWithCommas(decimalAmount.toFixed(2));

};
if (Amount.value.length > 0) {
    var m = Amount.value.replace(/\$/g,"").replace(/,/g, "");
    var formattedMoney = formatMoney(m);
    Amount.value = '$' + formattedMoney;  
}


Here is another example showing a text control named "Money" in a table. Another text control named "Total" displays the column total.

Note the addition of the for loop which runs this rule on each row of the table.

Rule Code
var x, x1, x2;

var tot = 0;
var numberWithCommas = function(x) {
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
var formatMoney = function(amt) {
  var decimalAmount = Math.round(amt * 100) / 100;
  return numberWithCommas(decimalAmount.toFixed(2));

};

if (Money.value.length > 0) {
  for (var i = 0; i < Money.value.length; i++) {
    var m = Money[i].value.replace(/\$/g,"").replace(/,/g, "");
    var formattedMoney = formatMoney(m);
    Money[i].value = '$' + formattedMoney;
    tot = tot + Number(m);
  }
  var formattedTotal = formatMoney(tot);
  Total.value = '$' + formattedTotal;
}

European Number Format

This rule is not yet supported in the Visual Rule Builder and thus still requires some JavaScript.

You may need to display numbers in a locale-specific format. While current versions of frevvo do not support javascript locale methods on number controls, you can write a business rule to handle displaying numbers in your desired format. This example displays a number with a period as the thousands separator and a comma as the decimal separator, i.e. 10.000,00. You can modify this rule slightly for other formats, such as using a space as the thousands separator. This example has a text control in the form named EuropeanFormat.


Rule Code
//use a decimal as thousand separator
var numberWithDecimals = function(x) {
  return x.replace(/\B(?=(\d{3})+(?!\d))/g, ".");
};

//format with two decimals
var formatNumber = function(amt) {
  var decimalAmount = Math.round(amt * 100) / 100; // this adds a decimal with two zeros after the number.
  return numberWithDecimals(decimalAmount.toFixed(2));
};

if (Boolean(EuropeanFormat.value) && Boolean(EuropeanFormat.value)) {
  var e =  parseInt(EuropeanFormat.value.replace(/,/g, ''), 10); 
  var f = formatNumber(e); //format the value using above functions
  var intIndex = f.lastIndexOf(".");
  f = f.slice(0, intIndex) + f.slice(intIndex).replace(".", ",");//change the last decimal to a comma
  EuropeanFormat.value = f;
}

This rule is similar to the format money rule above, in that it adds a decimal with two zeroes behind the number entered, so 12345 displays as 12.345,00. If you'd rather convert the number to a decimal as entered, simply remove the '* 100' from the Math.round() method parameters. This change will display 12345 as 123,45.


TextArea Max Length

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

The TextArea control does not have a maxlength property like the text control does, because in html there is no way to set a maxLength on a TextArea control. However, it is possible to do this via a business rule. This example form has a TextArea control named 'Desc' where the user can enter up to a 500 character description. On this control we also set the ErrorMsg property to the string 'You must limit your description to 500 characters'. This message is automatically displayed when the description control is set to invalid by the following business rule.

Rule Code
if (Desc.value.length > 500) {
  Desc.valid = false; 
} else {
  Desc.valid = true; 
} 

You can even customize the error message by adding this line to your rule. Now the error message will tell the user how many characters they are over the maximum allowed.

Desc.status = 'Invalid. Max 500 chars allowed and you have ' + Desc.value.length; 

Convert TextArea newline to HTML break

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Users may enter multi-line text into TextArea controls. If you want to display that text in an html context, for example on a web page, in an html formatted email, or in your form's Form Action display message you will need to replace newlines with html breaks. Line breaks entered into a web form TextArea are represented by a single newline character \n while line breaks in an html context are represented by the html break characters.

Our example has a TextArea control named Description and a hidden text control named DF. The user types into the visible control named Description and a business rules converts the newline characters (\n) into html breaks (<br/>), and sets the new value in the hidden control 'DF'. Note that this particular rule also replace the carriage return (\r) with a blank. You will then use the template {DF} in your message control, form action, email, etc.

var x = Description.value; 
x = x.replace(/\\r/g,""); // replaces "Carriage Return" (CR, ASCII character 13) with a blank
x = x.replace(/\\n/g,"<br/>"); // replaces "Line Feed" (LF, ASCII character 10) with an html line break
DF.value = x; 

TextArea Wrap Long URL in PDF Snapshot

This rule is not yet supported in the Visual Rule Builder so still requires some Javascript.

If users enter a long URL into a TextArea control, it appears wrapped in use mode but does not wrap in the PDF snapshot. Here is a rule to ensure text wraps in the PDF snapshot if needed.

Rule Code
var str;
if (form.unload) {
str = Source.value; //Source is the name of Text Area where user enters the URL.

var result = '';
while (str.length > 0) {
result += str.substring(0, 20) + '\n'; //The 2nd number '20' is the number of characters per line. Adjust this based on the width of your TextArea control.
str = str.substring(20); //See note above
}

Source.value = result;
}

Split Phone Number

There may be cases where you collect data from the user in one format, such as a phone number, but you need to use it in another format, such as in two different fields on a mapped PDF. You can use standard JavaScript methods to accomplish this. Here is an example that uses split() and also strips the separators (- or .) from the resulting values. You can use a similar method to split emails or other text as well.

Rule Code
var p = PhoneNumber.value;

if (PhoneNumber.value.includes('-')){
  var split = p.split("-");

  AreaCode.value = split[0];
  Number.value = split[1] + split[2];
}else if (PhoneNumber.value.includes('.')){
  var split = p.split(".");
    
  AreaCode.value = split[0];
  Number.value = split[1] + split[2];

} else {
  AreaCode.value = p.substring(0, 3);
  Number.value = p.substring(3,10);
}

The result places the split values in two separate controls at run time. These controls would typically be hidden in use mode and mapped to the PDF.

Prefilling Rules

Initializing a form with data is a popular way to streamline data entry for your users, declutter forms (prefilled controls can be hidden) and reduce potential errors. Many of these rules optimize built-in methods to retrieve data stored in frevvo, such as user info, tenant info, and form info.

Tenants, Roles, and Users

frevvo has several built-in methods that enable you to access information about your current tenant. Some common examples are:

  • Initialize a form with the logged in user data (first name, email address etc).
  • Populate fields with user data for another user
  • Populate a dropdown with a list of users and roles.
  • Display a list of users with a specific role
  • Display a list of users with multiple selected roles

Use the Rule Builder to Initialize a form with user data - the Rule Builder has built-in functions for user information. Use the Dynamic Options feature to populate a ComboBox with a list of users and roles - no JavaScript required.

Displaying lists of roles for selected users and populating Dropdowns with lists of users/roles still require JavaScript using frevvo built-in methods. Some of these methods return a Boolean true/false value. Others return a JSON string that is automatically converted to a JavaScript object.

This section contains several examples to get you started.

Initialize Form with Current User Info

Prefilling form controls with the logged-in user's info can save your user time and prevent data entry errors. Use the Rule Builder functions userID(), user FirstName(), userLastname(), userFullName(), userEmail(), and userManagerID() to create this rule. When the form loads, fields are populated with the logged-in user information.

Condition Wizard

You don't strictly need a condition for this rule in a form. However, if you are using a workflow, you will want to set a condition to run the rule on the particular step you want to gather user info, or set a condition like "When userID is empty" so that the rule does not run again on subsequent steps that may have different users.

Action Wizard

Rule List


How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. Notice the use of the frevvo built-in methods for the "subject", or current logged-in user.

Rule Code
var event = form.load;
UserID.value = _data.getParameter('subject.id');
FirstName.value = _data.getParameter('subject.first.name');
LastName.value = _data.getParameter('subject.last.name');
FullName.value = _data.getParameter('subject.first.name') + ' ' + _data.getParameter('subject.last.name');
EMail.value = _data.getParameter('subject.email');
ManagerID.value = _data.getParameter('subject.reports.to');

Populate Form with Any User's Info

Perhaps you want to prefill form fields not with the logged-in user's info, but with another user's info. This might be used in a case where you get the logged-in user's manager's userID, and then you want to look up the manager's email using their userID. In this example, a user id is entered into a text control in the form named UserLookup. When the user enters a frevvo user id into the UserLookup field, the uFirstName, uLastName, uFullName, uEmail and uManagerID are populated with the information about that user.


Condition Wizard

Action Wizard

The user functions in the VRB take the parameter userId. In this case, the userId is in the control 'UserLookup', so you enter that control name in each function's parentheses.

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. This examples uses the variable user_UserLookup to get the user details using the built-in metho userDetails for the user ID entered in the UserLookup control. Then, each control value is set to the specific userDetail property, i.e. firstName, lastName, etc. The VRB uses the Boolean check in the if condition, but this is not strictly necessary.

Rule Code
/*members firstName, lastName, reportsTo, email */
var user_UserLookup;
var event = form.load;
if (Boolean(UserLookup.value)) {
  user_UserLookup = frevvo.userDetails(UserLookup.value);
  uFirstName.value = user_UserLookup.firstName;
  uLastName.value = user_UserLookup.lastName;
  uFullName.value = user_UserLookup.firstName + ' ' + user_UserLookup.lastName;
  uEmail.value = user_UserLookup.email;
  uManagerID.value = user_UserLookup.reportsTo;
}

The userDetail function can also be used to retrieve individual information fields for the currently logged in user. These fields can either be one of the standard fields or a custom attribute configured using the LDAP Security Manager.
The standard properties are 'id', 'first.name', 'last.name', 'email' and 'reports.to'.

Populate Dropdown with Users/Roles

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Populating the options of a ComboBox control with lists of users/roles can be done without writing JavaScript rules. See Dynamic Options feature to populate a ComboBox for details.

Perhaps you want users to be able to select a user or role from a list of all the users or roles in your tenant. This might be used to set up dynamic workflow step assignments or to get user info for a specific user. This example populates the following controls when the form loads:

  • text controls with the name of the current tenant and the logged in user
  • a dropdown named AllUsers with a list of all users in the current tenant
  • a dropdown named AllRoles with a list of all roles in the current tenant

Leave the default options for the dropdown controls in the designer.

Rule Code
if(form.load){
currentTenant.value = frevvo.currentTenant();
currentUser.value = frevvo.currentUser();
AllUsers.options = frevvo.userIds();
AllRoles.options = frevvo.roles();
}

Populate Dropdown with Roles Assigned to a User

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Similar to the example above, you can access of list of all the roles assigned to a particular user. In this example we use that list to populate a dropdown control, so the user can select a role at runtime.

In the code below, we have set the rule to get the roles for the user 'designer'. You could replace "designer" (in line 3) with the <controlname>.value where a userID is entered or populated to make this rule dynamic.

Rule Code
var event = form.load;
var options=[];
var ud = frevvo.userDetails("designer"); 
var roles = ud.roles;
for (var i=0; i < roles.length; i++) {
  options[i] = roles[i] + '=' + roles[i];
}
Roles.options = options;

List Users in a Role

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

A common requirement is to filter the list of users in a tenant to display only users that have a specified role. The frevvo.userIds() and frevvo.roles() functions can be used in a rule to accomplish this.

One approach is to populate one dropdown control with all the roles in your tenant, allow the user to choose a role and then display the results in a dropdown. Another approach is to use an invisible text field where you hardcode the role name. Remember that frevvo role names are case-sensitive.

Let's say you want to display a list of users that have the Manager role in a dropdown named Role. The user sees only the users that are assigned the Manager role instead of the entire list of users in the tenant. Once a user is selected from the filtered list you can display the details about that user.

Here is an example of a rule that filters the list of users to only those that are assigned the Manager role and populates the options of a dropdown control with the results. The frevvo.userDetails(String userId) function is used to retrieve the details about the selected user (Jim). The results are displayed in a formatted Message Control named m. The form has a text field named Role with an initial value of "Manager".  This field is referenced in the rule to supply the role name.

Rule Code
/*member email, firstName, id, lastName, reportsTo*/

if(form.load){
UsersWithManagerRole.options = frevvo.userIds(Role.value); //Populate dropdown with users who have the role entered in the 'Role' control.
}

//Display the user details for the selected user
var ud = frevvo.userDetails(UsersWithManagerRole.value); 

m2.value = "<b>Selected User Details :</b><br/>"+
 "User ID : "+ud.id+"<br/>"+
 "User FirstName : "+ud.firstName+"<br/>"+
 "User LastName : "+ud.lastName+"<br/>"+
 "User FullName : "+ud.name+"<br/>"+
 "User Email : "+ud.email+"<br/>"+
 "User ReportsTo : "+ud.reportsTo+"<br/>";

List Users in Multiple Roles

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

You can also select more than one role from your tenant list and use a business rule to display a filtered list of users who are assigned any of the selected roles. This business rule populates the options for a checkbox named Roles with the list of all users in the tenant. Selecting more than one role from the list populates a dropdown named UsersWithSelectedRoles with the filtered list of users assigned those roles. Details for the selected user are displayed in a formatted Message Control named m.

Rule Code
/*member email, firstName, id, lastName, reportsTo*/
if(form.load){
Roles.options = frevvo.roles(); //Populate Checkbox options with all roles in the tenant 
}

UsersWithSelectedRoles.options = frevvo.userIds(Roles.value); //Populate dropdown with users who have any of the roles selected in the Roles checkbox

//Display the user details for the selected user
var ud = frevvo.userDetails(UsersWithSelectedRoles.value);
m.value = "<b>Selected User Details :</b><br/>"+
 "User ID : "+ud.id+"<br/>"+
 "User FirstName : "+ud.firstName+"<br/>"+
 "User LastName : "+ud.lastName+"<br/>"+
 "User FullName : "+ud.name+"<br/>"+
 "User Email : "+ud.email+"<br/>"+
 "User ReportsTo : "+ud.reportsTo+"<br/>";

List a User's Roles

It's also possible to list all of the roles assigned to a specified user. This rule gets the user's roles using the same built-in methods we used above to initialize the form with the logged-in user's info. If you were to set them in a text or textArea control, you could simply have added a line to that rule which returns the list of roles.

RolesText.value = _data.getParameter ("subject.roles");

To populate the roles in a dropdown, use a variable ('roles' in this example) to hold the string of roles and evaluate it to an array, which can then be used as the dropdown options.

Rule Code
var x;
if (form.load) { 
	// Get the User's Roles and set them as dropdown options
    var roles = _data.getParameter ("subject.roles"); 
    if (roles) { 
        eval ('x=' + roles); 
        Roles.options = x; 
    } 
}

Verify User

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

This rule executes when the user enters a value into the username text field named 'U'. It uses the built-in isUniqueUserId() method that returns false if the user already exists. If the user already exists this rule then sets the value of a message control, makes that message control visible on the form and sets the Username valid property to false so that Username field displays as invalid to guide the user to make a correction. Notice that the isUniqueUserId() method takes two parameters, userId and tenant name. In this example, we have set the tenant name in a control 'tenant'. You could also hard code this or set it dynamically using the frevvo.currentTenant(); method.

See the section on dynamic content for more details.

Rule Code
if (U.value.length > 0) {  
    if (frevvo.isUniqueUserId(U.value, tenant.value) === false) { 
        M.value = 'User: ' + U.value + ' already exists'; 
        M.visible = true; 
        U.valid = false; 
    } else { 
        M.visible = false; 
    } 
} 

Verify Role

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

This rule executes if there is a value in a field named role. It uses the built-in isUniqueRoleId() method that returns false if the role already exists. If the role already exists, this rule displays the message "The role <role entered> already exists in the tenant <tenant name>." to notify the user. Notice that the isUniqueRoleId() method takes two parameters, userId and tenant name. In this example, we have set the tenant name in a control 'tenant'. You could also hard code this or set it dynamically using the frevvo.currentTenant(); method.

Rule Code
// Verify that a role already exists in the tenant
if(role.value.length > 0)
{
  t.value = frevvo.isUniqueRoleId(role.value, tenant.value);
  if (frevvo.isUniqueRoleId(role.value, tenant.value) === false) {
    ErrMsg.value = 'The role ' + role.value + ' already exists in tenant ' + tenant.value; 
  }
}

Unique and/or Sequential ID

frevvo has a built-in Sequence Number generator! Create a sequence number with your preferred initiating number. Then, use the Visual Rule Builder to increment it and populate a control on your form. We recommend this simple and built-in method for creating unique and/or sequential IDs. See the Sequence Numbers documentation for steps and rule examples.

The methods described below will continue to work as alternatives.


This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Forms such as invoices, bills of lading, or purchase orders often need to be stamped with a unique ID. There are several different approaches you could take for this business requirement. 

  1. Use the frevvo Database Connector (or another Web Service) to create a sequential unique ID for each submission. See this example in our Database Connector documentation.
  2. Use the frevvo Google Connector the create sequential numbers. This approach is described in the Google Connector documentation on this page, however it has some limitations. One is that you must guarantee that only one person at a time is filling out your form. This is because there is no mutex around the read and update of the Google spreadsheet cell. 
  3. If the unique ID does not need to be sequential, you can use the form id generated by frevvo. The _data 'form.id' is guaranteed to be unique for every new form instance. Capture the form id using _data.getParameter as follows:

    Rule Code: Unique ID using form UUID
    if (form.load) 
    { 
        InvoiceNum.value = _data.getParameter('form.id'); 
    } 
  4. An another approach is create a number from a timestamp using the JavaScript Date method as follows. new Date() creates a date object for the exact millisecond the rule runs. Then valueOf() returns the primitive value of that object, and toString() converts it to a string. If it's unlikely you will have multiple forms initialized at the same second, this will very likely provide unique numbers.

    Rule Code: Unique ID using JS Date()
    // some VERY likely unique number
    var event = form.load;
    if (!(PONumber.value)) {
    var n = new Date().valueOf().toString();
    PONumber.value = n;
    }

Signatures

frevvo offers built-in, legally binding signatures for authenticated users. However, there may be times when you want to create your own signature stamp using an external source, pin code, or other such method to authenticate. You may also want to work with the Signature control from palette (anonymous signature) to give it a similar behavior in UI such as setting a date stamp after signing. The following examples demonstrate rules for such scenarios.

Signature via Web Service

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. 

This form uses a rule to pass a username and password to an LDAP Active Directory authentication service. If authentication fails the form makes an error message control visible. If authentication succeeds the form disables the username form field and replaces the password field with a date field set to the current date. The form contains a trigger control named sign, username and password fields named u and p respectively, a date field named d and a message field named m.

Rule Code
/*member auth */
var x;
 
if (sign.clicked) { 
    // passwords may contain characters that need url encoding 
    var p_encode = encodeURIComponent(p.value);

    eval('x=' + http.get('http://(your webhost)/authServices/signForm?username=' + 
                          u.value + '&password=' + p_encode));

    if (x.auth) { 
        var dt = new Date(); 
        var day = dt.getDate(); 
        var month = dt.getMonth() + 1; 
        var year = dt.getFullYear(); 
        d.value = month + '-' + day + '-' + year;

        d.visible = true; 
        u.enabled = false; 
        p.visible = false; 
        sign.visible = false; 
        m.visible = false; 
    } else { 
    m.visible = true; 
    } 
}

The authService is an example HTTP servlet that returns a JSON response.

public void doGet (HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException {
 
    try { 
        String username = request.getParameter(REQUEST_SIG_USERNAME); 
        String password = request.getParameter(REQUEST_SIG_PASSWORD); 
        password = URLDecoder.decode(password, "UTF-8"); 
        // Authenticate Signature 
        authenticateUser(response, username, password);  
    } catch (Exception e) { 
        throw new ServletException(e); 
    } 
}

private void authenticateUser(HttpServletResponse response, String u, String p) { 
    if (realm.authenticate(u, p) === null) 
        response.addHeader(RESPONSE_JSON_HEADER, "{auth:false}"); 
    else 
        response.addHeader(RESPONSE_JSON_HEADER, "{auth:true}"); 
} 

Here's another example form that requires a doctor's signature. This shows how the form works in use mode. The first image contains a dropdown to select one of the doctor's authorized to sign the form and a text control where they enter their PIN code.

This image shows the case where the doctor entered an invalid PIN and the error message becomes visible.

This image shows the case of a valid PIN. Today's date is entered into the date control via the rule and made visible and disabled from edit. The username dropdown is disabled and the PIN and Sign controls are hidden.

Set Signed Date

This example sets a date control to today's date as soon as the user signs the form. The Signature control is named EmployeeSignature and the date control is named EmployeeSignDate.

Use the today() function in the Rule Builder to create this rule.

Condition Wizard

Action Wizard

Else Action Wizard

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. Notice the use of the built-in method frevvo.currentDate(), which returns the current date in the user's local time zone.

Rule Code
var event = form.load;
if (Boolean(EmployeeSignature.value)) {
  EmployeeSignDate.value = frevvo.currentDate();
} else {
  EmployeeSignDate.value = null;
}

Enable Click to Sign for Anonymous User

Perhaps you would like your anonymous user to be able to "click to sign" that automatically enters a text signature (similar to the way the authenticated-user signed section works). You can do this using a checkbox control and two business rules.

First, add the Click Here to Sign checkbox control. Then, add a business rule that enables that checkbox only if all other sections are valid (meaning, required controls have been filled in):

Rule Code
if (Section1.valid === true && Section2.valid === true){
  ClickHereToSign.enabled = true;
  ClickHereToSign.required = true;
} else {
  ClickHereToSign.enabled = false;
  ClickHereToSign.required = false;
}

Finally, use the Visual Rule Builder to concatenate the First Name and Last Name the user entered at run-time.

Rule List

At run time the result looks like this:

Doc Action, Activity Doc Action, and Precondition Related Rules

Conditionally Send Email

Let's say you have a form or workflow step that should send an email only under certain conditions. The solution is to dynamically fill a control with either the recipient's email address (when the condition is true) or a no-reply type email address (when the condition is false.) In the example below, the Account Receivable form will only send an email to the Client Services Manager if the payment is related to a services project. 

  1. To dynamically send an email, add a hidden email control (SendtoCSEmail) to your form.
  2. In the Email Doc Action (or Email step for a mid-workflow Activity Doc Action), enter the control name as a template in the "To" field.



  3. Use the Visual Rule Builder to fill the hidden email control with the valid email if the condition is true, and a no-reply email if the condition is false. The Else Action is important here - if you set it to Empty or to an invalid string (any string not using the syntax <name>@<name>.<string>), you will get a submission error.


    You can also set the hidden email to a template, as shown below, or to the userEmail() function, so that it is evaluated at run time.

  4. When the user clicks Submit or Continue, the email will send to either the valid email (condition=true) or the noreply email (condition=false.)

Construct Form URL

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. A rule can dynamically construct the URL to itself. This can be handy when you want to add a link to your form's URL to an email message or to the form action display message. In this example FormURL is the name of a text control in your form.

Rule Code
if(form.load){
  var baseUrl = _data.getParameter('_frevvo_base_url') + 
      "frevvo/web/tn/" +
      _data.getParameter('tn.id') +
      "/user/"+_data.getParameter('user.id') +
      "/app/"+_data.getParameter('app.id') +
      "/formtype/"+_data.getParameter('form.type.id') + "?_method=post&embed=true"; 
  FormURL.value = baseUrl;  
}

For workflows, replace Line 8 above with "/flowtype/"+_data.getParameter('flow.type.id') + "?_method=post&embed=true";

Now FormURL can be used as a template in your display message as shown below.

Thank You!<br/><br/> 
Your account will be created within 24 hours.<br/> 
Click <a href="{FormURL}">here</a> to request another demo account.

Rules by Control Type

This section will cover example rules for various control types. Some control types have their own properties and methods, which are helpful to understand when using the Visual Rule Builder or Rule Code Editor to customize their behavior. 

Selection Controls | Date and Time Controls | Rule Examples#Repeats | Rule Examples#Tables | Upload Control | Message Control

Selection Controls

Dropdowns, Checkboxes, Radio, ComboBox and T/F controls are selection controls. You can populate your selection control options, either statically or dynamically, using business rules. You may want such customized picklists for:

  • List of products for a Sales Order / PO
  • Number of available vacation days for a Leave Approval
  • List of projects for a Time Sheet

and many more....

You may want to populate selection control options by pulling from a web service or database. You can do this without writing an JavaScript code with the frevvo Dynamic Options feature.

Options

Set Options

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Populating options works the same way for all selection controls, by accessing the control's ".options" property. The options must be set in an array, where each value in the array represents an option. The option value is the value passed in the submission .xml document, while the label is what the form user will see. When your option value and label are the same, each array value can simply be the option value. frevvo will automatically use that item as the option label as well. When the option value and label are different, each array item uses the syntax '<option value>=<option label>'.

The simplest method to set the options array is to use an array literal. 

Set Options when Option Value and Label are the SAME
if (form.load) { 
    var cc = ['Red', 'Blue', 'Green'];
    Colors.options = cc;
}
Set Options when Option Value and Label are DIFFERENT
if (form.load) { 
    var cc = ['R=Red', 'B=Blue', 'G=Green'];
    Colors.options = cc;
}

In several examples below you will see other methods to dynamically set the options array.

Set Options to Control Values

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

You may wish to customize your selection control options to the values entered in other form controls at runtime. Unlike other control properties, options do not support templatized strings, so you will have to write a rule to set the options. Let's say you have a form that asks the user to enter three items, and then select their top choice from those three items. You have three text controls (Item1, Item2, and Item3) and a radio control (TopChoice). Write your rule as follows:

Rule Code
var opt = ['I choose ' + Item1.value,'I choose ' + Item2.value, 'I choose ' + Item3.value];

TopChoice.options = opt;

The run time result will look like this:

Default Selection

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. 

When your options are set dynamically in a business rule, you cannot set a default selection in the form designer. You need to set the default in the rule. If your options have <value>=<label> where value is different from label, make sure you set the <control>.value to '<option value>', not '<label>' nor '<option value>=<option label>'.

Set Options and Default Value
if (form.load) { 
    var cc = ['R=Red', 'B=Blue', 'G=Green'];
    Colors.options = cc;
	Colors.value = 'R';
}

Display Selected Option Label

This rule is not yet available in the Visual Rule Builder, and will require some JavaScript.

One common use for the Dropdown (or Radio) control is to allow the person completing the form to select who will perform the next step from a list of users. In this case, the designer may set up the Dropdown options with the userId as the option value (to ensure a valid user assignment when this field is used as a template in the Assignment tab), but a friendly name, such as the user's first and last name, as the option label. However, since the form user does not know that the value does not equal the label, when they search their tasks or submissions for 'First Name Last Name', they see no results because they should be searching for userId.

You can use this business rule to display the Dropdown's option label in a hidden field, and then set that hidden field as the Searchable Field for users to search task list or submissions by. Here's an example of a dropdown where the options value and label are different. Notice that we have a hidden control beside it to collect the selected option's label:

Create a new rule, click Rule Code, and then Edit Rule Code. Add this code (replacing the control names with your control names.)

Rule Code
var x;
var selectedcolors = '';
 
    var v = AsssignStep2To.value;
    for (x in AsssignStep2To.options) {
 
        var opt = AsssignStep2To.options[x];
        var val= opt.split('=')[0];
        var lab= opt.split('=')[1];
        if (v === val) {
            selectedcolors = selectedcolors + ' ' + lab;
        }
    }
SelectedOptionLabel.value = selectedcolors;

The result is that when an option is selected from the dropdown, we pass the option value in the dropdown control, and the option label in the hidden control. The Selected Option Label control is visible here in use mode for demo purposes:

In the Workflow Settings Searchable Fields tab, select the hidden field (in this example, "Selected Option Label") instead of the dropdown control itself. This allows your users to search by the option label, set in the hidden field, rather than the Dropdown value, which they do not know.

Clear Selections

This sample resets a dropdown option to the automatically added blank option. For dropdowns added from palette controls and from schema, frevvo automatically adds a blank option so the dropdown initially shows no choice by default. To reset the dropdown, set the dropdown control's value to null, not to the empty string. The empty string will not work since the empty string is not a valid option. This form resets the dropdown named size whenever the value of the product option changes. This also works for Radio and Checkbox controls, even though they do not have a built in blank option.

Randomize Options

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

You may want the options of a selection control to appear at random each time a user opens the form, such as in a quiz. Add your selection control and options.

Then, add this rule:

Rule Code
var points = select.options;

if (form.load) {
  points.sort(function(a, b) {
    return 0.5 - Math.random();
  });
}

select.options = points;

Each time you load the form, you will see the options in a different, random order.

The JavaScript method sort() also allows you to show options in alphabetical order, which can be helpful when getting Dynamic Options from database, Google Sheet, or other web service. 

Dynamic Options

The Dynamic Options rules discussed in this section are not yet supported in the Visual Rules Builder and thus still require some JavaScript.

Selection controls' (radios, checkboxes, dropdowns, T/F) options can be set dynamically from a webservice (such as the frevvo Database Connector or Google Connector) via rules. This keeps your forms in sync with your database and ensure the accuracy of your submission data.

frevvo provides a built-in no-code Dynamic Options feature in the form/workflow designer. See this documentation and short video to see how it works. 

Selection Controls from Schema

If the selection control you are populating comes from an XSD schema data source rather than one of the standard palette controls, take care to set the options to values that are valid for that schema element. For example, imagine that your XSD has a string enumeration and lists valid options as 'red', 'green', and 'blue'. If you were to use a rule to dynamically set the options to 'small', 'medium', 'large', your form will not work correctly in use mode. If a user selects the option 'small' they will get a validation error on the form because 'small' is not one of the options allowed by your underlying XSD schema.

Options from a Database

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

This example uses the frevvo Database Connector to set a list products as dropdown options. The option value is the Product Code, which is a rather cryptic number that would not be easy for the user to choose. So, we have set the Product ID as the option value but the user-friendly Product Name as the visible option label.

Rule Code
/*member productName productCode resultSet */
var x, opts;
 
for (var i=0; i < x.resultSet.length; i++) { 
    if (x.resultSet[i]) { 
            opts[i] = x.resultSet[i].productCode+ '=' + x.resultSet[i].productName; 
    } 
} 
Products.options = opts; 

Combobox Options from a Webservice

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

In many cases, you can simply use the Dynamic Options feature to pull ComboBox options from a web service in the Form Designer without writing business rules. However, if you need more complex dynamically determined endpoint URLs that cannot be built using control templates within the URL or if you have multiple ComboBoxes making HTTP calls to a web service and notice performance issues, you may choose to use business rules to populate options instead. See ComboBox Control Properties for Business Rules

Limiting Results to Improve Combobox Performance

A Comobobox that pulls options from a webservice might return a large number of options which can slow performance. One way to improve the performance of a Comobobox that pulls options from a web service is to configure your query to limit the results returned. Here is an example of a SELECT statement that limits the results to 50 rows. 

SELECT NAME FROM SUPPLIER WHERE UPPER(NAME) LIKE UPPER('%{NAME}%') AND ROWNUM <= 50 ORDER BY NAME
Example 1 - Set optionsUrl in a business rule.

You must still set the ComboBox OptionsSrc property to Webservice and set the Bind Path in the Properties panel. This is a very simple example of setting the optionsUrl property in a business rule. Notice the use of the built-in JavaScript method encodeURIComponent, which encodes the ComboBox value entered at run time in case it may have spaces or special characters.

Rule Code
ComboBox.optionsUrl = 'http://localhost:8082/database/combobox/productsByName?pname=' + encodeURIComponent(ComboBox.matchText);
Example 2 - Populate options directly from webservice.
/*member productCode, productName, resultSet*/

if (ComboBox.matchText) {
var x;

eval('x=' + http.get('http://localhost:8082/database/combobox/productsByName?pname=' + encodeURIComponent(ComboBox.matchText)));
var opts = [];
var j = 0;

for (var i=0; i < x.resultSet.length; i++) {
if (x.resultSet[i].productName.includes(ComboBox.value)) {
opts[j] = x.resultSet[i].productName;
j++;
}
}
ComboBox.matchingOptions = opts;
}
Example 3 - Populate Options from JSON

This method is useful when you have multiple ComboBoxes and making multiple calls to the same webservice impacts performance. This simple example gets product data from the frevvo Database Connector and stores it in a hidden textArea in the form. Then a second rule parses the data and sets the ComboBox options. Please see the chapter on Reusing Dynamic Content for more details.

The "Single Value" property must be checked in order for this to work properly.


Rule 1 Store Results in Hidden Control
if (form.load) {
  JSONData.value = http.get('http://localhost:8082/database/combobox/allproductsByName');
}
Rule 2 Populate Options
/*member productName, resultSet*/
if (ComboBox.matchText) {
  var opts = [];
  var j = 0;
  var x = JSON.parse(JSONData.value);
  for (var i = 0; i < x.resultSet.length; i++) {
      if (x.resultSet[i] && x.resultSet[i].productName.toLowerCase().includes(ComboBox.value.toLowerCase(), 0)) {
        opts[j] = x.resultSet[i].productName;
        j++;
      }
    }
  ComboBox.matchingOptions = opts;
}

Note that this example removes case sensitivity using the toLowerCase() function on both sides of the comparison. 

You may use the includes() function or the startsWith() function. includes() will return true if the string contains the specified string (in any position), where startsWith returns true if a string starts with a specified string. includes() will tend to produce more results; the method you choose will depend on your data and how your users tend to use typeahead.

Example 4 - Populate ComboBox in a Table

Similare to Example 3, this reuses content stored in a hidden text control in your form. However, it provides additional code to loop through the table when setting options.

//Populate ComboBox options using results saved in the TextArea
/*member DESCRIPTION, resultSet*/
for(var k=0;k<Combobox.value.length; k++){
  if (Combobox[k].value.length > 0 && Combobox[k].matchText) {
    var x = JSON.parse(jsonUserData.value); 
    var opts = [];
    var j = 0;
    for (var i = 0; i < x.resultSet.length; i++) {
      if (x.resultSet[i] && x.resultSet[i].DESCRIPTION.includes(Combobox[k].value)) {
        opts[j] = x.resultSet[i].DESCRIPTION;
        j++;
      }
    }
    Combobox[k].matchingOptions = opts; 
  }
}

Options from a Google Sheet

Dropdown control options can be dynamically populated from a Google Sheet using a business rule. Refer to Create a Dynamic Pick List from a Google Sheet for the relevant details.

Synchronized Dropdowns

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

The method shown here is an older way of providing a user with friendly options while also passing a more cyrptic value, such as a unique id used in database tables, to the submissions document. In many cases you will find the example above, Populate Options from a Database, which dynamically sets <option value>=<option label> to be a more straightforward solution.

This example automatically sets the option selected in one dropdown based on the option selected in another. This is often useful when you have a form with choices that were dynamically populated. For example, imagine product choices that are descriptive text. When the user selects a product, your form needs to perform an action based on a product ID rather than the descriptive product text. A nice way to do this is to have the rule that dynamically populates the product choices dropdown also populate a hidden product ID dropdown. The product choices dropdown control was named Products and the product ID dropdown control was named PID.

This scenario requires two rules. The first rule, Load Products, populates both the visible and hidden dropdowns with options from a database. Notice it also sets the default selection to the first option for both dropdowns. 

Load Products Rule
/*member productCode, productName, resultSet*/
var x;
  
if (form.load) {
  eval('x=' + http.get('https://app.frevvo.com/database/BIRT/allProducts'));  
 
    var opts1 = [];
    var opts2 = [];
 
    for (var i=0; i < x.resultSet.length; i++) {
        if (x.resultSet[i]) {
            opts1[i] = x.resultSet[i].productName;
            opts2[i] = x.resultSet[i].productCode;
        }
    }
 
  Products.options = opts1;
  PID.options = opts2;
  Products.value = opts1[0]; // default to 1st product option
  PID.value = opts2[0];
}

The second rule is covered below in Find a Selected Options Index.

Here is another rule that dynamically populates both the product choices and product ID dropdowns. This rule calls a REST Service which returns an object rather than the resultset returned by the database connector as shown above. See the section on dynamic content for more details.

/*member ids products */
var x;
 
if (S.value.length > 0) {
    eval('x=' + http.get('http://localhost:8182/products/?category=' + S.value)); 
    P.options = x.products; 
    ID.options = x.ids; 
} 

Find a Selected Options Index

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

This rule can be used with the example above that populates two dropdown controls from a database query, and it is a good example in general of how to identify a selected option's index. The rule Select Product ID keeps the hidden PID dropdown synchronized with the visible Products dropdown.

Find Index and Synchronize Selection
var i, x; 
for (x in Products.options) { 
// Determine the index of the selected product in the Products dropdown options 
if (Products.value === Products.options[x]) 
    i = Products.options.indexOf(Products.options[x]); 
}

// Changed the selected PID to match the selected Product 
PID.value = PID.options[i] + '';

Filter Dynamic Options

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

You may want to filter the dynamic options before setting the value of your dropdown control. For example, perhaps you want to populate options from a database while excluding any option with the value of 0. Use a rule like this. 

Rule Code: Filter Dynamic Options
if (tr.clicked) {
  var opts = [];
  var j = 0;
  for (var i=0; i<dd.options.length; i++) {
    var opt = dd.options[i];
    // filter out the one with a 0 for value
    if (opt.split("=")[0] != "0") {
      opts[j] = dd.options[i];
      j++;
    }

  }  
  // put the filtered options back
  dd.options = opts;
}

Triggers & Dynamic Options

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

The Trigger control can be a helpful tool when working with http calls and dynamic options. It allows the user to perform the call on demand, and perhaps multiple times if needed. One example is to create a Search form where a user can type a keyword, click the trigger button, and then see a dynamic picklist result.

This rule is executed when the user clicks the trigger controls with Name ''search''. It then dynamically sets options on a dropdown list control with Name coffeeShopList.

Rule Code
if (search.clicked) 
{ 
    coffeeShopList.options = ['Koffee', 'Starbucks', 'Willoughbys', 'Dunkin Donuts'];
} 

Now replace the hard coded list of coffee shops with a rule that invokes an http.get. This must return an X-JSON header which contains a JSON object. The object is evaluated and assigned to the variable x. In this case the JSON object contains an options field of type array. See the section on dynamic content for more details.

Rule Code
var x;
if (search.clicked) 
{ 
    eval('x=' + http.get('http://(your webhost)/getCoffeeShopList'));  
    coffeeShopList.options = x.options; 
} 

Triggers do not work in repeating items (Repeats, Tables).

Search a JSON String

This rule searches a JSON string (populated from a database, google sheet, or other webservice) and populates a radio control with matching options. The form has a hidden TextArea control, used to save and reuse the JSON string. It also has a text control, trigger, radio control and error message.

  1. Write a rule to fetch the data from your system and store it in the TextArea, as described here.
  2. Write the following rule to search the stored JSON string and populate the radio control. This rule uses the conditions of a value change in the custName text control OR a trigger click; you only need to choose one of those conditions.

    Rule Code
    /*member customers, id*/
    
    var string;
    var customerlist = JSON.parse(CustomerListJson.value.trim());
    
    if (custName.value.length > 0 || onClick.clicked){
      var matchingcustomers = [];
      var matchtext = custName.value.toLowerCase();
      for (var i=0; i<customerlist.customers.length; i++){
        if (customerlist.customers[i].name.toLowerCase().indexOf(matchtext) >= 0){
          matchingcustomers.push(customerlist.customers[i].id + '=' + customerlist.customers[i].name);
        }
      }
      if (matchingcustomers.length > 0){
        SelectCustomer.visible = true;
        SelectCustomer.printable = true;
        SelectCustomer.options  = matchingcustomers;
        noMatches.visible = false;
      } else {
        noMatches.visible = true;
        SelectCustomer.visible = false;
        SelectCustomer.printable = false;
      }
    } else {
       noMatches.visible = false;
       SelectCustomer.visible = false;
       SelectCustomer.printable = false;
    }

The result is a handy search form:

Cascading Dropdowns

This rule dynamically sets the options in a dropdown list based on the value selected in another dropdown, creating an effect of "cascading" dropdowns. This form contains three fields named Products, Series and Model. The series options are set dynamically based on the product selection, and the model options are set based on the series selection. When a new product is selected we enable the series dropdown and both clear and disable the model dropdown. For this example, we have hard coded the option arrays; however, you could replace those with dynamic options (i.e. options from a database) using the prior selection in the query URL. See the Database Connector Tutorial for an example.

Here is a simple example to show you the rule syntax. 

Rule Code (simple)
if (product.value === 'Laserjet Printers') 
{ 
    series.options = ['Laserjet4 series', 'Laserjet5 series', 'Laserjet6 series']; 
    series.enabled = true; 
    model.options = []; 
    model.enabled = false; 
} 
 Click to expand full rule code...

Here's a more complete example of this rule (with hard coded options.)

if (product.value === 'Laser_Printers') { 
  series.options = ['Laserjet4 series', 'Laserjet5 series', 'Laserjet6 series']; 
  series.enabled = true; 
  if (series.value === 'Laserjet4 series'){
    model.options = ['Laser4-123','Laser4-456']; 
    model.enabled = true; 
  } else if (series.value === 'Laserjet5 series'){
    model.options = ['Laser5-abc','Laser5-def']; 
    model.enabled = true; 
  } else if (series.value === 'Laserjet6 series'){
    model.options = ['Laser6-basic','Laser6-premium','Laser6-ultimate']; 
    model.enabled = true; 
  } else {
    model.options = [];
    model.enabled = false;
  }
}  else if (product.value === 'Inkjet_Printers'){ 
  series.options = ['Inkjet22 series', 'Inkjet33 series', 'Inkjet44 series']; 
  series.enabled = true; 
  if (series.value === 'Inkjet22 series'){
    model.options = ['Inkjet-22 Master','Inkjet-22 Buddy']; 
    model.enabled = true; 
  } else if (series.value === 'Inkjet33 series'){
    model.options = ['Inkjet33-abc','Inkjet33-def']; 
    model.enabled = true; 
  } else if (series.value === 'Inkjet44 series'){
    model.options = ['Inkjet44-basic','Inkjet44-premium','Inkjet44-ultimate']; 
    model.enabled = true; 
  } else {
    model.options = [];
    model.enabled = false;
  }
} else if (product.value === 'AllInOne_Printers'){
  series.options = ['WidgetWorld', 'Epsonian']; 
  series.enabled = true; 
  if (series.value === 'WidgetWorld'){
    model.options = ['W2134','W8675']; 
    model.enabled = true; 
  } else if (series.value === 'Epsonian'){
    model.options = ['E-all-in-one','E-plus']; 
    model.enabled = true; 
  } else {
    model.options = [];
    model.enabled = false;
  }
} else {
  series.options = [];
  series.enabled = false;
  model.options = [];
  model.enabled = false;
}


Dynamic Control Initialization with JSON

This rule initializes multiple control values based on the selection of a dropdown control. It handles this case better than using a long if/else construct by using a JSON string. First add options to the dropdown named SalesRep in the format <value>=<label> where <value> will be used as an index key into a JSON array of details about each person.

Megan=Megan Smith 
Jim=Jim Brown 
Nancy=Nancy Jones 
Brian=Brian Jones 

Then write a rule that first sets up a javascript JSON syntax array with the contact information for each person. The rules then uses the dropdown value to index into the contactInfo array to set the details for the selected person into four other form controls.

Rule Code
/*member '' Brian Jim Megan Nancy cell email phone */
 
var contactInfo = { 
    "" : {  
        name : "", 
        email : "", 
        phone : "", 
        cell : "" 
    }, 
    "Megan" : {  
         name : "Megan Smith", 
         email : MSmith@mycompany.com, 
         phone : "(203) 694-2439 Ext. 516", 
         cell : "(203) 337-3242" 
    }, 
    "Jim" : {  
        name : "Jim Brown", 
        email : jim@comcast.net, 
        phone : "203-208-2999", 
        cell : "" 
    }, 
    "Nancy" : {  
        name : "Nancy Jones", 
        email : nancy@snet.net, 
        phone : "203-208-2991",  
        cell : "" 
    }, 
    "Brian" : {  
        name : "Brian Jones", 
        email : BJones@mycompany.com, 
        phone : "203-748-6502", 
        cell : "" 
    } 
};
 
var repId = SalesRep.value;
SalesRepName.value = contactInfo[repId].name; 
SalesRepEmail.value = contactInfo[repId].email; 
SalesRepPhone.value = contactInfo[repId].phone; 
SalesRepCell.value = contactInfo[repId].cell;

Try this simple Clinic Location form which uses this approach to initialize its controls.

Comment Property Rules

Dropdowns, Checkboxes and Radio controls can display a Comment field if the user selects the last option. It is most often used as an "Other - please specify" option. In the Forms designer, check the Comment checkbox in the Properties panel and provide a # of rows for the comment area. If the user selects this option, a comment box will appear below asking the user to provide details. Selecting or unselecting the comment property is not supported in rules; however, business rules can be used to set or retrieve the value in the comment field.

Set Comment Field

The Visual Rule Builder can be used to set the comment field to a value or expression. 

Action Wizard

Rule List

Retrieve Comment Value

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Business rules can be written to retrieve the value of the Comment field using the commentValue property. Note that the initial value of the commentValue is null. Your rules may have to be coded to check for that. This simple rule copies the value in the comment field of a dropdown control named Supervisor to a text field named OtherSupervisor. The rule will run only if the value in the comment field is not null.

Rule Code
if(Supervisor.commentValue != null){
  OtherSupervisor.value = Supervisor.commentValue;
} else {
  OtherSupervisor.value = "";
}

Review this special syntax to reference comment fields in templates.

Set Max Length for Comment Field

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

You may need to set a max character limit on the comment box of a selection control. Use this rule example for a control named "Reason."

if(Reason.commentValue){
  if(Reason.commentValue.length > 50 && Reason.value){
    Reason.valid = false;
    Reason.status = 'Invalid. Max 50 chars allowed and you have ' + Reason.commentValue.length;
  } else {
    Reason.valid = true;
  }
}

Checkbox Control

Checkbox controls are different from other selection controls in that they are multi-select. The value of the Checkbox control is passed as an array, so rules that set or reference this value are handled with for loops (similar to Repeat and Table control rules.) However, unlike Repeats and Tables, the Visual Rule Builder will not automatically handle this for you, so you may need to use some JavaScript.

Checkbox Initialization

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Since checkbox options are multi-select, in order to select multiple options via a rule you must use this syntax. The correct way to initialize the checkbox control value is to collect all required options in an array and then assign that array as the value of your checkbox control.  In this example CB is the name of a checkbox controls with the following options: red, green, blue. This rule selects all of the options.

CB.value = ['red', 'green', 'blue']; 

To clear all checked options in the control named CB:

CB.value = []; 

Display Selected Checkbox Options

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Let's say you want to list the value of your Checkbox control (the options the user has selected) in another control. This rule has a checkbox control named ColorPalette with the options: purple, green, blue, yellow, orange. The form also contains a text control with name ColorChoice. This rule assigns the choices selected from ColorPalette to ColorChoices. Note that this rule displays the option value, not the option label.

This rule uses a for loop to collect the selected option values in a variable, 'choices'. Then it sets the text control's value to that variable outside of the loop.

Rule Code
var choices = ''; 
for (var i = 0; i < ColorPalette.value.length; i++) 
{  
    choices = choices + ColorPalette.value[i] + " "; 
} 
ColorChoice.value = choices; 

Display Selected Checkbox Labels

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

In this example, the rule displays the labels of the checkboxes the user selects. Imagine the Color Palette checkbox had the options p=purple, g=green, b=blue, y=yellow, o=orange. The above rule would display the values instead of the labels:

In this case the option value and option label are different, and we prefer to display the option label. The rule below loops throught the selected options, and for each selection it finds the corresponding options array item, splits it at the equal sign, and adds the label to the 'choices' variable.

Rule Code
var x;
var choices = '';

for (var i = 0; i < ColorPalette.value.length; i++) 
{ 
    var v = ColorPalette[i].value; 
    for (x in ColorPalette.options) {

        var opt = ColorPalette.options[x]; 
        var val= opt.split('=')[0]; 
        var lab= opt.split('=')[1];
        if (v === val) { 
            choices = choices + ' ' + lab; 
        } 
    } 
}
ColorChoice.value = choices;

The result is that the selected option labels are displayed in the text control.

Checkbox isFilled Condition

This section only applies to rules created prior to v11.1. In v11.1 the isFilled condition will utilize the "empty" property instead of the methods described below.

The Visual Rule Builder provides an easy way to dynamically change the behavior of controls when another control has any value. When the condition refers to a Checkbox control, you must use both the control's value and the value's length (number of items in the value array). The VRB handles this for you.

Using the same form as the example above, this rule changes the Label of the Color Choice control to "Thank You for selecting colors" if options are checked in the Color Palette control. If there are no options checked then the Label of the Color Choice text control is "Please select colors."

Condition Wizard

Action Wizard

Else Action Wizard

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. In the if statement, notice the boolean checks for both the .value property and the .value.length property. Length checks that there are more than zero items in the value array.

Rule Code
var event = form.load;
if (Boolean(ColorPalette.value && ColorPalette.value.length)) {
  ColorChoice.label = 'Thank you for selecting colors:';
} else {
  ColorChoice.label = 'Please select colors.';
}

Selected Checkbox Options in Conditions

Another way to use Checkbox in your rule condition is to look for a specific selected option. The Visual Rule Builder offers both the "is" and "contains" functions for this purpose. It will look through the Checkbox value array of selected options, and return "true" if the value is found. (The "is not" and "does not contain" looks for the value and returns "false" if it is found.) You must list the entire, exact option value when using these condition functions on a checkbox.

Here is an example of a form that contains a checkbox named Structures. If the user selects the option "Detached Garage" or "House", we want to display a text field named StructureDetails.

When the options of a from-palette checkbox contain spaces, the option array converts each space character to the '_' character. Checkbox controls from schema do not have space replaced with '_'.




Condition Wizard

Edit the logic expression to "or" instead of "and."

Action Wizard

Else Action Wizard

Notice the addition of the "Set StructureDetails to empty" action, which clears the value if the control is unchecked. This resets the control if a value change is made to the Structures checkbox. For example, the user may have selected one of the Structures checkboxes that made Details visible and entered a value into Details. If the user then changes their mind and unchecks the option that caused Details to become visible, you don't want the value entered into Details to be in your form submission.

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. Note the use of the special frevvo.multiValueEquals method which takes two parameters: the control value and the string to look for. 

Rule Code
var event = form.load;
if ((frevvo.multiValueEquals(Structures.value, 'Detached_Garage')) || (frevvo.multiValueEquals(Structures.value, 'House'))) {
  Details.visible = true;
} else {
  Details.visible = false;
  Details.value = null;
}

Many Checkbox Comments

This rule, as written, is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. You can achieve the same result in the Rule Builder but you must create a six separate rules to show/hide the details section for each option in your selection control. Expand the section after the example for details about how to create this rule in the Rule Builder.

This rule makes an associated comment input control visible and required when a checkbox is checked. The for loop determines which checkboxes are checked and sets an appropriately named variable to true. Depending on the value of the checkbox the associated input control will be made visible and required via the if/else and will be hidden and not-required when it is unchecked again. This is a very common rule design pattern.

This form uses the frevvo Compact layout to align the comment input controls with the checkbox options. Download a working sample that uses JavaScript rules here.

Rule Code
var heartProblem = false;
var foodAllergy = false;
var rashes = false;
var jointInjury = false;
var asthma = false;
var moodiness = false;

for (var i = 0; i < MedicalIssues.value.length; i++)
{
  if (MedicalIssues[i].value === 'heart_problem') {
    heartProblem = true;
  } else if (MedicalIssues[i].value === 'food_allergy') {
    foodAllergy = true;
  } else if (MedicalIssues[i].value === 'rashes') {
    rashes = true;
  } else if (MedicalIssues[i].value === 'joint_injury') {
    jointInjury = true;
  } else if (MedicalIssues[i].value === 'asthma') {
    asthma = true;
  } else if (MedicalIssues[i].value === 'moodiness') {
    moodiness = true;
  }
}

if (heartProblem === true) {
  heartProblemDetails.visible = true;
  heartProblemDetails.required = true;
} else {
  heartProblemDetails.visible = false;
  heartProblemDetails.required = false;
  //heartProblemDetails.value = null;
}

if (foodAllergy === true) {
  foodAllergyDetails.visible = true;
  foodAllergyDetails.required = true;
} else {
  foodAllergyDetails.visible = false;
  foodAllergyDetails.required = false;
  //foodAllergyDetails.value = null;
}

if (rashes === true) {
  rashesDetails.visible = true;
  rashesDetails.required = true;
} else {
  rashesDetails.visible = false;
  rashesDetails.required = false;
  //rashesDetails.value = null;
}

if (jointInjury === true) {
  jointInjuryDetails.visible = true;
  jointInjuryDetails.required = true;
} else {
  jointInjuryDetails.visible = false;
  jointInjuryDetails.required = false;
  //jointInjuryDetails.value = null;
}

if (asthma === true) {
  asthmaDetails.visible = true;
  asthmaDetails.required = true;
} else {
  asthmaDetails.visible = false;
  asthmaDetails.required = false;
  //asthmaDetails.value = null;
}

if (moodiness === true) {
  moodinessDetails.visible = true;
  moodinessDetails.required = true;
} else {
  moodinessDetails.visible = false;
  moodinessDetails.required = false;
  //moodinessDetails.value = null;
}
 Click here to see how to create this rule in the Rule Builder
Rule List

Repeat this rule pattern for each possible checkbox value.

Set Options for a Checkbox to Values of Another Checkbox

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. 

This rule will take values selected in one checkbox control and populates those as options in a second checkbox control. For example, a checkbox control (Services) displays all available services. The second checkbox (Selected Services) will display as options the values selected in Services. One scenario you might use this is to customize an employee scheduling form. In Step 1 of the workflow the Coordinator picks the offered services from the full Program Service list. In Step 2 the Employee sees only the smaller list to select from.

Rule Code
var opts = [''];

for (var i = 0; i < ProgramService.value.length; i++ ){
  var v = ProgramService.value[i].replace(/_/g,"  ");
  opts[i] = ProgramService[i].value + "=" + v;
}
SelectedPS.options = opts;  
var event = form.load;

Count the Options Selected

There are use cases where you may want a total of how many checkbox options were selected. For example, one or more checkboxes may display a list(s) of products, and you want to know how many total products were selected. A simple rule will count the number of options selected where the checkboxes are named "Accessories," "Shoes," and "Jewelry" and the count is given in a Quantity control named "Quantity":

Rule Code
Quantity.value = Accessories.value.length + Shoes.value.length + Jewelry.value.length;

Repeating Checkboxes

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Checkboxes inside repeat controls must be treated as an array (each checkbox control's values) of checkbox option values which is inside another array (the repeat control itself). This form example has a repeating section containing two controls -- Message which is a text control and AreYouAttending which is a checkbox control with a single option 'yes'. To access the selected options the syntax is AreYouAttending[i].value[0] === 'yes'.

Rule Code
for (var i = 0; i < AreYouAttending.value.length; i++) 
{ 
    if (AreYouAttending[i].value[0] === 'yes') {
        Message[i].value = Name.value + 
               ' is attending event #' + i; 
    } 
} 

T/F Boolean Control

T/F controls are simplified checkbox controls with only a single visible option.

In frevvo versions pre-v10.2, the value property for a Boolean checkbox behaved differently depending on whether it was from schema or from palette. If the T/F control was from schema, you will see control.value === true in the rule when the Boolean is checked. If the T/F control was from the palette, you will see control.value[0] === 'true' in the rule when the Boolean is checked. Note the array syntax used in the rule when the T/F control comes from the Palette. Designer should be aware of this difference when manually writing JavaScript.

For Checkboxes (from schema and from palette) created in v10.2+, the syntax does not require the index. If you use the Rule Builder, this difference will be seamless to the designer. Depending on the frevvo version your T/F control was built in, you may see an index after the control name in the condition, e.g. TaxExemptOrg.value[0]. See Selection Controls for more details. 

T/F Value as Condition

You can use the value of the T/F control as a condition to change the behavior of other controls. Consider a form in which you ask if the organization is tax exempt, and if they are, provide an upload control for the user to attach the relevant documents. Otherwise, leave the upload control hidden. There is a T/F control named TaxExemptOrg and and Upload control named TaxExemptionDocument.


Condition Wizard

Action Wizard

Else Action Wizard

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. Depending on the frevvo version your T/F control was built in, you may see an index after the control name in the condition, e.g. TaxExemptOrg.value[0]. The Visual Rule Builder will handle this. See Selection Controls for more details. 

Rule Code
var event = form.load;
if (TaxExemptOrg.value === 'true') {
  TaxExemptionDocument.visible = true;
} else {
  TaxExemptionDocument.visible = false;
}

Set a T/F Value

You can also set the T/F Value in the Visual Rule Builder. We'll show two examples here - one that sets the value to true, and another that clears the value on a trigger click. Imagine a form with a policy agreement section. Your rule will check "I Agree" for the user (in this example, there is no condition so it sets the value on form.load.) However, you also want the user to have the option to click a button and clear that checkmark if they don't agree.

Action Wizard

Switch the toggle to "literal value" and notice that the VRB gives you a dropdown with the only two possible T/F values, true and false.

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. This is a very simple rule that sets the control's value. Remember that the value will always be true, even if you have customized the label.

Rule Code
var event = form.load;
IAgree.value = 'true';

Clear a T/F Value

Now we'll show a rule to clear the value from the T/F control.

Condition Wizard

Action Wizard

There are two possible actions to clear a T/F control: the function 'is empty' which sets the value to null, and the function set 'to' the literal value 'false'. With either action, the result for the user is the control appears unchecked. However, if you have the control's "Required (false)" property checked, there will be a difference in the submission .xml document. Setting the control to null will not show a value in the submission .xml document, but setting the control to false will show the value 'false' in the submission .xml. When the "Required (false) property is unchecked, neither 'to empty' nor 'to false' will show a value in the submission .xml.

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. This is a very simple rule that sets the control's value.

Rule Code
var event = form.load;
if (Clear.clicked) {
  IAgree.value = 'false';
}

However, if you have chosen the function 'to empty' in the VRB, notice that the VRB adds an extra line to the rule code that first sets the T/F's required property to false, and then sets the value to null. This is because when the T/F control's required property is true, the unchecked state is always the 'false' value. See this documentation for more explanation of the Required (false) property.

Rule Code
var event = form.load;
if (Clear.clicked) {
  IAgree.required = false;
  IAgree.value = null;
}


Date and Time Controls

Working with dates and times is very common in most forms. Some common scenarios are:

  • Populate a Date control with Today's date when the form/workflow loads
  • Populate a Date control when the user signs
  • Show the Date and Time of a Sales Order
  • Create a Date Time Stamp
  • Calculate the Number of Days between two dates

The samples below show you how to create the most common business logic with dates and times. 

Working with Date and Time Rules

  • frevvo dates can be set in the user's local time zone by using the built-in date, time and date/time methods such as frevvo.currentDateTime(). See Built-in Methods for a complete method list. If you use the base JavaScript date object you will get the UTC time zone.
  • Many of the samples below use the JavaScript Date() object. Since business rules run on the form server these dates will be in the time zone where the form server was installed. There are techniques below to convert to different timezones as you need.
  • The Date/Time control uses a "T" to separate the date and time when initializing from a business rule. For example, the syntax shown below will initialize a Date/Time control named DtTm to May 15, 2012 at 4:20 am.

    DtTm.value = "5/15/2012T4:20";

A Date or Date/Time control can be converted to a js Date object: 

var date = new Date(Field.value);

A Time field can be converted to a js Date object:

var time = new Date('1970-01-01T'+Field.value);

Date controls will successfully initialize via a rule or an xml document when the data provided has a single digit for the month and/or the day, however Date/Time controls must have a 2-digit month and day.  The rule below can be used to initialize a form with a date and date/time control.  

if(form.load)
{
// Date control may have a single digit for the month and/or day
Date.value = "2020-7-1";
// DateTime control must have a double digit month and day
Date_Time.value = "2020-07-01T20:20:20";
}

The Date/Time control will display an "Invalid Value" error if single digits for the month and/or day are used .


Date and Time functions typically run in the user's browser time zone. See the Time Zones and Business Rules documentation for more details.


Set Dates and Times

The rules below allow you to set specific dates and times in your rules.

Today's Date and Time

Use the today and timeofday functions in the frevvo Rule Builder to populate a Date field with the current date and a Time field with the current time on form load. Remember to select Date or Time from the Date Control Type dropdown.

Rule List



Use the now function to set your date/time controls to the current date and time in the user's local timezone on formload.

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. These rules use the Built-In Methods: frevvo.currentDate(), which returns the current date in the user's local time zone; frevvo.currentTime(), which returns the current time in the user's local time zone; and frevvo.now() (or frevvo.currentDateTime()), which returns the current date and time in the user's local time zone.

Rule Code: Today's Date and Time
var event = form.load;
TodaySDate.value = frevvo.currentDate();
CurrentTime.value = frevvo.currentTime();
Rule Code: Current Date/Time
var event = form.load;
CurrentDateTime.value = frevvo.now();

The currentTime(), currentDate() and currentDateTime() will not work in a form.load rule unless you specify a time zone on the form's URL via the _formTz Url parameter. This is because the form server needs to know the time zone in which to return the date and time. If you do not specify a _formTz the methods will return the server date, time or date/time as explained in our Working with Rules documentation. For example &formTz=America/New_York will set the control values to the current date and time in the eastern time zone.

Date/Time Stamp

This rule sets a control named Signature to the value of a control named Name plus a date/time stamp. The form has a hidden Date and Time field.

Build two rules with the Rule Builder:

  • Populate the hidden Date and Time fields with the current Date and Time
  • Concatenate the text and Date and Time field values

Rule List

Create a Date, Time or DateTime Value

Use the date, time and date/time functions to construct dates and times in your form.

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. This uses the functions frevvo.date, which takes three parameters (year, month, day), frevvo.time which takes two parameters (hour, minute), and frevvo.dateTime which takes five parameters (year, month, day, hour, minute). You can hard code the parameter values as we have here, or you could use other control's values or variables to create the date dynamically at run time.

Rule Code
var event = form.load;
CDate.value = frevvo.date(2020, 8, 21);
CTime.value = frevvo.time(13, 59);
CDateTime.value = frevvo.dateTime(2020, 8, 21, 13, 59);

Set a Future Date

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

You can write a rule using the addToDate method to calculate a future date by adding years, months or days to a given date. The example code executes when the form loads. It uses the DateUtil.today() method to populate the control named D1 with the current date. This method returns today's date in the format 'yyyy-mm-dd' instead of 'mm-dd-yyyy', making it compatible with frevvo utility methods such as addToDate(). The rule then

  • adds one day to the current date to populate the control named D2.
  • adds one month to the current date to populate the control named D3.
  • adds one year to the current date to populate the control named D4.

Rule Code
if (form.load) {
  var dt = DateUtil.todayISO();
  D1.value = dt;
  D2.value = frevvo.addToDate(dt,'d','1');//add day
  D3.value = frevvo.addToDate(dt,'m','1');//add month
  D4.value = frevvo.addToDate(dt,'y','1');//add year
}
 Click here for a few more examples of addToDate rules...

Here is a rule that will add 3 years to a given date.  For example, to calculate the expiration date of a three year contract by adding three years to the starting date, your form could have two date controls, one used to enter the starting date and the other to show the contract expiration date. This rule will take the date from the StartingDate field, add 3 years to it and populate the result in a field named ExpirationDate.

Rule Code
if (StartDate.value.length > 0) {
var dt = StartDate.value;
ExpirationDate.value = frevvo.addToDate(dt,'y','3');
}

This rule adds 1 month to the Start Date:

Rule Code
if (StartDate.value.length > 0) {
  var dt = StartDate.value;
  ExpirationDate.value = frevvo.addToDate(dt,'m','1');
}

This rule adds 11 days to the Start Date:

Rule Code
if (StartDate.value.length > 0) {
  var dt = StartDate.value;
  ExpirationDate.value = frevvo.addToDate(dt,'d','11');
}

A common use case for this is to set a deadline, which is then used in a workflow escalation. This form is a Professional Development Request. The user will enter the date of the professional development event. We will calculate a date two weeks past that date as a deadline for them to submit their proof of attendance.  In this case we've calculated the deadline in a Date/Time control, so that it can be used in the escalation.

Rule Code
if (DateOfEvent.value.length > 0) {
  var dt = DateOfEvent.value;
  Deadline.value = frevvo.addToDate(dt,'d','14');
}

Then, you can set up an escalation on the Submit Proof step based on this date. See this documentation for details on how to set up escalations.

Set a Future Date Based on Five-Day Work Week

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Calculating a date based on a five day work week is a common business scenario used in time sheets and displaying deadlines. You can calculate 'x' number of working days from the current date, and set that date in a Date control on your form. Here is the business rule that will add 3 working days to the current date to give you the deadline date. Copy/paste the entire rule including the function in the Rule Editor. Substitute the name of your date control for <your date control>:

Rule Code
 function calcWorkingDays(fromDate, days) {
    var count = 0;
    while (count < days) {
        fromDate.setDate(fromDate.getDate() + 1);
      if (fromDate.getDay() !== 0 && fromDate.getDay() !== 6) {  // Skip weekends
            count++;
      }
    }
    return fromDate;
}
 
if (form.load && <your date control>.value.length === 0){
  var numWorkingDays = 3; 
  var today = frevvo.currentDate().split('-');
  var escDate = calcWorkingDays(new Date(today[0], today[1]-1, today[2]), numWorkingDays);
  var m = escDate.getMonth() + 1;
  var d = escDate.getDate();
  var y = escDate.getFullYear();
  <your date control>.value = m + '-' + d + '-' + y;
}

How it works

The function "calcWorkingDays" in this rule takes two parameters, fromDate and days, which you set in the if statement. This function increments the from date by 1 day at a time until it reaches the number days you specified. uses the getDay() method, which returns the day of the week (from 0 to 6) for the specified date. Sunday is 0, Monday is 1, and so on. It increments the date as long as the day of the week is not '0' (Sunday) and '6' (Saturday). 

The if statement runs on form.load and when your date control is empty. This ensures it does not overwrite the original deadline on later workflow steps. Then it gets the current date and splits it into an array (day of the week, month, day of the month) which the function can handle. It runs the calcWorkingDays function using this array as the fromDate parameter, and the numWorkingDays variable as the days parameter. Then the rule uses the getMonth(), getDate(), and getFullYear() methods to parse the result back into date format so you can set it as the date control's value. You may notice that when using getMonth() we add one to the result - this is because the month is returned as it's array position, so January is 0, February is 1, and so on. By adding 1 we get the month number that our date control expects, January is 1, February is 2, etc. 

Similarly, you can calculate a future date that accounts for both weekends and specific holidays. In this example, you will specify holidays in the holiday array. Then, the isHoliday and compare functions check each date in the count to see if they are weekends or a holiday. 

Calculate Future Date (Skip Weekends and Holidays)
var holiday = [];
        holiday[0] = new Date(2023, 2, 2);// holiday 1
        holiday[1] = new Date(2023, 2, 3);// holiday 2

function calcWorkingDays(fromDate, days) {
    var count = 0;
    while (count < days) {
        fromDate.setDate(fromDate.getDate() + 1);
      if (fromDate.getDay() !== 0 && fromDate.getDay() !== 6 && !isHoliday(fromDate, holiday)) {  // Skip weekends
            count++;
      }
    }
    return fromDate;
}

function isHoliday(dt, arr){
    var bln = false;
    for (var i = 0; i < arr.length; i++) {
        if (compare(dt, arr[i])) { //If days are not holidays
            bln = true;
            break;
        }
    }

    return bln;
}

function compare(dt1, dt2){
  frevvo.log(dt1, dt1);
    var equal = false;

    if(dt1.getDate() == dt2.getDate() && dt1.getMonth() == (dt2.getMonth()-1) && dt1.getFullYear() == dt2.getFullYear()) {
        equal = true;
    }

    return equal;
}
  
if (form.load && DeadlineSkipW.value.length === 0){
  var numWorkingDays = 10;
  var today = frevvo.currentDate().split('-');
  var escDate = calcWorkingDays(new Date(today[0], today[1]-1, today[2]), numWorkingDays);
  var m = escDate.getMonth() + 1;
  var d = escDate.getDate();
  var y = escDate.getFullYear();
  DeadlineSkipWH.value = m + '-' + d + '-' + y;
}

Here, we have three rules; one sets the deadline to 10 days in the future, one sets the deadline skipping weekends, and one skips both weekends and holidays. We ran the form on Jan 31, 2023 and set our holidays as Feb 2 and Feb 3, 2023.

Get Day of Week from Date

It can be helpful to display the day of the week (Monday, Tuesday, etc.) on your form based on a specified date. You can do this in a business rule using the getDay() JavaScript method. Here is an example.

var d = new Date(SelectDate.value);

var weekday = new Array(7);
weekday[0] = "Sunday";
weekday[1] = "Monday";
weekday[2] = "Tuesday";
weekday[3] = "Wednesday";
weekday[4] = "Thursday";
weekday[5] = "Friday";
weekday[6] = "Saturday";

var n = weekday[d.getDay()];

if (SelectDate.value){
DayOfTheWeekOutput.value = n;
}

When the user enters a date, the rule loops through the array of day-of-week names and returns the one that matches the getDay() result. Note that getDay() returns a number 0 (Sunday) - 6 (Saturday).

Calculate a Return Time

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

You can use a business rule to add a specified time interval to a value entered in one field and display the updated value in a different field. For Example, Let's say you have a Transportation Request where the user enters the start time of the trip into a control named LeaveTime. You want to automatically calculate the Return Time as 7 hours later and display it in a field named ReturnTime. Here is an example of a rule that will add 7 hours to the time entered.

Rule Code
if(LeaveTime.value.length > 0){
  var timeStr = LeaveTime.value;
  var parts = timeStr.split(':');
  var hour = parseInt(parts[0], 10);
  hour += 7;
  ReturnTime.value = hour + ':' + parts[1];
} 

Be sure to specify your controls as Time controls and change the control names to yours if they are different than shown in the example. You can change the number of hours added by modifying the value in the hour += 7 line in the rule to match your requirements.

Use this example only when you want to add whole number hours. Adding a decimal value for the time interval (such as 7.5) will not work.

Set a Pay Period

One-Week Pay Period

This Time Sheet template automatically sets a 1-week pay period and includes "Previous" and "Next" buttons that allow the user to enter data for different periods. This rule calculates the initial one-week pay period.

Set a 1-week pay period
var monthNames = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sept", "Oct", "Nov", "Dec" ];
var dayOfWeek = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];

var pa = SelectPeriod.value.split('-');
var d = new Date(pa[0]+"/"+pa[1]+"/"+pa[2]);

// Correct for user not selecting 1st day of week
d.setDate(d.getDate() - d.getDay());
var e = new Date(d);
e.setDate(e.getDate() + 6);

// Period Message Control
PeriodMsg.value = monthNames[d.getMonth()] + ' ' + d.getDate() + ' - ' + monthNames[e.getMonth()] + ' ' + e.getDate() + ' ' + e.getFullYear();
Period.value = monthNames[d.getMonth()] + ' ' + d.getDate() + ' - ' + monthNames[e.getMonth()] + ' ' + e.getDate() + ' ' + e.getFullYear();


// Column Headers
Sun[0].label = 'Sun<br/>' + d.getDate();
d.setDate(d.getDate() + 1); Mon[0].label = 'Mon<br/>' + d.getDate();
d.setDate(d.getDate() + 1); Tue[0].label = 'Tue<br/>' + d.getDate();
d.setDate(d.getDate() + 1); Wed[0].label = 'Wed<br/>' + d.getDate();
d.setDate(d.getDate() + 1); Thu[0].label = 'Thu<br/>' + d.getDate();
d.setDate(d.getDate() + 1); Fri[0].label = 'Fri<br/>' + d.getDate();
d.setDate(d.getDate() + 1); Sat[0].label = 'Sat<br/>' + d.getDate();
Two-Week Pay Period

Many businesses use a two-week pay period. Here is an example that sets the start date for the period in a date control based on the date the user enters. Optionally, you can default the user-entered date to the current date, and/or set the period end date in another date control.

function calculatePeriodStart(targetDate) {
    
    // get first day of year
    var day1 = new Date(targetDate.getFullYear(), 0, 1);
    frevvo.log("First Day of the Year " + day1);
  
    // move forward to first Saturday
    day1.setDate(7 - day1.getDay());
    frevvo.log("First Sat of teh Year " + day1);

    //Get 2 weeks in milliseconds
    var two_weeks = 1000 * 60 * 60 * 24 * 14;
    
    // Calculate the difference in milliseconds
    var difference_ms = targetDate.getTime() - day1.getTime();
    
    // Convert back to fortnights
    var numFortnightsD = difference_ms/two_weeks;
    var numFortnights = Math.ceil(numFortnightsD);
    
    // handle if on a Saturday, so a new cycle
    if(numFortnightsD === numFortnights)
    {
        numFortnights++;
    }
    
    // add the number of fortnights
    // but take one off to get the current period
    day1.setDate(day1.getDate() + (numFortnights-1)*14);
    
  frevvo.log("First Day of Current Period " + day1);
    return day1;
}

var pa = Today.value.split('-');
var d = new Date(pa[0]+"/"+pa[1]+"/"+pa[2]);
frevvo.log("Date to use as Target date" + d);

// Correct for user not selecting 1st day of 2 week period
var e = calculatePeriodStart(d);
frevvo.log("Function Result - should be first day of current period " + e);
PeriodStart.value = frevvo.date(e.getFullYear(),e.getMonth()+1, e.getDate());

Time Zone Adjusted for Daylight Savings

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

In some cases you may want to display a date in a specific time zone (for example, when scheduling a meeting in a time zone other than the user's time zone). This rule example converts today's date in UTC time zone to Central time zone and adjusts for daylight savings time. The in-line comments in the rule code below explain how this JavaScript works.

Rule Code
// Converts UTC to either CST or CDT 
if (form.load) 
{ 
    var today = new Date(); 
    var DST = 1; // If today falls outside DST period, 1 extra hour offset 
    var Central = 5; // Minimum 5 hour offset from UTC
    // Is it Daylight Savings Time? 
    var yr = today.getFullYear(); 
    // 2nd Sunday in March can't occur after the 14th 
    var dst_start = new Date("March 14, "+yr+" 02:00:00"); 
    // 1st Sunday in November can't occur after the 7th 
    var dst_end = new Date("November 07, "+yr+" 02:00:00"); 
    var day = dst_start.getDay(); // day of week of 14th 
    // Calculate 2nd Sunday in March of this year 
    dst_start.setDate(14-day); 
	day = dst_end.getDay(); // day of the week of 7th 
    // Calculate first Sunday in November of this year
	dst_end.setDate(7-day); 
    // Does today fall inside of DST period? 
    if (today >= dst_start && today < dst_end) { 
		DST = 0; 
} 
    // Adjust Date for Central Timezone 
    today.setHours(today.getHours() - Central - DST);   
 
    var m = today.getMonth() + 1; 
    var d = today.getDate(); 
    var da = today.getDay(); 
    var y = today.getFullYear(); 
    var h = today.getHours(); 
    var min = today.getMinutes(); 
    if (min < 10) { 
		min = '0' + min;
	} 
    var timezone = ['CDT', 'CST']; 
    var dom = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 
               'Thursday', 'Friday', 'Saturday']; 
    var todayStr = dom[da] + ' ' + m + '-' + d + '-' + y + ' ' + h + ':' + min + ' ' + timezone[DST];  
 
    DateTime.value = todayStr; //Set the date time string constructed above in a text control
	DT_C.value = frevvo.dateTime(y, m, d, h, min); //Set the date and time in a DateTime control
}

Duration

frevvo offers built-in methods to calculate duration in years (age) or days. 

Age

It is easy to calculate age with the Visual Rule Builder. This example form automatically determines today's date and then calculates the person's age in the control named 'Age' when they enter their Date of Birth.

Action Wizard

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. This uses two Built-In Methods: frevvo.yearsBetween, which gets the number of years between date1 and date2; and frevvo.currentDate(), which returns the current date in the user's local time zone.

Rule Code
var event = form.load;
Age.value = frevvo.yearsBetween(DateOfBirth.value, frevvo.currentDate());

Stay Duration (Days)

This form initializes the hospital discharge date using a rule, and when the user enters the admission date a 2nd rule calculates the number of days the patient stayed in the hospital.

Build three rules using the Rule Builder to:

  • Use the daysBetween function in the Rule Builder to calculate the number of days between the Admission Date and the Discharge Date.
  • Populate the Hospital Stay Duration field with the result.
  • Concatenate the number of days between the dates with the word "days" in the Hospital Stay Duration field.
  • Display an error message in the Hospital Stay Duration field if the Admission Date is later than the Discharge Date.


Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. This uses the Built-In Method frevvo.daysBetween, which is used to get the number of days between two parameters: date1 and date2. 

Rule Code
var event = form.load;
StayDuration.value = frevvo.daysBetween(AdmissionDate.value, DischargeDate.value);

Duration Including Start & End

One common use for this function is in a Time Off Request. In this case, you may wish to include both the start and end date in the calculation. Since the daysBetween function counts the start date up to (but not including) the end date simply add +1 in the Visual Rule Builder Expression.

Action Wizard

The result at run time is inclusive of both the start and end dates.

 

Duration (Working Days)

In some cases, you may want to exclude weekends when calculating the difference between two dates. Here is a rule that let's you calculate just the number of working days:

Rule Code
function getWorkingDays(startDate, endDate){
  var result = 0;
  var currentDate = startDate;
  while (currentDate <= endDate)  {  
    var weekDay = currentDate.getDay();
    if(weekDay !== 0 && weekDay !== 6){
    result++;
  }
    currentDate.setDate(currentDate.getDate()+1); 
  }
  return result;
}
var dt1 = date1.value.split('-');
var dt2 = date2.value.split('-');
var begin = new Date(dt1[0], dt1[1]-1, dt1[2]);
var end = new Date(dt2[0], dt2[1]-1, dt2[2]);
var days = getWorkingDays(begin, end);

NumberOfWorkingDays.value = days;

How it works

The function "getWorkingDays" in this rule takes two parameters, startDate and endDate. This function uses the getDay() method, which returns the day of the week (from 0 to 6) for the specified date. Sunday is 0, Monday is 1, and so on. It increments the result by 1 as long as the day of the week is not '0' (Sunday) and '6' (Saturday). 

The rule then gets the two date controls in your form and splits them each into an array (day of the week, month, day of the month) which the function can handle. It runs the calcWorkingDays function using these array as the startDate and endDate parameters. Finally we set the result of the function as the "NumberofWorkingDays" control value.

Duration between Date/Time

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Here is a rule example to calculate the time difference between two Date/Time values in hh:mm format.

Rule Code
var event = form.load;
var endDate = null;
var startDate = null;
var millisDiff = 0;
if (StartDateTime.value && EndDateTime.value) {
  startDate = new Date(StartDateTime.value);
  endDate = new Date(EndDateTime.value);
  millisDiff = endDate.getTime() - startDate.getTime();
}
var hours = Math.floor(millisDiff / 1000 / 60 / 60);
    millisDiff -= hours * 1000 * 60 * 60;
    var minutes = Math.floor(millisDiff / 1000 / 60);
  
    Duration.value = (hours <= 9 ? "0" : "") + hours + ":" + (minutes <= 9 ? "0" : "") + minutes;

How it works

 Click to learn how it works...

This rule code makes use of several functions to correctly calculate and display Date/Time values:

  • Date() - converts the date control value into an object containing year, month, day, hours, minutes, seconds, milliseconds. This allows us to work with it in other parts of the rule.
  • getTime() - returns the number of milliseconds since January 1, 1970. The millisDiff variable will be the difference between the dates in milliseconds.

To get the hours, we use Math.floor(), which rounds down to the nearest integer so we just get the number of whole hours. Any leftover partial hour will be handled in the next line as minutes. The parameter will be the number of milliseconds between the dates, divided by 1000 (to get the number of seconds, divided by 60 (to get the number of minutes, divided by 60 again (to get the number of of hours).

Next, the rule recalculates the millisDiff variable to reduce it by the number of hours you just calculated. The remaining milliseconds equal something less than one hour.

Then, use a similar Math.floor() calculations to to get the minutes. Notice you only need the 1000 and one of the 60 divisors.

Finally, we set the Duration control to results in hh:mm format. In order to ensure that the values in the hours and minutes positions are two digits, if they are less than or equal to 9, we'll add a "0" in front of the single digit. So, a result of 8 hours and 2 minutes would display 08:02.

Calculate Hours Apart

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

You may need to calculate the hours difference between two times. This rule makes sure the end time is at least 4 hours greater then the start time but no more then 6 hours later then the start time.

Rule Code
var event = form.load;
var sTime = null;
var eTime = null;
var millisDiff = 0;
if (TS.value && TE.value) {
   sTime = new Date('1970-01-01T'+TS.value);
   eTime = new Date('1970-01-01T'+TE.value);
   millisDiff = eTime.getTime() - sTime.getTime();
  if (millisDiff < 4*3600*1000 || millisDiff > 6*3600*1000) {
     TE.valid = false;
  } else {
     TE.valid = true;
  }
}

How it works

This rule makes use of the standard JavaScript date methods. We create date objects for the start and end times, and then use the getTime() method to calculate the milliseconds difference between them. Then, we use the calculations "4*3600*1000" and "6*3600*1000" to convert the milliseconds to 4 and 6 hours respectively so that we can check if the time difference falls outside of that range. If so, we set the end time to invalid, which will display it's Error Msg property. Otherwise, we set the end time control to valid.

Calculate Time Between

Consider a Time Sheet form in which the user clocks in (FromTime) and clocks out (ToTime), and we want the form to automatically calculate the time worked. To do this in a business rule, we'll convert the times to datetime strings, and then calculate the milliseconds between them. Then, we convert the milliseconds back to hours and minutes to show the time between. Here are two examples. 

  1. Simple Time Between - this rule finds the difference between two time controls in the form, and displays the difference in hh:mm format.

    Rule Code - Simple Time Between
    var event = form.load;
    var endDate = null;
    var startDate = null;
    var millisDiff = 0;
    var sd = Date.value + 'T'+ FromTime_c.value + 'Z';
    var ed = Date.value + 'T'+ EndTime_c.value + 'Z';
    
    if (FromTime_c.value && EndTime_c.value) {
      startDate = new Date(sd);
      endDate = new Date(ed);
      millisDiff = endDate.getTime() - startDate.getTime();
    
      var hours = Math.floor(millisDiff / 1000 / 60 / 60);
      millisDiff -= hours * 1000 * 60 * 60;
      var minutes = Math.floor(millisDiff / 1000 / 60);
    
      TimeBetween_c.value = (hours < 9 ? "0" : "") + hours + ":" + (minutes < 9 ? "0" : "") + minutes;
    } else {
      TimeBetween_c.value = null;
    }
  2. Time Sheet Table - this example has a table where a user can enter multiple From and To times. The rule finds the hours and minutes between, stores them in hidden table columns and displays the result for each row in the format "# hours # minutes".

    Rule Code - Time Sheet Table
    var event = form.load;
    var endDate = null;
    var startDate = null;
    var millisDiff = 0;
    
    
    for (var i = 0; i < FromTime.value.length; i ++){
      if (FromTime[i].value.length > 0 && EndTime[i].value.length > 0) {
        var sd = DateEnter[i].value + 'T'+ FromTime[i].value + 'Z';
        var ed = DateEnter[i].value + 'T'+ EndTime[i].value + 'Z';
        startDate = new Date(sd);
        endDate = new Date(ed);
        millisDiff = endDate.getTime() - startDate.getTime();
    
        var hours = Math.floor(millisDiff / 1000 / 60 / 60);
        millisDiff -= hours * 1000 * 60 * 60;
        var minutes = Math.floor(millisDiff / 1000 / 60);
    
        timeBetween[i].value = (hours < 9 ? "0" : "") + hours + " hours " + (minutes < 9 ? "0" : "") + minutes + ' minutes';
        Minutes[i].value = Number(hours) * 60 + Number(minutes);
    
      } else {
        timeBetween[i].value = null;
    
      }
    }
    
    

    We can also display the total time for the entire table in a "total time" control. This can be displayed as a number by using the Visual Rule Builder to sum the Minutes column, divide by 60 to get a decimal representation of the total hours, and then rounding to two decimal places.

    Commonly in a Time Sheet use case, you will want to round the time to the nearest quarter-hour. Here is a JavaScript rule that does just that.

    Rule Code: Total Time to Nearest Quarter Hour
    var sum_result;
    var event = Table6Repeat.itemRemoved || Table6Repeat.itemAdded || form.load;
    sum_result = 0;
    for (let i = 0; i < Minutes.value.length; i++) {
      sum_result += Minutes[i].value;
    }
    var number = (sum_result / 60);
    totalTime.value = number = (Math.round(number * 4) / 4).toFixed(2);

Date Conditions

Date controls have special condition operators available in the Visual Rule Builder: is after, is before, is on or after, and is on or before. This example rule makes the date control invalid if the date entered isn't before today's date.

Use the Rule Builder to create a rule that:

  • Uses the is after condition for the Date of Birth control
  •  Sets the Date of Birth status to invalid if the entered date is after the current date
  • Sets the error text for the Date of Birth field to "Birth Date must be earlier than today"

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. This uses the Built-In Method frevvo.beforeDate, which takes two parameters: Date1 and Date2. It returns true if Date2 is earlier than Date1. frevvo.afterDate works similarly, returning true if Date2 is after Date1.

Rule Code
var event = form.load;
if (frevvo.beforeDate(frevvo.currentDate(), DOB.value)) {
  DOB.valid = false;
  DOB.status = 'Birthdate must be earlier than today.';
} 

When you choose the 'or or after' or 'on or before' conditions, the VRB uses these same functions in the negative. So, the on or after condition would create the code if (!(frevvo.beforeDate(date1, date2))). If date1 is NOT before date 2, it must be on or after date2.

Date No More Than x Days From Today

In this example, we want to check that the date entered into a control named AppointmentDate is no more than 14 days greater than today's date. If the Appointment Date is more than 14 days from the current date, we want to display an error message to the user. The form contains a hidden field named DaysApart (visible below for the purpose of demo.) 

You will need two rules created in the Rule Builder:

  • A rule that uses the daysBetween function to calculate the number of days between the AppointmentDate and the current date. See the Rule Examples#Duration rule example for how it works.
  • A rule that sets the invalid property and error text of AppointmentDate if there are more than 14 days between the two dates.

Rule List

Date No More Than x Days Ago

In this example, we want to check that the date entered into a control named EventStartDate is not more then 30 days ago. If the Event Start date is more than 30 days from the current date, we want to display an error message to the user.

The form contains a hidden field named DaysAgo (visible here for demo purposes).

You will need two rules created in the Rule Builder:

  • A rule that uses the daysBetween function to calculate the number of days between the Event Start Date and the current date. See the Rule Examples#Duration rule example for how it works.
  • A rule that sets the invalid property and error text of Event Start Date if the date entered is more than 30 days before the current date.

Rule List

Show Error Message if Date1 is On or Before Date2

You are designing a weekly Time Sheet that has fields where the user must enter the From and To dates for the reporting period. If the date entered in Period To is equal to or earlier than the date entered in Period From field, you want to show this error message "Must be after From Date". 

Condition Wizard

Action Wizard


Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. When you choose the 'or or after' or 'on or before' conditions, the VRB uses the frevvo.beforeDate() or frevvo.afterDate() functions in the negative. It returns true when date1 is not after date2, meaning date1 is on or before date2. 

Rule Code
var event = form.load;
if (!(frevvo.afterDate(PeriodFrom.value, PeriodTo.value))) {
  PeriodTo.valid = false;
  PeriodTo.status = 'Must be after from date.';
}

Similarly, the on or after condition would create the code if (!(frevvo.beforeDate(date1, date2))). If date1 is not before date 2, it must be on or after date2.

Display a Date in a Message Control

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

The frevvo server stores all date control values in a canonical yyyy-dd-mm format, irrespective of your date control's Date Format property. The Date Format you set in the designer affects only the format the user sees at run time and not the actual stored value. The canonical format is also what you will see in your form's submission XML. When you want to use a date control's value in a message control you will need to convert the canonical date format to your desired display format. This rule writes the date control named 'datefield' into a message control named 'Msg' using the format dd/mm/yyyy.

Rule Code
var date = datefield.value.split('-');
var dateStr = date[2] + '/' + date[1] + '/' + date[0] ;
Msg.value = "I selected the date: " + dateStr;

Here's the same rule in US date format (mm/dd/yyyy).

Rule Code
var date = datefield.value.split('-');
var dateStr = date[1] + '/' + date[2] + '/' + date[0] ;
Msg.value = "I selected the date: " + dateStr;

Checking a Date for Extra Digits

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

Occasionally a user will mistakenly enter a year with more than four digits. Since technically it is still a valid year (just very far in the future) this value does not trigger an error by default. This rule verifies that the entered date does not contain extra digits and sets the control invalid if it does. For example, if a user enters a date with a 6 digit year (05/12/202121) into a control named StartDate, the customized error message displays.

Rule Code
if (StartDate.value.length > 10) {
  StartDate.valid = false;
  StartDate.status = 'Invalid. Please check that the date is entered in the mm-dd-yyyy format'; 
} else {
  StartDate.valid = true;
} 

Copy Date, Time and DateTime Values

The date control can be set to either just a date, just a time, or a combined date/time. Using the Rule Builder you can copy values from these fields to other controls in your forms/workflows.

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. This is a very simple value set rule, and will display in the date format set in the target control (Date B).

Rule Code
var event = form.load;
if (Boolean(DateA.value)) {
  DateB.value = DateA.value;
}

Repeats & Tables

Repeats and Tables are a special type of control that let you make other controls repeating so that a user can enter multiple items. Controls in a Repeat and columns in a Table have values set in arrays that represent each input instance. When writing rules for these types of repeating controls, you must loop through the Repeat/Table to set or get the array indices' values. Repeats and Tables also have special properties that you can access in a rule: maxOccurs and minOccurs. These set the minimum and maximum number of entries the user can supply.

Many rules for Repeats and Tables can be designed with the Visual Rule Builder. The Visual Rule Builder will generate code to handle when the user clicks on the  plus icon to add a repeating control/section/row (itemAdded event). This code will not be generated for rules that have the Initialization Only box checked. 

The rules in this section are divided between Repeat and Table rules. However, in most cases the rules apply to both since Tables and Repeats are both repeating types, just with a different layout.

See Working with Rules - Rules and Repeating Controls for more information.

Repeats

Show/Hide Controls in a Repeat

Here is an example of a business rule created using the Visual Rule Builder, that makes a field in a repeat control "Age" visible and required when another field in that repeat, "Name" is filled in.

Rule List

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. Notice that the VRB uses the event handler itemAdded to run this rule whenever an item is added, and a for loop to loop through the array until all index properties have been set.

Rule Code
var Name_start_index = GuestsRepeat.itemAdded ? GuestsRepeat.itemIndex : 0;
var Name_end_index = GuestsRepeat.itemAdded ? Name_start_index + 1 : Name.value.length;
var event = form.load;
for (let i = Name_start_index; i < Name_end_index; i++) {
  if (Boolean(Name[i].value)) {
    Age[i].visible = true;
    Age[i].required = true;
  } else {
    Age[i].visible = false;
    Age[i].required = false;
  }
}

Here's an example of the exact same rule when Initialization Only is checked:

Rule Code (Initialization Only)
if (form.load) {
  for (let i = 0; i < Name.value.length; i++) {
    if (Boolean(Name[i].value)) {
      Age[i].visible = true;
      Age[i].required = true;
    } else {
      Age[i].visible = false;
      Age[i].required = true;
    }
  }
}


Repeat itemAdded Condition

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. 

The event handler itemAdded is a useful condition for repeats and tables, as it runs the rule or statement whenever a new item is added to a repeat. Imagine your form has a Repeat named Erepeat that contains a section named Employee. The Employee section control contains many controls such as Name, Phone, Email, a dropdown for Manager, and a radio control for Shift.

frevvo will execute this rule each time a user clicks "+" on the repeat to add a new item. Here we want to default Shift to the value 'Day', and populate the Manager dropdown dynamically with values from the frevvo Database Connector. Usually, your form will have a form.load rule to initialize dropdown options for the first repeat item visible on your form by default.

Rule Code (form.load rule to initialize first repeat item))
/*member firstname lastname options resultSet value */
var x;
 
if (form.load) 
{ 
    var baseURL = 'http://www.myhost.com:8080/database/';
 
    // Manager Dropdown 
    eval('x=' + http.get(baseURL + 'Manager'));
    var opts = ['']; // default 1st option to blank 
    for (var i=0; i < x.resultSet.length; i++) { 
        if (x.resultSet[i]) { 
            opts[i+1] = x.resultSet[i].lastname+ ", " + x.resultSet[i].firstname; 
        } 
    }
 
  // Repeat item index 0 is on the form by default 
  Manager[0].options = opts; 
  Shift[0].value = 'Day'; // default the employee shift to day 
}

Now when a new item gets added when a user clicks the "+" icon we can save the overhead of going back to the database to retrieve dynamic options by using the itemAdded event as the condition.

Rule Code (itemAdded rule)
if (Erepeat.itemAdded) 
{ 
    var index = Erepeat.itemIndex; // which item is this in the list
  
    // No need to go back to the database for options. 
    // We already retrieved them in form.load rule for repeat item index 0 
    Manager[index].options = Manager[0].options; 
    Shift[index].value = 'Day'; // default the employee shift to day 
}

The same rule can be written for a table control. The name of a table's repeat is always <TableName>Repeat. For example, if you name your table Children, the repeat is named ChildrenRepeat.

Collapse Prior Items When Item is Added

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. 

This rule executes when a new item is added to a repeat. This form contains a repeating section with a templatized label. It is nice to collapse the other items when adding a new item to keep the repeat list small and grid-like. Medrepeat is the name of the repeat control. Medication is the name of the section control inside the repeat.

Rule Code
if (Medrepeat.itemAdded) 
{ 
    var index = Medrepeat.itemIndex; 
    for (var i = 0; i < Medication.value.length; i++) { 
        if (i !== index) {
            Medication[i].expanded = false; 
        }
        else {
            Medication[i].expanded = true;  
        }
    } 
} 

Dynamically Set Min and Max in a Repeat

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. 

You can use a business rule to set a Repeat or Table's min and max properties based on data entered at run time. Imagine an airline reservation form where the number of traveler information sections/rows displayed is based on the number of travelers. This example form has a field labeled Number of Travelers, named 'count', and a repeat named 'TravelRepeat' with a Traveler Info section. You can leave the default values for the Min/Max properties of the Section in the Forms designer or set them to any values provided the min value < max value and the max value  > min value.


This business rule displays numbered sections based on the number of travelers. 

Rule Code
var i;
if (TravelRepeat.itemAdded) {
    i = TravelRepeat.itemIndex;
    TravelNo[i].value = 1 + i;
} else {
    if (count.value > TravelNo.value.length){
        TravelRepeat.maxOccurs = count.value;
        TravelRepeat.minOccurs = count.value;
    } else {
        TravelRepeat.minOccurs = count.value;
        TravelRepeat.maxOccurs = count.value;
    }
    for (i=0; i<TravelNo.value.length;i++) {
        TravelNo[i].value = 1 + i;
    }
} 

How it works

  1. In this example, the user enters a number in the Number of Travelers (the control name is 'count') field in the form. Alternatively, you could also have a rule that retrieves the number of travelers from a webservice.
  2. The itemAdded statement is needed to determine if you are adding more sections. The first time the rule runs, this statement will evaluate as false and run the else statement
  3. Evaluate if the number of sections needed ('count.value') is greater than the number or existing sections. If true, set the maxOccurs first because the table needs to increase.
  4. If the number of sections needed is less than the number of existing sections then set the minOccurs first.
  5. The rule will loop through the existing sections and set the values in the TravelNo field. If there are still additional sections to be added after the rule has looped through the existing sections, the itemAdded lines will run.
  6. This rule sets the Min/Max properties to the same values so the plus and minus icons are not visible. This prevents users from adding repeating items.

Entering "3" as the number of travelers, sets the minOccurs and maxOccurs to 3 and shows three (3) information sections.

Repeat Item Initialization

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. 

When you add a repeat to your form in the Form Designer you can set a default value in any of those repeating controls visible in the designer. However when the user clicks "+" to add an additional item, the default entered in the Form Designer is not automatically used in this new item. In order to accomplish this you can add a simple rule as follows:

This rule initializes the value of one of the fields in the repeat to a default of '0'. RepeatTrack is the name of the repeat control containing the input control named albumOnly. This rule will execute each time a new RepeatTrack item is added when the user clicks "+".

itemAdded and itemIndex are properties of the Repeat control.

Rule Code
if (RepeatTrack.itemAdded) 
{ 
    var index = RepeatTrack.itemIndex; 
    albumOnly[index].value = 0; 
} 

Again, to initialize repeat items already on your form in the Form Designer by default, either enter your initial default values directly into the in the Form Designer or add this rule.

Rule Code
if (form.load) 
{ 
    for (var i=0; i < albumOnly.value.length; i++) { 
        albumOnly[i].value = 0; 
    } 
} 

This rule takes into account a repeat where min > 1. If min is 0 or 1, you can simplify this further by removing the for loop and simply have albumOnly[0].value = 0 inside the if (form.load).

Repeat itemAdded by Init Doc

This rule is not yet supported in the Visual Rule Builder and thus still requires some JavaScript. 

The itemAdded event also executes when frevvo adds items found in an init doc. Consider a case where you only want to initialize items added when the user clicks "+" on the form, not those added from an initialization document. An example is a form that contains a Mailing Label section in a Repeat named 'MLrepeat'. Each label needs a unique ID assigned in a control named 'MLmid'. We plan to initialize this form from a database, where these IDs are already saved, so we only need to assign new IDs when the user manually adds a new Mailing Label by clicking the "+" button on the form. When the form is submitted the assigned IDs are saved in the database via the form's Doc URI, so the next time it runs it will pull back the newly added rows and their IDs as well.

Rule Code
if (MLrepeat.itemAdded)
{
  var index = MLrepeat.itemIndex;
 
   // This rule is fired both when the user clicks "+" 
   // and when frevvo adds items found in the init doc. 
   // Need to assign new mid only when user clicks "+" 
 
  // New items added via "+" will have a zero length value. 
  if (MLmid[index].value.length === 0) { 
    // Assign unique ID to label so it can be referenced 
    // in RI Mailing Labels field 
 
    // Count the number of existing mailing labels on the form 
    var maxId = 0; 
    for (var i=0; i < MLmid.value.length; i++) 
    { 
     if (MLmid[i].value > maxId) { 
       maxId = MLmid[i].value; 
     } 
   } 
   var next = parseInt(maxId, 10) + 1; 
   MLmid[index].value = next.toFixed(0); 
 } 
}

Repeat Item Increment

This rule is not yet supported in the Visual Rule Builder and thus still requires some JavaScript. 

You can easily auto populate incremental items numbers in repeats using a business rule. In this example Erepeat is the name of the repeat control and Item is the name of the item control inside the repeat. You also need to set 1 as the default value of first repeating Item control directly into your form field via the form designer as shown here.

Rule Code
if (Erepeat.itemAdded || Erepeat.itemRemoved){
  for(var i = 0; i < Item.value.length; i++) {
    Item[i].value = i+1;
  }
}

Tables

Table controls are often used in frevvo forms/workflows. Tables are used in many situations. Here are just a few:

  • Collect Expenses in an Expense Report
  • Collect Employee work hours in a TimeSheet
  • Compute a subtotal and a grand total in a table of line items.
  • Show/Hide Table Columns on different steps of a workflow
  • Prevent users from deleting Table rows when it is populated from a back-end system.

and many more...

The Visual Rule Builder supports the calculation of Rule Examples#CalculateaSubtotal and Rule Examples#CalculateaGrandTotal, showing/hiding Table columns, and hiding the Minus  icon. The rest of the business rules in this section are not yet supported in the Visual Rule Builder and thus still require some JavaScript.

Tables are a grid layout of repeating items, and so they are identical to repeat controls when referenced in business rules. All the rule examples in this chapter that discuss repeats apply also to tables. The one important note is that you cannot explicitly name the repeat control inside your table. The repeat control inside a table is automatically named as <TableName>Repeat. For example a table named Expense automatically has a repeat named ExpenseRepeat. The rule ExpenseRepeat.itemAdded and ExpenseRepeat.itemIndex references an item added to your table and that item's index respectively.

You can change a table control to a repeat control via the Control Type or Display As properties, depending on whether the table was dragged and dropped from the palette or added from schema. When you change a table to a repeat or vice versa and there are referencing rules, it is recommended that you check the rules to ensure you have the correct syntax.

The Visual Rule Builder will generate code to handle when the user clicks on the  Plus icon to add a row (item added event). You should see this code for rules with/without conditions. This code will not be generated for rules that have the Initialization Only box checked. Here is an example of a business rule with the itemAdded event when calculating a Sub Total.

Rule Code
var Subtotal_start_index = Table290Repeat.itemAdded ? Table290Repeat.itemIndex : 0;
var Subtotal_end_index = Table290Repeat.itemAdded ? Subtotal_start_index + 1 : Subtotal.value.length;
var event = form.load;
for (let i = Subtotal_start_index; i < Subtotal_end_index; i++) {
  Subtotal[i].value = (Price[i].value * Quantity[i].value);
}

Table Column and Cell Properties

The setting of properties on table cells and columns from rules is really at the "cell" level in that you are operating on a JavaScript array. For some properties, setting the property on any of the indices affects the whole column, while for others it only affects the individual cell. Enabled, printable, required, status, valid are cell level only and do not affect the column as a whole when used in a business rule.

Each column in a table has a label, help and hint property. These properties work at the column level and can be set dynamically with a rule created with the Visual Rule Builder. 

Rule List

Here is an example of setting the table column printable property using rule code. Line 1 will set the entire column's printable property to false; lines 2-4 show an example of how to make individual cells not printable. Keep in mind that the column setting will override the setting of individual cells.

Col2.printable = false;
for (var i=0; i<Col2.value.length; i++) {
  Col2[i].printable = false;
}

Setting a column to printable is a feature introduce in the v7.4.19 patch for in-house customers and v9.1 for Cloud customers. If you were using a rule to set a column to printable in a prior version, edit the rule and re-save it to ensure that the column setting takes effect.

Show/Hide a Column in a Table

The Visible property is available on the column level and can be used in a rule to make the whole column visible or hidden. For example, let's say you have a table with 3 columns in a two step workflow. You want all three columns to display for Step 1 of the workflow. When the workflow navigates to Step 2, you do not want the ID column to display.

In the designer, the visible property is checked on the ID column property panel.

Here is the rule created with the Rule Builder that hides the ID column on Step 2.

Rule List

The Visible property is available on each table cell as well and can be used to make the cell content visible/invisible. Here is a rule that will hide the cell contents of the first cell in column a:

Rule Code
if(form.load)
{
 a[0].visible = false;
}

Hide the Minus Icon on a Table Row

You can hide the  minus icon of a table row using a business rule. For example, the designer may not want to allow users to delete rows in a table that is populated from a back end system. Hiding the delete icon on these rows eliminates accidental deletion. The rule must set the table row's deletable attribute. You will not see this property listed on the Table control property pane, but it will be listed in the Form Outline tool and is available in the Rule Builder. 

The easiest way to create a rule like this is to use the Rule Builder. Let's take a look at a simple example. Users are instructed to enter a lower case or capital Y in a table if they are planning on calling a customer. The user enters the "Y" or "y" then tabs to the company name column. The minus icon for that row will disappear. In this example, the name of the table control is CustomerTable and the first column in the table is named ContactYN.



Condition Wizard

Change the Logic Expression from "and" to "or".

Action Wizard

Use the deletable/non-deletable properties in the Action and Else Action wizards.

Else Action Wizard

Rule List

How it works

 Click to learn how it works...

If you click on the Rule Code to see the JavaScript generated by the Rule Builder, you will notice:

  • The individual rows inside the CustomerTable are referred to in the rule as CustomerTableItem.
  • Notice the TableItem deletable property is set to false when a lowercase or capital Y is entered in the first column. This will remove the minus icon for that row of the table. The for loop cycles through the table rows until the last row is reached.
Rule Code
var event = form.load;
for (let i = 0; i < ContactCustomer.value.length; i++) {
  if ((ContactCustomer[i].value === 'Y') || (ContactCustomer[i].value === 'y')) {
    CustomerTableItem[i].deletable = false;
  } else {
    CustomerTableItem[i].deletable = true;
  }
}

Dynamically Setting Min/Max in a Table

The Min/Max properties of a table can be dynamically set using a business rule. Some scenarios where you might want to do this are:

  1. On form.load you want to populate a table with data from a database. 

  2. On selecting a dropdown option, the table populates from a google spreadsheet. 

  3. The user selects a start and end date. The table populates with the number of rows = the number of days in the date range. 

For example, lets say you have a form that collects how many travelers are planning to travel. When the user enters a number of travelers, the table will populate with the same number of rows as travelers and populate the column for Traveler ID with an ID number. The form will adjust if the number of travelers is changed. You can leave the default values for the Min/Max properties of the Table in the Forms designer or set them to any values provided the min value < max value and the max value  > min value.

Here's how it works.

  1. The first part of your rule should retrieve the results set or as in this example, the user can enter a number in the Number of Travelers field (control named 'count') in the form.
  2. The itemAdded statement is needed to determine if you are adding more rows to the table. The first time the table populates this statement will evaluate as false and run the else statement
  3. Evaluate if the number of rows needed is greater than the number or existing rows. If true, the table needs to set the maxOccurs first because the table needs to increase.
  4. If the number of rows needed (count.value) is less than the number of existing rows (the table needs to shrink) then the table needs to set the minOccurs first.
  5. The rule will loop through the existing rows and set the values in the appropriate columns. If there are still additional rows to be added to the table, after the rule has looped through the existing rows, the itemAdded lines will run.
  6. This rule sets the Min/Max properties to the same values so the plus and minus icons are not visible. This prevents users from adding or removing table rows.
Rule Code
var i;
if (TableRepeat.itemAdded) {
    i = TableRepeat.itemIndex;
    TravelID[i].value = 1 + i;
} else {
    if (count.value > TravelID.value.length){
        Table.maxOccurs = count.value;
        Table.minOccurs = count.value;
    } else {
        Table.minOccurs = count.value;
        Table.maxOccurs = count.value;
    }
    for (i=0; i<TravelID.value.length;i++) {
        TravelID[i].value = 1 + i;
    }
} 

The images show the table when the user enters 5 as the number of travelers and then changes the value to 3.

When you change the minOccurs and maxOccurs values in a business rule at run time, and the rule sets the min and max to the same value, the plus and minus icons disappear but the empty left-side column will still display.

There may be cases where you want the plus and minus icons to remain visible and enabled in the table. To do this, you must reset the minOccurs to less than maxOccurs after the rule runs as follows:

Rule Code
//set the maxOccurs to more than the current length & minOccurs to 1 again
	if (TravelID.value.length === count.value){
		Table.maxOccurs = count.value + 1; //or any number larger than the count.value
		Table.minOccurs = 1;
}

Populate Table Rows based on Query Results

The recommended approach to using a table control to perform Create, Read, Update, and Delete (CRUD) operations on your database is the Doc URI method, which is efficient and requires no code. You can also find examples of the Doc URI approach in the Database Connector Tutorial.

Another approach is to use business rules to populate a table based on the results from an http.get call to a web service, for example, a frevvo Database Connector query. The business rule can dynamically set the min and max properties of the table or repeat based on the length of the query results, and populate the table rows with those query results. In this example there is a dropdown to select an order ("SelectOrder"). When the user selects an order the first rule below will execute a database connector query to bring back the order details.  We store that data in a hidden TextArea so that it can be reused in the second rule without degrading performance. The second rule then populates data in newly added table rows. The comment lines in the rule code explain what each part of the rule is doing.

Rule #1: Execute DB Query and Store Results
/*member MSRP, description, price, product, quantity, resultSet*/
if (SelectOrder.value.length > 0) {
  var x;
  eval('x=' + http.get('http://app.frevvo.com:8082/database/BIRT/orderDetailsByOrder?onum=' + SelectOrder.value));
  jsonUserData.value = JSON.stringify(x);  //Using a hidden field to store retrieved data that can then be used in item added condition
  var repeatlen = 0;
  if (x.resultSet.length > 0) {
    if (product.value.length < x.resultSet.length) {
      //Add new rows if existing number of rows is less than resultset length
      RepeatRow.maxOccurs = x.resultSet.length;
      RepeatRow.minOccurs = x.resultSet.length;
      repeatlen = product.value.length;
    } else {
      //Delete extra rows if existing number of rows is more than resultset length
      RepeatRow.minOccurs = x.resultSet.length;
      RepeatRow.maxOccurs = x.resultSet.length;
      repeatlen = x.resultSet.length;
    }
	//Populate data in already existing rows
    for (var i = 0; i < repeatlen; i++) {
      product[i].value = x.resultSet[i].product;
      quantity[i].value = x.resultSet[i].quantity;
      price[i].value = x.resultSet[i].price;
      MSRP[i].value = x.resultSet[i].MSRP;
      description[i].value = x.resultSet[i].description;
    }
  }
}
Rule #2: Populate Values in Table Rows
/*member MSRP, description, price, product, quantity, resultSet*/
//Populate values in rows as they get added using itemAdded condition
if (RepeatRowRepeat.itemAdded) {
  var y = JSON.parse(jsonUserData.value);
  var newIndex = RepeatRowRepeat.itemIndex;
  product[newIndex].value = y.resultSet[newIndex].product;
  quantity[newIndex].value = y.resultSet[newIndex].quantity;
  price[newIndex].value = y.resultSet[newIndex].price;
  MSRP[newIndex].value = y.resultSet[newIndex].MSRP;
  description[newIndex].value = y.resultSet[newIndex].description;
}

At runtime, the form looks like this:

Clear Values in a Table

Use the Rule Builder to create this rule which clears the values from all rows in a table. Simply set the name of your table control to empty. In the rule shown, the Table control named MyTable is cleared when a trigger (ClearTable) is clicked.

Rule List


How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. A for loop that iterates over all the rows. Inside the loop a null value is assigned to each column in the table row.

Rule Code
var Item_start_index = MyTableRepeat.itemAdded ? MyTableRepeat.itemIndex : 0;
var Item_end_index = MyTableRepeat.itemAdded ? Item_start_index + 1 : Item.value.length;
var event = form.load;
if (ClearTable.clicked) {
  for (let i = Item_start_index; i < Item_end_index; i++) {
    Item[i].value = null;
    Qty[i].value = null;
    Price[i].value = null;
  }
}

Clickable Links in a Table

In most cases, the Link control can be used to add a clickable link to your form that can also be clicked from the submission PDF snapshot. However, Link controls cannot be added to tables. Instead, use a message control with a rule like this to seamlessly show the clickable link on the PDF printout. For example, on an office supply order form you want the user to add the link to the requested product. The PDF snapshot will go to the buyer, who only needs to click the link. In this example, the message column is hidden in use mode and only appears on the PDF snapshot.

Rule Code
for(var i=0; i < Link.value.length; i++){
  if(EnterLink.value){
    Link[i].value = "<a href='" + EnterLink[i].value.split('?')[0] + '?' + encodeURIComponent(EnterLink[i].value.split('?')[1]) + "' target='_blank'>Open</a>";
  } else{
    Link[i].value = null;
  }
}

Set a Value Based on Table Rows

You can set a value in another control based on the value(s) in your table. For example, consider a table that asks for Appropriation Number. If there is only one row filled, we want to set a separate text control (AppNumber) to the Appropriation Number entered in the table. However, if more than one row is filled, we want to set that text control to the word 'various'. Write a business rule like this:

//The first half of the rule counts the number of items in the table. You can custom log this count by uncommenting line 10.
var countIf_sum;
var event = form.load || AppropriationTableRepeat.itemAdded || AppropriationTableRepeat.itemRemoved;
countIf_sum = 0;
for (let i = 0; i < AppropriationNumber.value.length; i++) {
  if (Boolean(AppropriationNumber[i].value)) {
    countIf_sum += 1;
  }
}
//frevvo.log(countIf_sum);
//The second half of the rule determines whether AppNumber should be the value of the first row, or the word "various" based on the count.
if (countIf_sum > 1){
var app = 'Various';  
} else if (countIf_sum === 1){
  app = AppropriationNumber[0].value;
} else {
  app = null;
}
  AppNumber.value = app;

The result is that the text box is populated with either the Appropriation Number from the table, or the word various. It will update automatically if a second table row is filled in, or if table rows are deleted leaving only one filled.

Count the Number of Items in a Table

Perhaps you need a simple rule to calculate the number of rows (in this case, Items) in a table. This table has a column named Item and a separate quantity control named NumberOfItems. We can use the Visual Rule Builders countIf function to count how many rows have a value in the Item column. The countIf function takes two arguments - first, the control to be counted, and second the condition on which to count it. In this case, we are using the isFilled function inside the second argument.

Action Wizard

The result is that the quantity field Number of Items automatically displays the number of rows in which the Item column is filled.

How it works

 Click to learn how it works...

Click Rule Code to view the JavaScript set by the Visual Rule Builder. This rule loops over the table column, adding '1' to the the variable countIf_sum for each row that has any value. When the loop is complete, it sets the Number of Items control to that variable.

Rule Code
var countIf_sum;
var event = form.load || Table1Repeat.itemAdded || Table1Repeat.itemRemoved;
countIf_sum = 0;
for (let i = 0; i < Item.value.length; i++) {
  if (!(Item[i].empty)) {
    countIf_sum += 1;
  }
}
NumberOfItems.value = countIf_sum;

Sort a Table by a Column Value

You can use JavaScript to write a sort function that reorders your table rows based on one of the columns. In this simple example, we have a table with two columns, Item (text) and Quantity. On the trigger click, this rule will resort all of the table entries alphabetically by Item. The rule loops through the table and holds the values in an array variable, sorts that array, and then replaces the values in the table in their new (sorted) indexes.

Rule Code
if (SortByItem.clicked){
  var newarr = [];
  for (let i = 0; i < Item.value.length; i++){  
    newarr.push([Item[i].value,Quantity[i].value]); 
  }

  newarr.sort(sortthis);
  for (let j = 0; j < newarr.length; j++){
    Item[j].value = newarr[j][0];
    Quantity[j].value = newarr[j][1];
  }
}

function sortthis(a, b){
  if (a > b) {return 1;}
  if (a < b) {return -1;}
  return 0;
}


Original table entries:

After Sorting:

Upload Control

The rules for the Upload Control are not yet supported in the Visual Rules Builder and thus still require some JavaScript.  

If you set the upload control's value property to null (i.e. uploadcontrol.value = null;) via a business rule, the uploaded files will be removed in the UI but not in the submission. If you have "Attachments" checked on Activity Doc Action or Doc Action Emails, the uploaded files will still be sent. A feature to delete uploaded files may be available in a future release. 

Display Uploaded Image

A rule can dynamically display an image uploaded to your form via the upload control. In this example, the upload control is named 'u'. The form also must contain a message control as a place holder for displaying the uploaded image. The rule dynamically creates a URL to the uploaded image in the frevvo temporary attachment repository. The upload control's value 'u.value' is a GUID that uniquely identifies the attachment. The uploaded image will be included in the submission PDF.

Rule Code
if (u.value.length > 0) {
  var baseUrl = "/frevvo/web/tn/" +
      _data.getParameter('tn.id') +
      "/user/"+_data.getParameter('user.id') +
      "/app/"+_data.getParameter('app.id') +
      "/form/"+_data.getParameter('form.id');
 
  im.value = '<img src="' +
         baseUrl + '/attachment/' + u.value+'/does_not_matter"/>';
}

Here is the example form before and after the user has uploaded the frevvo_logo_small.png image:

This rule is a bit more complex to handle the case where the user uploaded multiple images via the upload control.

Rule Code
var x = "";
for(var i=0; i< u.value.length; i++) {
  x = x + u[i].value + ",";
}
var y = x.split(',');
var baseUrl = _data.getParameter('_frevvo_base_url') + 
      "frevvo/web/tn/" +
      _data.getParameter('tn.id') +
      "/user/"+_data.getParameter('user.id') +
      "/app/"+_data.getParameter('app.id') +
      "/form/"+_data.getParameter('form.id');
if(y[0].length === 1) {
  im.value = "<img src='" + baseUrl + "/attachment/"+u.value+"/does_not_matter'/>";
} else {
  var z = "";
  for(var i=0; i< y.length-1; i++) {
    z = z + "<img src='" + baseUrl + "/attachment/"+ y[i] +"/does_not_matter'/>" + "<br/>";
  }
  im.value = z;
}

Display Uploaded PDF

If you are displaying an uploaded document, such as a PDF, instead of an image, replace the <img src=.../> tags with the <iframe src=.../> tag in the rule examples above.

Dynamically Set Min/Max Properties

There are two properties, minFiles and maxFiles, associated with the Upload control that can be read or changed at runtime using a business rule. This is a way to dynamically control the number of attachments uploaded to your form/workflow.

Here is a simple form with a Radio control named FilestoUpload and Upload control named UploadFiles. The options for the Files to Upload are: none, one or many. The Enabled property for the Upload control is unchecked in the designer.

The requirements for the rule are:

  • If the user selects none for the Files to Upload control, then we do not want to allow any files to be uploaded - disable the Upload control
  • If the user selects one then we want to restrict the file uploads to 1 - set minFiles to 0 and maxFiles to 1.
  • If the user selects many then we want to allow no more than 10 attachments - set minFiles to and MaxFiles to 10.

Rule Code
if (FilesToUpload.value == 'none') {
  UploadFiles.enabled = false; 
}
else if (FilesToUpload.value == 'one') {
  UploadFiles.enabled = true; 
  UploadFiles.minFiles = 1;
  UploadFiles.maxFiles = 1; 
}
else if (FilesToUpload.value == 'many') {
  UploadFiles.enabled = true; 
  UploadFiles.minFiles = 0;
  UploadFiles.maxFiles = 10; 
}

Upload Control Required/Optional

Similar to the above example, you can make the Upload control visible and required under some conditions, but hidden and option under others by setting minFiles in a business rule. Upload controls do not have a 'required' property; instead, set the 'minFiles' property to '1' for required, and to '0' for optional. Here is an example where if the user completed training, we will require them to upload a certificate by making the certificate upload control visible and setting minFiles to 1. If they did not, the certificate upload control will be hidden and have minFiles set to 0, making it optional.

Rule Code
 if (TrainingCourse.value === 'Yes') {
  certificate.minFiles = 1; 
  certificate.visible = true;;
} else {
  certificate.minFiles = 0;  
  certificate.visible = false;
}
  • There is no way to remove uploaded attachments via a rule at this time. For example, if the user checked "Yes" and uploaded one attachment, then changed their mind and checked "No", the originally uploaded attachment will still be in the submission documents.
  • Reminder - setting the maxFiles property to 0 allows unlimited attachments. To disable to control on the else condition, be sure to add the enabled property to this rule. 

    if (TrainingCourse.value === 'Yes') {
    	certificate.minFiles = 1; 
      	certificate.visible = true;
    	certificate.enabled = true;
    } else {
    	certificate.minFiles = 0;  
      	certificate.visible = false;
    	certificate.enabled = false;
    }

Count Uploaded Files

You can use a business rule to capture the total number of uploaded files at run time. 

Rule Code
if(Upload.value[0] === "_"){ //Looks at the first index to see if only a single file is uploaded
 FileCount.value = 1;
} else {
 FileCount.value = Upload.value.length;
}

Note that you must use the if condition to check if there is only one file uploaded, and the else condition to look at the total number of files if more than one is uploaded. 

Display Uploaded File Names in a Dropdown

You may want to allow your users to upload multiple files to one upload control, and then choose one of those files from a dropdown control. In this example, we're allowing the user to upload a number of documents, but we want to identify which of those is the driver's license. We will loop through the array of files and set the dropdown options to the file names.

Rule Code
var opts=[];
for (var i=0; i<UploadYourDocuments.filesCount; i++) {
   opts.push(UploadYourDocuments.files[i].name);
}
WhichFileIsYourDriverSLicense.options = opts;

Rename an Uploaded File

Using the above scenario, perhaps we also want to rename the Driver's License file (for example, to help organize it a filesystem). 

Rule Code
if (Boolean(WhichFileIsYourDriverSLicense.value)) {
  for (var i=0; i<UploadYourDocuments.filesCount; i++) {
    if (UploadYourDocuments.files[i].name === WhichFileIsYourDriverSLicense.value){
      UploadYourDocuments.files[i].name = FullName.value + "_DL";
    }
}
}

We loop through the uploaded files array and when the file name matches the selection, we set the file name to the value of a control in the form (FullName) and some static text ("_DL"). The change does not appear on the screen right away, but the next user who loads the form will see the change, for example, if the next user in a workflow loads their task, and the final submitted file is renamed. In this example, the original file name was MyDriversLicenseImage.jpg, and the new name set by the rule is Suzy Blue_DL.

Set a Maximum File Size for Submission

Many customers have limits on their email server for total size of attachments (on frevvo Cloud the limit is 25MB) sent over email. If you have multiple upload controls, or even a single Upload that allows unlimited attachments, you may need a rule to restrict the total file size on the submission. We will use the totalFileSize property to find the size of all uploads, and then a hidden control to disable the submit button. This example also shows a way to display file sizes to your user in a Message control so they know what they might need to delete.

Rule Code
//Calculate total of all Upload controls and set a hidden control to invalid if over 10 mb
if (AddFiles.totalFileSize + AddPhotos.totalFileSize > 10000000){
  DisableSubmit.value = "block submit";
  DisableSubmit.valid = false;
  MaxSizeMsg.visible = true;
} else {
  DisableSubmit.value = "allow submit";
  DisableSubmit.valid = true;
  MaxSizeMsg.visible = false;
}

//OPTIONAL - Display all uploaded file names with their sizes in mb (rounded) 
var f = "Your Files:<br/><ul>";
var p = "Your Photos:<br/><ul>";
for (var i=0; i < AddFiles.filesCount; i++) {
   f = f + "<li>" + AddFiles.files[i].name + " - " + Math.round((AddFiles.files[i].size / 1000000)*100)/100 + "</li>";
}
for (var i=0; i < AddPhotos.filesCount; i++) {
   p = p + "<li>" + AddPhotos.files[i].name + " - " + Math.round((AddPhotos.files[i].size / 1000000)*100)/100 + "</li>";
}
UploadSummary.value = f + "</ul>" + p + "</ul>";



Message Control

Display Form Data

Message controls can be used in business rules to create summary information on your form from values entered into earlier form fields. Using templates in the Message control in combination with business rules created in the Visual Rule Builder make creating the Summary easy. 

Quick Tip

Use this method to:

  • Display the body of a Doc Action or Activity Doc Action email that will be sent, so that the user can preview or edit components of it before sending. Check "Save Value" on the Message Control and then use the message control's template in the email body, i.e. {EmailMessage}.
  • Configure the nicely formatted Message Control as the only (or one of few) printable field on a form, such as an estimate form, in order to dispaly form data in a simple, branded PDF output. Here's an example:

     Click here to view an example of a printable estimate...


In this example, we simply change the value of a text control based on another control in them form. The Message uses templates to display the text control.

Form Designer

Run Time

Rule List

However, you may wish to use JavaScript for more complex rules, switch cases, or even just to improve readability of the rule. Here's an example from the Expense Report by Category prebuilt template. This rule calculates a subtotal for each category selected and displays it in a message control.

Rule Code
/* We won't use the Visual Builder here since we want to format the message for end user readability */

var cats = [];
for (var i = 0; i < Amount.value.length; i++) {
  if (Status[i].value === 'Ok') {
    if (cats[Category[i].value]) {
      cats[Category[i].value] += Amount[i].value;
    } else {
      cats[Category[i].value] = Amount[i].value;
    }
  }
} 

var s= "<strong>Expenses by Category:</strong><br/><table cellspacing='8'><tbody>";
for (var i in cats) {
  if (i) {
    s += '<tr><td>' + i + '</td><td>' + cats[i].toFixed(2) + '</td></tr>';
  }
}
s += '</tbody></table>';

CatExp.value = s;

if (ExpenseRepeat.itemRemoved) {var x;}

Here's another example that displays data from a repeat control in a message control as an HTML table. 

Rule Code
var Name_start_index = ContactsRepeat.itemAdded ? ContactsRepeat.itemIndex : 0;
var Name_end_index = ContactsRepeat.itemAdded ? Name_start_index + 1 : Name.value.length;
var event = form.load;
//set the initial value of t to the table headers
var t = '<strong>Contact Information:</strong><br/><br/><table style="width:50%" border="1px solid black"><tr><th>Name</th><th>Email</th></tr>'; 
//for each repeat instance, add to t a new html table row with the corresponding repeat data
for (let i = Name_start_index; i < Name_end_index; i++) {
  if (Boolean(Name[i].value)) {
    t += '<tr><td>' + Name[i].value + '</td><td>' + Email[i].value + '</td></tr>';
  }
}
//Finally set the value of the message control the string constructed above plus the table end tag	
ContactsMessage.value = t + '</table>';

Display a PDF in a Message Control

Message controls are a great way to display a PDF right in your form. You can host the PDF on your website where it is accessible by URL, or you can use frevvo's PDF Mapping feature to contain the PDF right in your form designer. 

Method 1 Host the PDF on your website
  1. Upload your PDF to a location on your website, and copy the link for it.
  2. Add a Message Control to your form. Give it a name (in this example we use the name PDFStatic and set the Message Type to "None." 
  3. Add a Business Rule like this:

    Rule Code
    if (form.load) {
     var baseUrl = "https://s3.amazonaws.com/static.frevvo.com/docs/Sample+PDF.pdf";
     PDFStatic.value = '<iframe width="100%" height="750" src="' + baseUrl + '" frameborder="0" ></iframe>';
    }
    
    


    1. Set the variable baseUrl to the link for your PDF.
    2. Then, set your Message Control to a string including the iframe tags and the base url. 
  4. Save and Test the form. The PDF controls at the top may vary based on the user's browser, but in most cases allow the user to download or print the PDF, and to scroll up and down the PDF if it is multiple page.
Method 2 Upload the PDF in frevvo's PDF Mapping tool

Usually the PDF Mapping feature is used to map form data to a PDF. However, it also allows you to host the PDF right within your form and use the generated link to display it similar to Method 1 above. While this method requires a few additonal steps, it eliminates the need to host your PDF elsewhere, and allows you to map form data to the PDF as well.

  1. In order to upload a PDF into frevvo's PDF Mapper, it must be an acroform. Start by uploading it to a tool like PDF Escape. If you plan to map fields, you can set the fillable fields in that tool. If you only need to display it, simply upload the PDF to that tool, add a Form Field anywhere on the document (it will not be visible to the user), then immediately download it again as a fillable acroform.
  2. Open your form and select  PDF Mapping in the Guided Designer navigation bar. 
  3. Click the  Add icon and upload your acroform.
  4. Return to Forms editing mode by clicking Form(s) in the Guided Designer navigation bar.
  5. Add a Message Control to your form. Give it a name (in this example we use the name PDFfrevvo) and set the Message Type to "None." 
  6. Add a Form Viewer control to your form. In the From property, choose the form you just uploaded.
  7. Save and test the form, and click the Form View control. The PDF will open in a new tab. Click on the URL and copy from /genform/... to the end of the url. Paste to a text pad - we'll use this in our business rule.


  8. Optionally, set the Form Viewer control to hidden. Once you have the URL, you do not necessarily need this control visible to users.
  9. Add a business rule like this:

    Rule Code
    if (form.load) {
     var baseUrl = "/frevvo/web/tn/" + _data.getParameter('tn.id') + "/user/"+_data.getParameter('user.id') + "/app/"+_data.getParameter('app.id') + "/form/"+_data.getParameter('form.id');
     PDFfrevvo.value = '<iframe width="100%" height="750" src="' + baseUrl + '/genform/_dMOPwEFOEeurl6p3Z49rvQ?action=gen-form' + '" frameborder="0" ></iframe>';
    }


    1. Set the variable baseUrl to construct your form's URL. This is dynamic, so if you later move a form from a development tenant into a production tenant, the url will contineu to work. If you are setting this rule in a workflow, be sure to use /flow/ and 'flow.id' in place of /form/ and 'form.id'.
    2. Set the message control to a string that includes the iframe tags, the baseUrl, and the /genform/... url parameter you copied in Step 7.
  10. Save and Test the form. The PDF controls at the top may vary based on the user's browser, but in most cases allow the user to download or print the PDF, and to scroll up and down the PDF if it is multiple page.
Display PDF with mapped fields

If you are mapping fields to the PDF, the Method 2 rule will not automatically show the PDF with fields mapped. However, you can add a trigger control to your form, and change the rule's condition to if (<triggerName>.clicked) which will refresh the PDF displayed in the Message control with mapped fields each time it is clicked. This can be an alternative to the built-in Form Viewer control which opens the mapped PDF in a new browser tab.

Embed a Video in a Message Control

You can also use a message control to embed a video in your form. 

  1. Get the URL for your video, such as a YouTube link.
  2. Add a Message Control to your form. Give it a name (in this example we use the name VideoMsg) and set the Message Type to "None." 
  3. Add a Business Rule like this:

    Rule Code
    if (form.load) {
      VideoMsg.value = '<iframe width="100%" height="450" src="https://youtube.com/embed/AOtLYy6I9Vg" + " frameborder="0" ></iframe>';
    }

    This rule sets the Message Control to a string that includes the iframe tags and your video link.

  4. Save and Test the form. Your users can play the video right in your form.

Rules for Integration

frevvo Database Connector

In many cases, integration with your database via the frevvo Database Connector can be accomplished with the Doc URI approach and/or the dynamic options feature, without the need for complex business rules. However, if you wish to use business rules, there are rules on this page that handle database integration.

Additional details and rule examples for database integration can be found in the Database Connector Tutorial.

Geolocation

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. Rules can be used to save a snapshot of location information of any form. For example, an insurance company may want to capture the GPS location of their representatives filling out a form used for audit purposes. The designer can use the frevvo Geo Location feature in conjunction with rules like the ones shown in the examples below to accomplish this. When the form loads in the browser, the user will be prompted by to allow/disallow the use of location services. If permission is granted, the position will be calculated and server will be updated via an Ajax which causes the rule to fire. If the user denies permission or there is a timeout, the server will get an Ajax with an error.

Use the special rule identifier, form.positionUpdated, to set up a rule in a form that will execute every time the form location is updated. See Rules Position Data for the complete list of available position.* data that you can access from your rules.

Rule Code
if (form.positionUpdated) {
  Latitude.value = _data.getParameter ("position.latitude");
  Longitude.value = _data.getParameter ("position.longitude");
  Accuracy.value = _data.getParameter ("position.accuracy");
  PositionError.value = _data.getParameter ("position.error.message");
}

Here is an example of a location tab on a Police Report form.

Checking the Detailed Loc checkbox in the Form Property pane and implementing a rule like the one shown will go to Google Maps to get additional information and a map associated with the location.

Rule Code
if (form.positionUpdated) {
  Latitude.value = _data.getParameter ("position.latitude");
  Longitude.value = _data.getParameter ("position.longitude");
  Accuracy.value = _data.getParameter ("position.accuracy");
  PositionError.value = _data.getParameter ("position.error.message");

  StreetNumber.value = _data.getParameter ("position.street_number");
  StreetLine1.value = _data.getParameter ("position.route");
  City.value = _data.getParameter ("position.locality");
  State.value = _data.getParameter ("position.administrative_area_level_1");
  County.value = _data.getParameter ("position.administrative_area_level_2");
  Country.value = _data.getParameter ("position.country");
  ZipCode.value = _data.getParameter ("position.postal_code");

  CompleteStreetAddress.value = _data.getParameter ("position.street_address");
}

When you run the form (unless you get a timeout or error) the address will automatically get filled in and the Google map will display.  

Use Google Maps API to Calculate Mileage

Mileage can be automatically calculated in a frevvo form using Google Maps API and a business rule with Javascript. 

  1. Get a Google Maps API Key
  2. Create a form with "From" and "To" Dropdown controls as well as controls to calculate mileage as in this example. 

     Click here to see example form...

  3. The dropdown option addresses must have a "+" between each word and no special characters, and should include a city and state abbreviation at a minimum. Zip code is not needed. Format the left-side values as follows: 

    Rule Code
    //Format
    StreetNumber+StreetName+City+StateInit=Label
    
    //Examples
    123+Peach+Ln+Anytown+CT=Anytown, CT
    Chicago+IL=Chicago
    New+York+NY=New York, NY


    Note the the label (right-side value) can be any value you want your users to see in the dropdown list.

  4. Create a business rule to calculate the mileage.

    Rule Code
    /*member distance, rows*/
    var x;
    var origins = "";
    var destinations = "";
    var total = 0.0;
    for (var i = 0 ; i < From.value.length; i++) {
      if (From[i].value.length > 0 && To[i].value.length > 0) {
        origins+=From[i].value + "|";
        destinations+=To[i].value + "|";
      }
    }
    
    if (origins.length > 0) {
      eval('x=' + http.get('https://maps.googleapis.com/maps/api/distancematrix/json?origins=' + origins + '&destinations=' + destinations + '&units=imperial&key=<Your Key>'));
      var miles;
      for (var i = 0 ; i < x.rows.length; i++) {
        miles = Math.round(x.rows[i].elements[i].distance['value']*0.00062137);
        total += miles;
        Miles[i].value = miles; 
      }
      TotalMilesClaimed.value = total;
      Amount.value = total * IRSMileageRate.value;
    }
    
    if (MileageTableRepeat.itemRemoved) {var x1;}
  5. The form will automatically calculate the mileage between locations, multiply it by the IRS Mileage Rate, and calculate the total reimbursement.

You may wish to provide your end user with a link to create a calendar event, for example to book a meeting or set a deadline reminder. Many modern calendar applications like Google, Outlook, and O365 provide a straightforward method to generate calendar event links. Using a frevvo business rule, you can dynamically generate such a link with date/time data and provide it to your end user. 

Here's an example form where the user inputs the event from and to dates/times. The business rule below generates the unique calendar links for three major apps by plugging the frevvo dateTime value into the URL. Click here to download this example form, upload it to your frevvo installation (v11+), and take a closer look.

mceclip0.png


Generate Calendar Event Links
// Strip the special characters from the dateTime controls for use in google link
var from = dtf.value.replace(/:/g,"").replace(/-/g,"");
var to = dtt.value.replace(/:/g,"").replace(/-/g,"");

//set up Google Calendar Link
GoogleCalLink.value = '<a href="https://calendar.google.com/calendar/render?action=TEMPLATE&dates=' + from + '%2F' + to + '&details=frevvo%20Upgrade%20Deadline%20%28old%20license%20expires%29&text=frevvo%20Upgrade%20Deadline%20%28old%20license%20expires%29" target="_blank">Add to Google Calendar</a>';

//set up Outlook Calendar link
OutlookCalLink.value = '<a href="https://outlook.live.com/calendar/0/deeplink/compose?body=frevvo%20Upgrade%20Deadline%20%28old%20license%20expires%29&enddt=' + dtf.value + '&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=' + dtt.value + '&subject=frevvo%20Upgrade%20Deadline%20%28old%20license%20expires%29" target="_blank">Add to Outlook Calendar</a>';

//set up 0365 Calendar Link
O365CalLink.value = '<a href="https://outlook.office.com/calendar/0/deeplink/compose?body=frevvo%20Upgrade%20Deadline%20%28old%20license%20expires%29&enddt=' + dtf.value + '&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=' + dtt.value + '&subject=frevvo%20Upgrade%20Deadline%20%28old%20license%20expires%29" target="_blank">Add to O365 Calendar</a>';

Consult your favorite calendar service's documentation for details on URL structure. In our example, the event title and description are hard-coded; however, you could make these dynamic using controls in your form just as you do the dates. Some apps (like Google) require the date/time in a specific format. In this example, we've stripped the colons and dashes from the frevvo dateTime values for the Google link. You can set these up as clickable links on the form, or use the control templates to add a link to your email, Pending Message, or Form Action

Another way to use calendars with forms is to provide a link to your public appointment/booking page. See this Google documentation, and this Outlook documentation, for how to create the appointment page and get a link for it.

Search Popup

This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. frevvo forms can initialize dynamically from backend systems. A common form design pattern is the master/detail. An example of master/detail is a form that contains an order number or employee Id. When the user enters an existing Id into the form field you want all other form fields to populate dynamically from values in a database. This is done using a form created from XSD data source generated by the  database connector. This part of the master/detail form design pattern needs no business rule. It happens automatically using the Doc Action manually set document URIs. See the DB Connector Tutorial for complete detail on how to initialize your form using a Doc Action document URI to your database.

This form was created from employee.xsd generated by the frevvo database connector. The master Id is a control from that XSD named racfid. The key to this design pattern is in using the master Id in the form's doc action document URI. In this example the form's doc action document URI for the employee document is set to:http://localhost:8082/database/myqset/employee?racfid={racfid}.

Often the master Id is a cryptic number not easily remembered by people using your form. It is a common and useful form design pattern to populate the master Id using a lookup search.

Here is an example form that allows user to view existing employee records and to update their information.

If you know the RACF ID for an employee you can simply type it into that field. If the RACF ID is found in the database the form will automatically populate all other fields from the database (because of the doc action document uri setting). However, if you do not know the employee's RACF ID you can click the Find Employee trigger.

This trigger control is named FindEmployee and fires the following rule that makes the search condition section control visible.

Rule Code
//Show Employee Search Criteria 
if (FindEmployee.clicked) 
{ 
    EmployeeSearch_s1.visible = true; 
    EmployeeSearch_s1.expanded = true; 
} 

The section control contains controls that are the search criteria for finding a RACF ID based on other pieces of information you may know about an employee such as name, type, and email address. The values entered into the search criteria can be partial values. For instance, entering the name "Smith" will find all employees whose name contains the letters "Smith". If you also select email, it will find all employees whose name contains "Smith" and have an email address containing the string "frevvo".

This is the rule that fires when you click the trigger control named Search.

Rule Code
/*member clicked racfid resultSet type */
var x;
 
if (Search.clicked) {
   
 eval('x=' + 
http.get('database/myqset/findEmployee?name={name_s1}&type={type_s1}&racfid={id_s1}&email={email_s1}'));
 
    var opts= []; 
    for (var i=0; i < x.resultSet.length; i++) { 
        if (x.resultSet[i]) { 
            opts[i] = x.resultSet[i].racfid + "=" + 
                      x.resultSet[i].name + ", " +  
                      x.resultSet[i].type;    
        } 
}
    SearchMsg.value = x.resultSet.length +  
    ' Matches Found. Change your criteria and click "search" to try again.'; 
    SearchMsg.visible = true; 
    SearchResults.options = opts; 
    SearchResults.visible = true; 
}


The Search returns one or more matches and dynamically populates a dropdown control named SearchResults. You can change the search criteria to narrow or expand you search. When you select one of the matches from the SearchResults dropdown this 3rd rule executes to copy the selection into the RACF ID control.

//Select from Search results 
if (SearchResults.value.length > 0) 
{ 
    racfid.value = SearchResults.value;
 
    // Hide Employee Search and clear values 
    EmployeeSearch.visible = false; 
    SearchMsg.visible = false; 
    SearchResults.visible = false; 
    SearchResults.options = []; 
}

Once the value is copied into RACF ID the form automatically loads all other values in the form from the database. This is due to the form action document Uri setting.

The values in the employee loaded into the form can now be edited. When the users clicks the form's submit button the values will automatically be updated in the database record. This is due to the form action document Uri setting.

LDAP Custom Attributes

You can use a business rule to pull custom attributes from your LDAP server into your form. You will have to modify your LDAP configuration and write a rule using _data.getParameter. See the docs section Working with Rules: LDAP Security Subject Information for an example of this type of rule. This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.

REST Web Services

REST web services can be invoked via a rule. The frevvo database connector is a practical example of a REST web service. Refer to the Database Connector Tutorial for examples of rules using http methods. REST web services very commonly return JSON strings. For an overview of how to parse JSON in a business rule, for example to populate dropdown controls, see our rule example for Dropdown Options and also our documentation on Reusing Dynamic Content.