Section | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
...
Code Block |
---|
Desc.status = 'Invalid. Max 20 chars allowed and you have ' + Desc.value.length; |
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/flows In that meet Section 508 and WCAG 2.0 accessibility standards. Accessible forms/flows can assist users with visual and motor impairments. When the Accessible property is enabled for a form/flow, 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 the error message. Normally, the status property can be used in a business rule. 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 the message field (control named m) with the STATUS of the required/invalid control (control named t), as shown below, it will not work because the "You can't leave this empty" message for a required control is not treated as it's status.
Code Block | ||
---|---|---|
| ||
if(!t.valid)
{
m.value = t.status;
} |
If the rule is written this way, it will fill the message control with the errmsg from the invalid text control.
Code Block | ||
---|---|---|
| ||
if (!t.valid) {
if (t.value.length === 0) {
m.value = "You can't leave this empty."
} else {
m.value = t.status;
}
} |
...
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.
Required Field Status in Accessible Forms
This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.
Users typically enter multi-line text into textarea controls. If you want to display that text in an html context, for example on a web page or in an html formatted email or in your form's Form Action display message you will need to replace newlines with html breaks. This caused by the fact that 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 control named DF. The user types into the visible control named Description and a business rules converts the newline characters \n into html breaks.
Code Block |
---|
var x = Description.value;
x = x.replace(/\\r/g,"");
x = x.replace(/\\n/g,"<br/>");
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.
...
You can build forms/flows In that meet Section 508 and WCAG 2.0 accessibility standards. Accessible forms/flows can assist users with visual and motor impairments. When the Accessible property is enabled for a form/flow, 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 the error message. Normally, the status property can be used in a business rule. 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 the message field (control named m) with the STATUS of the required/invalid control (control named t), as shown below, it will not work because the "You can't leave this empty" message for a required control is not treated as it's status.
Code Block | ||
---|---|---|
| ||
if(!t.valid)
{
m.value = t.status;
} |
If the rule is written this way, it will fill the message control with the errmsg from the invalid text control.
Code Block | ||
---|---|---|
| ||
if (!t.valid) {
if (t.value.length === 0) {
m.value = "You can't leave this empty."
} else {
m.value = t.status;
}
} |
Textarea newline vs break
This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.
Users typically enter multi-line text into textarea controls. If you want to display that text in an html context, for example on a web page or in an html formatted email or in your form's Form Action display message you will need to replace newlines with html breaks. This caused by the fact that 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 control named DF. The user types into the visible control named Description and a business rules converts the newline characters \n into html breaks.
Code Block |
---|
var x = Description.value;
x = x.replace(/\\r/g,"");
x = x.replace(/\\n/g,"<br/>");
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.
Code Block |
---|
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;
} |
Selection Controls
Dropdowns, Checkboxes, Radio, ComboBox and T/F controls are selection controls. Populating selection control options to to create dynamic or static pick lists is a very common feature. Here are some of the top reasons you may want to do this:
...
The 1st rule "Load Products" populates both the visible and hidden dropdowns with options from a database.
...
database.
Code Block |
---|
/*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];
} |
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.
Code Block |
---|
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.
Code Block | ||
---|---|---|
| ||
ComboBox.optionsUrl = 'http://localhost:8082/database/combobox/productsByName?pname=' + encodeURIComponent(ComboBox.matchText); |
Example 2 - Populate options directly from webservice.
Code Block |
---|
/*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 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.
Code Block | ||
---|---|---|
| ||
if (form.load) {
JSONData.value = http.get('http://localhost:8082/database/combobox/allproductsByName');
} |
Code Block | ||
---|---|---|
| ||
/*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.includes(ComboBox.value)) {
opts[j] = x.resultSet[i].productName;
j++;
}
}
ComboBox.matchingOptions = opts;
} |
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.
Code Block |
---|
//Populate ComboBox options using results saved in the TextArea /*member DESCRIPTION, resultSet*/ for(var xk=0;k<Combobox.value.length; k++){ if (form.loadCombobox[k].value.length > 0 && Combobox[k].matchText) { eval('var x =' + httpJSON.get('https://app.frevvo.com/database/BIRT/allProducts')); parse(jsonUserData.value); var opts1opts = []; var opts2j = []0; for (var i = 0; i < x.resultSet.length; i++) { if (x.resultSet[i]) { opts1&& x.resultSet[i] = x.resultSet[i].productName; .DESCRIPTION.includes(Combobox[k].value)) { opts2[iopts[j] = x.resultSet[i].productCodeDESCRIPTION; } } Products.options = opts1j++; PID.options = opts2; Products.value} = opts1[0]; // default to 1st} product option PID.valueCombobox[k].matchingOptions = opts2[0]opts; } } |
Finding a Selected Options Index
...
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 flow 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.
Code Block |
---|
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; |
...
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 function/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>:
Code Block | ||
---|---|---|
| ||
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 dlDate = calcWorkingDays(new Date(today[0], today[1]-1, today[2]), numWorkingDays); var m = dlDate.getMonth() + 1; var d = dlDate.getDate(); var y = dlDate.getFullYear(); <your date control>.value = m + '-' + d + '-' + y; } |
...
Imagine an airline reservation form where the number of traveler information sections displayed or the number of rows in a table is based on the number of airline tickets purchased. There is a field in the form for the number of tickets named count and a repeat with a section including controls to collect information about each traveler. 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 is an example of a business rule that displays the number of sections based on the number of purchased airline tickets. Min/Max for the section are set to 1 and 20 in the forms designer.
Code Block | ||
---|---|---|
| ||
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; } } |
...
- The first part of your rule should retrieve the results set (this part of the rule is not shown here). In this example, the user enters a number in the Number of Travelers field in the form.
- 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
- Evaluate if the number of sections needed is greater than the number or existing sections. If true, set the maxoccurs first because the table needs to increase.
- if the number of sections needed is less than the number of existing sections then set the minoccurs first.
- 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.
- This rule sets the Min/Max properties to he same values so the plus and minus icons are not visible. This prevents users from adding repeating items.
Entering "5" as the number of travelers, sets the minOccurs and maxOccurs to 5 and shows 5 information sections.
Repeat Item Initialization
...
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 Max# property is reached.
Code Block |
---|
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; } } |
...
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 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.
Code Block | ||
---|---|---|
| ||
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"/>'; } |
...
Section | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Info | ||
---|---|---|
|
Count Uploaded Files
You can use a business rule to capture the total number of uploaded files at run time.
...