Live Forms v5.1 is no longer supported. Click here for information about upgrading to our latest GA Release.
Working with Rules
Business rules are added by editing your form or workflow. Click the Rules button in the form designer toolbar shown below to add new business rules and edit any existing rules in your form.
You may create a new rule by clicking on the icon. Your new rule is given a randomly generated name. You can edit the name as described below.
Each rule has three icons. Each is described below.
- Click the icon to edit an existing rule. You can edit the name, description and statement (JavaScript code) for the rule.
- Click the icon to delete a rule. Confirm your choice in the dialog that pops up and the rule will be immediately removed. This is an irreversible process so make sure you do not need the rule before you delete it.
Rules can be temporarily disabled by unchecking the ''enabled''' checkbox visible by opening a rule for edit.
On this page:
Writing Rules / Syntax
In this version of , a rule is created as a piece of JavaScript code. A future version of will provide a Rules wizard that makes it easier to create rules in a visual manner. Rules are saved and runable after you commit your form. In order to test a rule you must first commit your form. Then test it in use mode by clicking on the icon.
A rule is of the form:
if (condition) { true actions; } else { false actions; }
You can create more advanced rules primarily for the purpose of looping over repeating items. We will describe these later in the many rules examples below.
Some basic concepts of JavaScript:
- Everything is case-sensitive. Ex: var color1 and var Color1 are two different variables.
- Variables are loosely typed. Ex: var color = 'red'; and var num = 33 case the variable color to be a string type and num to be a numeric type.
- End-of-line semicolons are optional. However we recommend you use semicolons to terminate lines as part of proper coding practice as this often prevents mistakes. Also the rules validator will flag missing semicolons as an issue. So do add them to your lines.
- Comments. Code can be commented out using either // or /* */ syntax
Rules do not currently support the syntax:
- switch statement
Rules support the following syntax with some limitations:
- try-catch
Example: The value of the control named FN will get set to 'got exception' because the JavaScript variable named x is undefined in this rule.
if (form.load) { try { x.randomproperty = 'blah'; } catch (e) { FN.value = 'got exception'; } }
However, catching an exception when a control does not exist (is undefined), will not work as you might expect. This is because catches the problem while parsing the rule before the JavaScript interpreter catches the error. In the example below the control named Color does NOT exist in the form.
if (form.load) { try { Color.value = "red"; } catch(e) { msg1.value = "error caught: " + e; } }
Strings vs Numbers
Because javascript is a loosely typed language you may run into occasions where you are trying to add field values and the rule performs string concatenation instead. There are several ways to designate math vs string. One simple way is by adding a *1 to the variable. id = id*1 + 1; will ensure that id equals the current value plus one rather than the current value with a 1 appended. Ex: If the current value was 4 and you didn't write id*1 the result may be "41" rather than 5.
You may also encounter a situation in a rule in which money controls perform a string concatenation rather than addition. To correct this:
- Open the form with the rule in the Designer, change the money controls to text controls, and then save the form.
- Open the form, change the text controls back to a money controls, and then save the form again.
See this topic about string and numeric conversion.
Writing Conditions
One of the most common conditions is a rule that executes as soon as the user enters a value in control. The test for this condition depends on if the field is a string type or a numeric type.
String Types: text, textarea, date, phone
if (name.value.length > 0)
Numeric Types: money, quantity, number
if (name.value != null) or if (name.value > 0)
are both common test conditions.
Many times the condition name.value.length > 0 can be dropped altogether and the rule can be simplified. This rule executes whenever a user enters a value into either the control named firstname or lastname because the firstname and lastname controls are on the right side of the assignment operator.
fullname.value = firstname.value + ' ' + lastname.value;
Conditions testing for equality should use the === operator and not the ==. The latter will not pass the validator's strick syntax verification even though it may perform correctly at runtime. It is a good habit to always use ===.
if (Age.value === 13)
Dropdown, radio and checkbox control values are text types. So you must quote values when performing comparison operations:
Ex: if (Building.value === 1) will evaluate to true. You must quote the 1 as in if (Building.value === '1') to make the one a text type.
Control Name
Rules usually need to reference form controls. You assign a control a name using the control's name property.
- Names are case sensitive. If your control is named FirstName then you must write the rule as FirstName.value. firstname.value will not work.
- Use English alphabet characters only when naming controls. For example, controls named with ó as in Póliza may cause issues when the control is used in a business rule and with submission data.
- It is highly recommended that you avoid using JavaScript Reserved Keywords as control names. For Example, a section named New in your form will cause rule validation errors if explicitly referenced in a rule. Your rule may still work but the only way to fix the error in the rule validator is to change the name of the control. Click here for a list of the JavaScript Reserved Keywords to avoid.
Duplicate Control Names
It is very important when using a control in a rule that the control has a unique name. If multiple controls have the same name can not determine which control the rule refers to. Controls added to your form from palette are for the most part forced to have a unique name. If you try to change it to a name of a control that already exists in your form will not allow it. However there are several ways you can have multiple controls with the same name:
- Controls added from XSD data sources
- Controls added from the custom palette
- Controls nested in Sections
For example, when a control is dropped inside a section control, it is at a different nesting level then a control dropped outside a section. Also two controls, one inside a section called Car and another in a section called Boat are also at different nesting levels. The form designer will allow you to name the controls the same. For example both Car and Boat can contain a control named VIN.
You will have unexpected results if non-uniquely named controls are used in rules. Simply edit their names to make them unique. Note that editing the name of a from xsd schema control has no effect on the xml instance document created when the form is submitted nor on the xml validation.
The rule validator described in detail below will help you detect this potential rule bug by displaying a validation error such as the one shown below. This form has two controls named HorseName.
Accessing Control Properties
Rules refer to form controls using the control's Name. Let's say you have a control which you named FirstName. You can refer to properties of this control using the syntax <Control Name>.<Property>. The current version of , supports the following properties:
- visible : Set to false to hide a control and true to make the control visible.
- value : Read or write the value of a control. This is not applicable to sections, tabs and other controls where it does not make sense to set a value.
- enabled : Set to false to disable (grey out) a control so that a user can not change its value and true to enable it. This is not applicable to sections, tabs and other controls that do not make sense to disable/enable.
- expanded : Set to false to collapse a group control (sections controls only) and true to expand a group control.
- selected : Set to true to make a tab the selected tab (tab controls only).
- valid : The value of this property is true if the control contains a valid value otherwise false. Validity is based on the control’s type. For instance a numeric control will be invalid if the user enters a string value that cannot be converted to a number. This property can be read as well as written.
- required : Set to true to make a control required and display the red asterisk. (NOTE: only affects palette controls and not controls generated from XSD schema data source). This is also a property of section controls. Setting a section required to false automatically sets all inner controls to not required.
- options : This property enables dynamic setting select control options (radio, dropdown & checkbox controls only).
- label : This property sets the label seen on any control including sections.
- help : This property sets the help text.
- hint : This property sets the hint seen on hover.
- status : This property sets the error message display whenever the control's value is invalid.
- clicked : This property works with trigger controls. Its initial state is false. When a user clicks a trigger its state turns true.
- printable: Set to false to remove the control from both the printable view and PDF submission document.
- itemAdded : This property works with repeat controls. Its initial state is false. When a user clicks "+" to add a repeat item AND when a repeat item is added via a Document URI as the form loads its state turns true.
- itemRemoved : This property works with repeat controls. Its initial state is false. When a user clicks "-" to delete a repeat item its state turns true.
- itemIndex : This property works with repeat controls. When an itemAdded or itemRemoved event fires the value of itemIndex is set. For itemRemoved events itemIndex will return -1. For itemAdded events itemIndex will give you the index of the added item
These two special identifiers do not reference a specific control name. They use the static name "form". See Rule Examples for many use cases with these identifiers.
- form.load : This property is true when the form is first loading. It is useful for setting default values via rules that you need to be set before the user starts interacting with the form. This also holds true for flows. This property is true when each step of a workflow is first loading.
- form.unload : This property is true when users click the form's submit button. It is useful for setting control values just prior to the execution of the form's Doc Actions and Form Actions. This also holds true for flows. This property is true when the user clicks the continue button for each workflow step.
- form.positionUpdated : This property is used for the Geo location feature. You can fire a rule using this special identifier every time the position is updated.
Examples of identifiers used in rules are:
- FirstName.value
- BillingAddress.visible
- Email[1].value
- Email[i].visible
The latter two are examples of repeating controls. We discuss repeating controls in more detail below. Note that the case of the properties is important. FirstName.value is a valid rule identifier but FirstName.Value and FirstName.vAlUe are not.
Using the Form Outline
When writing rules, it is critical that you refer to your form's controls using correctly spelled control names, correct case and that you refer to the control properties applicable to the control's type. For example only section controls support the expanded property and only radios, checkboxes and dropdowns support the options property. The Form Outline assists form designers by making all the form's control names visible where you can copy/paste them into your rules. Also the Form Outline displays a properties list so you do not have to memorize the documentation above describing which properties are applicable to which control types.
The Form Outline is only visible when you toggle to the business rules designer view. When you toggle back to the form designer canvas view the Form Outline is hidden.
At first the form outline will be collapsed. Click the "+" on the top level form icon to begin expanding the outline. The icons are indicative of the control's type. See the LoanDate control displays a calendar icon and the LoanNumber is a quantity control. The BorrowerInformation, LoanTerms and SubjectPropertyInformation controls are section types. You can click the icon to copy a control name from the outline. The image below shows the light box that displays and prompts you to copy the control name to the clipboard via Ctrl C or Command C. From the clipboard, you can paste it into your rule. This is a great way to avoid typo mistakes.
Continue expanding the outline by clicking deeper "+" icons to reveal controls nested inside of panels and sections. In this example the Subject Property Information section contains a Panel40 and an inner Panel44 with a dropdown control named State. Notice that the outline shows all the dropdown control's options in value=label syntax. This is very useful when you need to perform a comparison to determine which option a user selected. You can copy/paste the option values into your rules to avoid typo mistakes by clicking on the icon to the right of the options.
Every control has a Properties icon. Click the "+" to expand the Properties. You will see a list of all the properties that are applicable to this control. Note that even the "form" itself has a Properties icon which show that the form has form.load and form.unload properties. LoanNumber is a quantity control and thus has all the properties you'd expect for this control type such as LoanNumber.required, value, visible, etc..
When are rules executed ?
When you create or edit a rule, figures out the list of controls and properties of those controls that the rule depends upon. The rule will be automatically executed whenever there is a state change that is relevant to the rule. This is called a dependency. If there are multiple rules triggered by the same dependency, the rules are executed sequentially in a top to bottom order as they are seen in the form designer's rules panel.
Rules can trigger the execution of other rules. So, if a rule, R1, sets the value of a control with Name A, and there is a rule R2, that depends on A.value, then rule R2 will be triggered and executed.
A rule will typically refer to one or more form controls and their properties and it will be executed if any of those properties change value. Note that Rules are not fired when the page is loaded. See form.load for more details. For example, the rule below will only be executed when N1.value changes its value.
if (N1.value > 0 || N2.value > 0) { T.value = N1.value + N2.value; }
Now let's assume a use case where you want to show a message if a user enters an invalid email. The form has a required email input control (Name=E) and an action should be executed if the email control 'valid' property is 'false'. One could write the rule:
if (!E.valid) { // code to show message here. }
The code above would not work as expected. E is a required field and therefore E.valid initial value is 'false' since the field is in initially empty. When the user enters an invalid email address, E.valid would still have the value 'false' and the rule would not execute since there is no state change. The code below would work properly.
if ((E.value.length > 0) && (!E.valid)) { // code to show message here. }
Now, the rule depends on both the value of E.valid and E.value.length and therefore, when a string longer than zero characters is entered the rule will be executed and the message will be shown.
Infinite Loops
It's easy to create rules that run forever. An example, is a rule that updates A.value based on B.value and another rule that updates B.value based on A.value. They could continually trigger each other.
The server will prevent this from happening by setting an execution time limit for each rule. Any rule that takes longer than 5 seconds to execute will be forcibly stopped. Note that this is a very lengthy period of time for most computational tasks and the vast majority of rules will not be impacted by this constraint. However, since is a hosted site that may be simultaneously used by numerous users, there is a time limit imposed on these computations.
Where are Rules Executed?
Business rules are executed on the server-side and not as client-side javascript. There are several reasons why rules execute server-side:
- Browser independence. We found it impossible to get these to run reliably in so many versions of so many browsers, especially IE. A bug in your rule (e.g. an infinite loop) will crash the browser and in some cases will require a Windows reboot. On the server, these run in separate threads with lower priority and timeouts so it will not impact the form server.
- The ability for the rule to do server-side things like http:get () to a database connector, invoke a REST service etc. These often reside behind the firewall and are not accessible to the browser.
- The rules are not exposed to the browser at all so any sensitive information in the rule (e.g. a password) won’t leave the server. However, we do not recommend putting any sensitive information in a rule.
- Rules can use information like the currently authenticated subject user id, name, email etc. though technically it would be possible to make this available on the browser if required. However, providing this information in the browser is a potential security hole.
- Rules can also modify a control – in theory, this can cause large-scale changes to the form’s valid state. Think of an optional XML complex type that has a deeply nested data structure inside it with some required and some optional elements. If any element has a value, all the other required elements become required. It’s much easier and efficient to analyze the form on the server although, technically, this is also possible in the browser. It can be extremely slow for large forms, especially in IE or if someone is running on a slower machine.
There is a potential for performance bottlenecks. However, this is rare since rules are not compute intensive typically. We think the benefits outweigh the drawbacks here.
Dates and Times
There are several special considerations when writing rules using date, time and date/time controls. You can also find many working samples in the Rules Examples chapter.
To initialize a date control, the data must be in the correct format. Here are examples of correct date formats:
d1.value = "2012-01-13"; //yyyy-mm-dd d2.value = "01-13-2012"; //mm-dd-yyyy d3.value = "jan 13 2012"; //using string for month
The following formats will not correctly initialize a date control. They will either cause unexpected results or be flagged as an invalid value when the form is used.
//adjusted or invalid date formats d4.value = "2012-13-01"; //yyyy-dd-mm d5.value = "13-01-2012"; //dd-mm-yyyy d6.value = "13-2012-01"; //dd-yyyy-mm d7.value = "01-2012-13"; //mm-yyyy-dd
Time and date/time controls also require correct formatting and an understanding of how manages timezones. converts times into the browser's local timezone before displaying them in the form. However, if time data is not correctly formatted will make multiple attempts to parse the time but will always display the time with NO timezone conversion. Here are examples of correct time formats:
t1.value = "20:30:00"; t2.value = "20:30:00Z"; t3.value = "20:30:00-04:00" dt1.value = "2012-08-23T20:30:00"; // Also any combination of valid date and time formats.
In the examples above, t2 uses the special "Z" character to specify UTC timezone and t3 uses the offset timezone method by adding a -/+ offset from UTC. -04:00 is the EDT summer timezone or otherwise known as America/New_York. dt1 is a date/time control and must have the "T" character separating the date part of the string from the time part. Any combination of valid date and time formats are allowed for date/time controls. Finally t1 does not specify a timezone so assumes the browser's timezone. If the browser is running in EDT America/New_York, then "20:30:00" is equivelent to "20:30:00-04:00". So, in this case t1 and t3 in the above example would display the exact same times.
The following time formats are incorrect. Though the time and date/time controls get initialized, no timezone conversion occurs.
// BAD time formats DateTime.value=["2011-8-23","20:20:20"]; Time.value="20:30";
automatically converts the displayed time to the browser's local time zone. Here are some examples to clarify proper time initialization and expected displayed time. Also refer to the chapter Initializing Forms with Data for date, time and date/time controls.
In the following examples the brower's timezone is UTC-05:00 which is the winter timezone for America/New_York.
In this rule the time data is specified as UTC-05:00. Thus when the control named DateTime is initialized with a date of August 23rd, which falls in the summer UTC-04:00 timezone, the time gets converted and displayed as 21:20:20, one hour ahead. The Time control is not converted since the browser is running in that same UTC-05:00 timezone.
if (ESTOffset.clicked) { // UTC-05:00 (winter) Date.value= DateUtil.today(); DateTime.value="2012-08-23T20:20:20-05:00"; Time.value="20:30:30-05:00"; }
In this rule the time data is specified as UTC-04:00. Thus when the control named Time is initialized it's converted to the browser's UTC-05:00 timezone and displayed as 19:30:30, one hour behind. The DateTime control has no conversion since August 23rd falls in UTC-04:00 timezone.
if (EDTOffset.clicked) { // UTC-04:00 (summer) Date.value= DateUtil.today(); DateTime.value="2012-08-23T20:20:20-04:00"; Time.value="20:30:30-04:00"; }
These two rules both specify the time data in UTC timezone because the special character "Z" is equivelent to an offset of UTC-00:00. The Time control is in browser's timezone UTC-05:00 so the rule data is converted and displayed as five (5) hours behind UTC. The DateTime control is a date of August 23rd, which falls in the summer UTC-04:00 timezone so the rule data is converted and displayed as four (4) hours behind UTC.
if (ZOffset.clicked) { Date.value= DateUtil.today(); DateTime.value="2012-08-23T20:20:20Z"; Time.value="20:20:20Z"; }
or
if (OOOO.clicked) { Date.value= DateUtil.today(); DateTime.value="2012-08-23T20:20:20-00:00"; Time.value="20:20:20-00:00"; }
This rule has incorrect UTC sytax. The Time control initialization is missing the required seconds and the DateTime control is using an array syntax that is not a valid UTC time initializer. makes an attempt to parse the times and does fairly well in this case. But due to the incorrect syntax the DateTime control's timezone is unknown and the rule is unable to do the proper conversion to the brower's timezone. The Time control's value appears to be ok as no conversion in this case was correct. The DateTime control's value is clearly wrong as the browser is running in UTC-05:00 while August 23rd is in daylight savings time UTC-04:00.
if (NotUTCSyntax.clicked) { Date.value = DateUtil.today(); DateTime.value = ["2011-8-23","20:20:20"]; // Array Syntax is not valid Time.value = "20:30"; // Missing required seconds }
Rule Debugging
There are several tools to help form designers write and debug rules. Each is described in detail below.
- Rule Validator
- Form Outline
- Debug Console
See the FAQ - Business Rules topic for additional important information and known limitations with rules in the current release.
Rule Validator
The first step in debugging your rule is to correct all errors caught by the rule validator. The validator executes automatically as soon as you leave a rule input window either by tabbing out of the input, clicking away from the rule to the control view, clicking the form designer finish button, etc... All rules with errors are highlighted in red. If you click finish to save your changes while there are invalid rules, you will be prompted to save with errors.
Not all errors can be detected by the rule validator. It is still very important to test your use mode forms. Use the debug console described in the next topic to test for and solve runtime errors.
Here is a validation error. Note the red background and the validator information displayed below the rule. In this case the control name was mistyped as Yourage when the name is actually YourAge. Remember that control names are case sensitive. So Yourage is not the same as YourAge with a capital A.
Once you correct the control name in this rule and again tab out of the rule code input area, the validator automatically runs again and removes the red background warning and also the validation output area disappears as there are no errors to display.
The validator is very picky and enforces high code standards! Some may find this onerous at first but this is a win in the long run as your rules will have far few issues found at runtime.
Disabled rules will not be validated. This can be useful if you are in the middle of writing a rule and want to save the form without validation errors. Disable the rule temporarily. Note that disabled rules do not execute at runtime either.
If you click finish to save the form and one or more rules have validation errors, you will be prompted: "One or more of the rules on this form validated with error(s). It is recommended that you correct these issues before saving. Do you wish to save now?". If you choose not to save, then the editor remains open.
Validation Directives
Directives are available to control how the JavaScript is validated. Directives are placed at the top of the rule code. The directives do NOT effect runtime rule execution. They are solely for communicating object information to the validator so that your rules pass validation.
This common rule paradigm shown in the example below is used to retrieve data from another system such as a database. This rule will cause validation errors since the validator does not have any information about the variable 'x'. The validator therefore cannot say whether or not the members 'resultSet', 'productId' and 'description' are valid.
if (form.load) { var x; eval('x=' + http.get('http://localhost:8082/database/products')); var opts = []; for (var i=0; i < x.resultSet.length; i++) { if (x.resultSet[i]) { opts[i] = x.resultSet[i].productId + '=' + x.resultSet[i].description; } } Products.options = opts; }
The validator will display the following errors and a suggestion that you can solve this error by confirming that resultSet, productId and decription are in fact valid members of object x. To confirm the suggestion copy the validator's suggested member directive into the first line of your rule. Of course if the validation errors are truly errors, then fix them.
You can copy/paste the members directive suggestion from the validation error pane directly into your rule. This corrected rule will pass validation:
/*member description productId resultSet */ if (form.load) { var x; eval('x=' + http.get('http://localhost:8082/database/products')); var opts = []; for (var i=0; i < x.resultSet.length; i++) { if (x.resultSet[i]) { opts[i] = x.resultSet[i].productId + '=' + x.resultSet[i].description; } } Products.options = opts; }
The validator also supports global directives. Use the global directive to identify additional global objects and functions defined in the execution environment and made available to the rule for which it is not appropriate to include a var statement in the rule code itself. One directive can be used to list all additional globals on a single line. Currently there are no known cases requiring the use of the global directive.
Debug Console
The debug console can help you quickly solve runtime problems in your business rules. The debug console is enabled by appending the URL parameter &_test=true to a form's share URL. A form's or flow's test button automatically adds the &_test=true parameter so you do not have to append the parameter manually. The debug console appears below the form. If your form is long, scroll down to view the debug console.
The debug console will only appear for users that have the special frevvo.Designer role. If a user without frevvo. Designer role appends the &_test=true URL nothing will happen, no debug console will become visible. This protects the designer's rule code from accidentally becoming visible to normal form/flow users.
The rule validator will catch many of the common errors you can make writing rules but it will not catch all errors. Some issues are only caught by testing your form at runtime. For example if a rule reference a control that repeats but the rule does not use the necessary repeating array syntax, this will pass validation but can be caught by runtime testing with the aid of the debug console.
Here is an example of debug console indicating that there is an error in the rule due to the fact that there is not control name 'TID' in this form.
The most recently executed rule is displayed at the top of the console. Scroll down to see prior rules. To follow a rule's execution, scroll down to find the RULE_START Event and then read the console output from bottom to top. This ensures that the latest rule output is always in the most handy position at the top of the debug console area.
The debug console is organized into 5 columns:
- # - A sequential line number. The # starts at 0 when a form is instantiated and increments until the form is finally submitted. Even if you clear the console by clicking the Clear link, the numbers continue incrementing from where they left off for this particular form instance. This gives you a clear way to follow the order in which the rule code was executed during the lifetime of a particular form instance.
- Level - One of 5 log levels. See below for details. The level also dictates a color coding. For example all ERROR log level messages are displayed in red.
- Event - One of many possible Events. This column enables you to quickly distinguish between various events such as rule start/stop. See the detailed event list below.
- Source - This column contains the name the designer gave to each rule for RULE_START and RULE_END events so that you can clearly see which rule the output lines are a part of. Source is also set for DOC_READ_START and DOC_READ_END events and indicates the name of the data source. If the data source is the default document Source displays the form/flow's name.
The console displays a maximum of 100 lines of debugging output. Refer to the log files for a complete history of all rule debugging output.
Log Levels
The debug console log level output defaults to the most verbose level which is TRACE. Currently there is no way to change this. The output is color coded with ERROR messages appearing in red so they can be quickly identified. The recognized log level are:
- INFO
- ERROR
- DEBUG
- VERBOSE
- TRACE
the log levels are inclusive. For example: INFO level includes ERROR, DEBUG includes INFO, etc.
This rule executed successfully as no ERROR level log messages appear in the console. The TRACE level output includes RULE_EVAL events which dump the exact lines of Javascript in your rule.
Event Types
The Event types enables you to quickly understand the meaning of each line in the debug console output. The event types are:
- START - signals the start of an execution that may span one or more rules and Doc URIs
- END - signals the end of the execution of a batch of one or more rules plus the total execution time in milliseconds
- ERROR - any errors that were thrown NOT in the context of executing rules
- START_RULE - signals the start of a rule execution
- END_RULE - signals the end of a rule execution plus the execution time of this one rule in milliseconds
- RULE_ERROR - any errors that happened while executing the specific rule
- RULE_EVAL - A message displaying the rule script
- RULE_DEP - one for each rule dependency. Examples of dependencies are: form.load, trigger clicked, if conditions that trigger rules, control values changes on the right hand side of assignments, etc..
- PROPERTY_SET - one for each control property set by the current rule
- HTTP_START - signals the start of an http call (get, put, ...)
- HTTP_END - signals the end of the http call + status code and timing
- HTTP_RESPONSE - a message that dumps the first few lines of the http response
- HTTP_ERROR - any errors that may have been thrown while performing the http call
- DOC_READ_START - signals the start of a manually set Doc URI read
- DOC_READ_END - signals the end of a manually set Doc URI read
- DOC_READ - signals the success or failure of the Doc URI read
- LOG - a user defined message that can be generated from a rule using frevvo.log('my INFO message') or frevvo.log('DEBUG', 'my debug message')
Custom Output
This example shows a CUSTOM event. These events are created by adding frevvo.log() calls to your rules. Custom log messages are useful when you need even more debugging output. See CustomLogging for more details.
Events that occur when the form is submitted currently do not appear in the debug console output. These include:
- form.unload rule output
- Doc URI write methods information
Rule Debug Logfile Output
Rules debug output can also be written to the log file <frevvo-home>/frevvo/tomcat/logs/frevvo.log file and is also visible in the tomcat console if tomcat stdout logging is enabled.
To change the rule debug logging verbosity level, append one of the Url parameters below to the base url. For example: http://localhost:8082/frevvo?rule-debug=TRACE turns on the highest rule debug log level. The verbosity options are:
- ?rule-debug=ERROR - Only log errors
- ?rule-debug=INFO - A moderate level of logging is output
- ?rule-debug=DEBUG - A higher level of logging is output
- ?rule-debug=VERBOSE - Rule dependencies and the rule code itself is also logged
- ?rule-debug=TRACE - This is the most verbose rule log level
Log levels are inclusive: INFO level includes ERROR, DEBUG includes INFO, etc
Changing the rule logging verbosity affects all tenants, users, and forms running on the server. A high log level can effect the form server's performance. Turn rule logging back to the lowest level of ERROR when your are done debugging.
Rule Profiling
Rule execution can be profiled to determine how much time each rule takes to execute. This can help you tune and improve performance in forms that use a lot of business rules. To turn on profiling in the log files set the rule-debug=INFO or higher. See rule debugging for setting log levels. Profling output is also displayed in the Debug Console.
The execution time for each rule is displayed on the RULE_END and END events. This sample debug console output indicates that the rule named "Init Products" executed in 125 milliseconds. The 141 milliseconds shown on the END event is the total time for all rules triggered by a single form change event. It's likely that the END event total time will be great then the addition of all RULE_END times. This is normal and due to the fact that some small time elapses between each rule.
Rule profiling is very helpful in narrowing down performance issues in rules that execute database queries. Profiling can help you identify if the db queries are taking too long. You will see HTTP calls taking a long time to execute.
As rules are executed on the server and not on the browser client side. Thus timing is effected by the performance of your server machine and the server's current load.
Rule Timeout
By default a rule that takes longer to execute then 5 seconds will be timed out by the Form Server. Customers using the in-house download version can customize this timeout. This is often necessary when rules have to perform longer running operations such as sending email messages, database stored procedures, etc. To configure the timeout add the following line to your web application containers property file. For the -tomcat bundle this file is <frevvo-home>\tomcat\conf\Catalina\localhost\frevvo.xml and is specified as follows:
<Parameter name="frevvo.rule.timeout" value="5000" override="false"/>
To set the timeout to 15 seconds set value="15000"
Rules and Repeating Controls
If you have a repeating control in a form that itself contains a repeating control, you cannot apply a rule to the "inner" repeating control, since there's no way to tell which of the inner repeating items goes with which outer repeating item.
What is not permitted in a rule?
Setting the value of a control to an array or to a random JavaScript object is not allowed.
Select Controls
Radio controls, dropdowns and checkboxes are all examples of select controls. Radio & dropdown controls are single select. That is, if one item in the dropdown is selected then all other items in the dropdown are deselected. The same is true for a radio. Only one radio button can be depressed at any time. Thus the ID.value of radios and dropdowns are similar to the other input and output controls. The value is a single item.
Checkbox controls are multi-select. That is, multiple items can be selected at any given time. Thus the ID.value of a checkbox is an array. Therefore on checkboxes a valid expression is ID.value.length which returns the number of items in the value array. And ID[0].value would retrieve the 1st item in the list and ID[1].value the 2nd and so on. If you have a checkbox control with only one checkbox and that checkbox is unchecked, the array will contain no elements. For a checkbox control a useful expression is ID.value.length === 0. Note that ID.length is not a valid expression. Also since a checkbox control is an array it is not a valid expression to write ID[0].value === . Because if at option in the checkbox control is unchecked, it's value will not be an empty string. It will simply not exist in the array.
Initial Control State
Every control in your form has an initial default property states for the visible, expanded, value, valid & enabled properties. However you can change a controls initial state in ' form designer Edit tab. A control’s initial state can be modified several ways. One way is by simply tying a value into an input control. This sets the default value for the control when the form is first opened in use mode. Another way is by expanding or collapsing group controls. This sets the initial expanded state. The default state for the visible and enabled properties is set via the controls edit property panel. The edit property contains checkboxes for visible and enabled for controls on which those properties make sense like input controls.
Dynamic Content
Real business forms often require dynamic content in dropdown list. Often based on the value entered into one form field, the values in other fields or options available in select controls need to be dynamic.
Rules allow invocation of http gets that return X-JSON headers with JSON objects. This allows complete flexibility in assignment of default values populated into form fields such as text controls and select (radios, dropdown, checkbox) controls. You can also use http.post(), http.delete(), and http.put() in rules, although you must use URL parameters with them, as they do not all support payloads.
Here is an example that shows the syntax of the http.get. This rule invokes the http get which must return a JSON object. The method on the servlet can do whatever necessary such as querying a database given the itemName to retrieve the itemPrice. In this example the JSON object returned contains a field called price. The eval converts and assigns the JSON object to the javascript variable x. Then x can be used in the rule as necessary. In this case it is used to set a value to the form field called Price.
eval('x=' + http.get('http://<webhost>/test/json/getPrice?itemName=' + itemName.value)); Price.value = x.price;
Imagine another example where the JSON object returned in the http.get response contains an array called clients. This array can then be used to set the options in a dropdown list.
eval('x=' + http.get('http://<webhost>/test/json/getClients')); Clients.options = x.clients;
Here is another example of a servlet that returns a JSON object after authenticating a user/password.
@Override public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { String u = request.getParameter("username"); String p = request.getParameter("password"); if (authenticate(u, p) === null) response.addHeader("X-JSON", "{auth:false}"); else response.addHeader("X-JSON", "{auth:true}"); } catch (Exception e) { throw new ServletException(e); } }
This servlet could be used in a rule as follows:
if (signForm.clicked) { eval('x=' + http.get('http://<webhost>/MYservices/signForm?username=' + u.value + '&password=' + p.value)); if (x.auth) { m.value = "<center>Authenticationn Succeeded</center>"; } else { m.value = "<center>Invalid username or password</center>"; } }
It is important to note that the http.get is accessing your http service via a URL. Certain characters must be encoded in order to correctly pass through via an HTTP URL. W3 Schools provides one good reference on this topic. You may not always be able to control the values your users enter into your form fields. If the value may contain one of these characters that value must be encoded.
For example, this http.get contains a password parameter. Since user passwords may contain characters such as '#' you should encode the value. The built-in javascript method encodeURIComponent() makes this easy.
eval('x=' + http.get('http://<webhost>/MYservices/signForm?username=' + u.value + '&password=' + encodeURIComponent(p.value)));
You may also need to decode the URL parameter in your HTTP service. For example:
import java.net.URLDecoder; String p = request.getParameter("password"); p = URLDecoder.decode(password, "UTF-8");
For complete details of integration with your backend systems to provide dynamic data into your forms, see the section on integration.
Reusing Dynamic Content
Fetching dynamic content from your backend system can be the source of degrading form performance. If your form needs to use the dynamic content multiple times you can improve performance by fetching the content once from the http.get() and saving the returned JSON string in a hidden text form control. Then you can later read the text control value and eval it again rather than having to call your backend system multiple times.
For example add a hidden text control to your form named jsonUserData. Add a single line to the rule that retrieves the user Data from your backend system via the http.get():
var result = http.get(requestUrl); jsonUserDate.value = result;
In other rules that also need the user data, rather than calling http.get() to your backend system again, add the following line to your rule:
var x; var val = jsonUserDate.value; eval('x' = val);
This will have the affect of setting x to the same string as if you had again fetched content from your backend system.
Relative URIs
The dynamic content described above requires an http get to a specific URI service returning a JSON object. The examples above specified absolute URI paths. You can also use a relative path.
Assuming the Form Server is running on http://www.myhost.com, http.get(‘http://www.myhost.com/path/to/resource’) may instead be written as http.get(‘/path/to/resource’).
For example:
eval('x=' + http.get('http://<webhost>/database/mydb/getPrices'));
In this example the path to the URI service is an absolute hard coded path. When the service is running on the same host as the form server you may use a relative URI. One common scenario where this is applicable is for In-house customers who have also installed the database connector. In this case the rule is best written as follows because it becomes portable if you move your form server from one <webhost> to another <webhost> as is common when running under development servers and moving to production servers.
eval('x=' + http.get('/database/mydb/getPrices'));
Templatized URIs
The URIs used in a http get very often contain variables. For example, the service getPrice takes as an argument an item name:
eval('x=' + http.get('http://<webhost>/test/json/getPrice?itemName=' + itemName.value)); Price.value = x.price;
This URI can also be shortened using a simplified template syntax:
eval('x=' + http.get('http://<webhost>/test/json/getPrice?itemName={itemName}')); Price.value = x.price;
For example assuming that when the form is used the field named itemName contains the value 'laptop', then using examples of both absolute and relative URIs and assuming that the service is running on http://www.frevvo.com:
http.get('/path/to/service/{itemName}') => 'http://www.frevvo.com/path/to/service/laptop' http.get('http://www.frevvo.com/path/to/service/{itemName}') => 'http://www.frevvo.com/path/to/service/laptop'
Note that since templates need not be bound to form controls the value for 'itemName' may be passed into the form using _data rather than as a value entered into the field by the user. If instead you use the syntax itemName.value in your URI then a control named itemName must exist as a field in your form.
This can be a very good way to dynamically configure your service's location rather than hard-coding the location into the rule. This form was loaded with the Url parameter &_data(myBaseUrl:'http://www.myhost.com')
http.get('{myBaseUrl}/path/to/service') => 'http://www.myhost.com/path/to/service
In this case since we do not want a form field to display myBaseUrl we use a template rather then adding a special field just for the purposes of passing the base Url into the rule.
Url Parameters Accessed in Rules
Url parameters passed into your form via the _data parameter can be accessed in rules several ways. If &_data(FirstName:'Joe') was added to your form Url, then:
- FirstName.value - only for parameters bound to a control in your form.
- {FirstName} - only for parameter used in http get templatized URI.
- _data.getParameter('FirstName') - if used anywhere else in a rule other than a URI.
Option 2 & 3 are available for both bound and unbound controls. So choose whichever approach is the simplest for you.
Built-in Data
makes certain data available to your rules. This includes information about the person currently using your form, the tenant and form information. You retrieve this data in your rule using the _data.getParameter('<data name>') syntax. Here is the list of available data:
- subject.id - logged in user's username.
- subject.first.name - Logged in users's First Name
- subject.last.name - Logged in users's Last Name
- subject.email - Logged in users's email address
- subject.roles - A list of all the roles for the logged in user
- subject.reports.to - Logged in users's Reports To
- user.id - Owner of form/flow and user folder name in the filesystem
- user.name - TBD
The following additional data is also available:
- app.id - The unique id associated with a given application. See Sharing Forms
- app.name - The name of the application
- form.type.id - The unique id associated with a given form. See Sharing Forms
- flow.type.id - The unique id associated with a given flow. See Sharing Forms
- form.id - The unique form instance id. This id is unique for each for submission.
- form.name - The name of the form
- flow.id - The unique flow instance id. This id is unique for each flow submission.
- flow.activity.id - The Id of the current workflow activity
- flow.activity.name - The name of the current workflow activity
- form.extid - Client defined extId passed in the formtype Url parameter.
- flow.extid - Client defined extId passed in the flowtype Url parameter
- form.theme - Theme assigned to a form/flow.
- tn.id - the Tenant ID where a form/flow is located
- tn.name - the Tenant Name where a form/flow is located
Position Data
The following data is available when you enable your form's GEO Location feature:
- position.latitude - Latitude
position.longitude - Longitude
- position.accuracy
- position.error.message - Error if any, retrieving the GEO Location
- position.street_number - Street Number
- position.route - Street Name
- position.locality - City
- position.administrative_area_level_1 - State
- position.administrative_area_level_2 - County
- position.country - Country
- position.postal_code - Postal Code
- position.street_address - Complete street address
Security Subject Information
You can use a form.load rule to pre-populate fields in your form with information about the currently logged in user. For example, if you have controls in your form named Id, FirstName, LastName, Email and Roles, the following rule will prefill those fields as indicated below.
if (form.load) { // User Information Id.value = _data.getParameter('subject.id'); // Username FirstName.value = _data.getParameter('subject.first.name'); LastName.value = _data.getParameter('subject.last.name'); Email.value = _data.getParameter('subject.email'); var roles = _data.getParameter ("subject.roles"); if (roles) { eval ('x=' + roles); Roles.options = x; } }
This rule is useful in a workflow where you want to make a the tab named Review visible only for the workflow activity named Manager Review.
if (form.load) { if (_data.getParameter('flow.activity.name') === 'Manager Review') { Review.visible = true; } }
Built-in Methods
provides built-in helper methods for common functionality required by business rules.
Date and Time Methods
Here is the list of available methods for working with dates and times:
- Time - frevvo.currentTime(form) - returns the current time in the user's local timezone. This method should only be used to set the value of a Time control.
- Date - frevvo.currentDate(form) - returns the current date in the user's local timezone. This method should only be used to set the value of a Date control.
- DateTime - frevvo.currentDateTime(form) - returns the current date and time in the user's local timezone. This method should only be used to set the value of a Date/Time control.
- String DateUtil.Today() - returns today's date in mm/dd/yyyy format
Here is an example of setting a Time control named Tm, a Date control named Dt and a Date/Time control named DtTm. See the dates and times rule examples for more sample.
Tm.value = frevvo.currentTime(form); Dt.value = frevvo.currentDate(form); DtTm.value = frevvo.currentDateTime(form);
When you test a form in . the formTz parameter is automatically appended in the url. Your form will load with the values specified in your rule. However, when you copy the share url, you MUST append the formTz paramater. currentTime(), currentDate() and currentDateTime() will not work in a form.load rule unless you specify a timezone on the form's Url via the _formTz Url parameter. This is because the form server needs to know the timezone in which to return the date and time. If you do not specify a _formTz the methods will return null and the control values will remain blank. Use ?_formTz= time zone string if the you are adding the timezone parameter directly after the form/flow URL and &_formTz= time zone string if you are adding the parameter after an existing URL parameter. . For example, to specify the Eastern time zone as the first parameter after the URL use ?_formTz=America/New_York. Timezone strings can be found here.
Users, Roles and Tenants Methods
Here is the list of methods for working with users, roles and tenants:
- boolean frevvo.isUniqueUserId (String userId, String tenantId) - returns true if this user does not exist in the tenant and false if it does
- boolean frevvo.isUniqueRoleId (String roleId, String tenantId) - returns true if this role does not exist in the tenant and false if it does
- boolean frevvo.isUniqueTenantId (String tenantId) - returns true if this tenant does not exist in the form server and false if it does
- String frevvo.listUsers(String tenantId) - returns a list of users in the tenant
- String frevvo.listTenants() - returns the list of all tenants on the form server
- String frevvo.listRoles(String tenantId) - returns a list of all roles in a tenant
- String frevvo.listRoles(String tenantId, boolean filterAdmin) - returns a list of all roles in the tenant if filterAdmin is false. Returns a list of all roles in the tenant except for frevvo.Administrator and frevvo.TenantAdmin if filterAdmin is true (true is the default)
- String makeCategories(String tenantId, String type) - returns a list of tenant form or control categories. Set type to 'CT' for control categries and 'FT' for form categories.
The tenantId string is your tenant name, ie: the name you enter into the tenant input when you login to .
The Strings returned are JSON Objects. See Rules Examples for Tenants, Roles, Users for details on how to use strings returned from these methods. Also to better understand the JSON Objects it's convenient to assign the returned string a textarea control for debugging purpose. In this code snippet Tid is the name of a textarea control.
Tid.value = TenantList.value;
Here is an example of the returned JSON string:
{"tenants":["Default tenant","Nancy Tenant"],"ids":["d","nancy.com"]}
The eval method is used to convert the JSON string back into an easy to use javascript object as shown below. In this example TenantList is the name of a dropdown control.
/*member tenants */ if(form.load) { var x; eval('x=' + frevvo.listTenants()); TenantList.options = x.tenants; // Init a dropdown list of all tenants on this form server }
Custom Logging Methods
You can output user defined logging messages to debug console using these helper methods:
- void frevvo.log(String msg) - adds an INFO level USER event message to the debug console
- void frevvo.log(String level, String msg) - adds a message to the debug console at a log level you define via the 'level' parameter.
If you do not set a log level the log message defaults to INFO. The recognized log level are:
- INFO
- ERROR
- DEBUG
- VERBOSE
- TRACE
Here is a snippet of debug console output. The frevvo.log() message Event type displays as "CUSTOM". In this example the rule called the method frevvo.log('ERROR', 'Loading tenant roles...); The message appears in red because all ERROR level log messages automatically are colored red.
See the debug console topic for full details of all the console output information.
Additional Helper Methods
Here is a list of additional useful helper methods:
- String frevvo.uuid() - returns a universally uniqe identifier
- Boolean IsInt(Var s) - returns true if the string is actually a number and returns false if it is not a number
- Integer makeInt(Var s) - converts the string to an integer. Returns 0 if the string cannot be converted
- String makeString(Var s) - converts the input to a string
- Boolean makeBoolean(Var s) - converts the string "true" to a boolean true else returns false
- String SubjectUtil.getFullName(Var fn, Var ln) - concatenates fn + ' ' + ln. This a handy helper so you do not have to code the javascript string concat yourself
Here is an example:
if (form.load) { myGuid.value = frevvo.uuid(); myFullName.value = SubjectUtil.getFullName(_data.getParameter('subject.first.name'), _data.getParameter('subject.last.name')); IsItInt.value = isInt(Input.value); }
See Rules Examples for usage details.
Common Methods (includes.js)
This feature is mainly intended for OEM partners. Often there are common rule code that is reused over and over in many forms. Traditionally this code must be duplicated in each form. is now packaged with a file in .war /WEB-INF/rules/includes.js which is initially empty. OEM partners can put commons functions into includes.js. There is a second file com.frevvo.forms.model/src/main/resources/com/gauss/forms/rule/executor/includes.js also packaged in .war whose contents are defined by .
When you write any form rule you can now call functions defined in these two includes.js files above. The first one is for OEM partners to create and repackage with frevvo.zip. The second one is frevvo's and is automatically updated when there is a new release and should not be modified by OEM partners.
Extracting common functions into includes.js can simplify rules by calling a common method in includes.js rather than coping the common code into each rule.
Note that includes.js is parsed and compiled once when starts. If you update this file with new functions and fixes to existing functions you must restart for the changes to take affect.
View All
Click the View All button to display all the rules in the form. Notice the lines in the rules are numbered. This view might be helpful when correcting errors reported by the Rules Editor.
Quick Help
The Quick Help button provides information about rules and documentation links to other topics.
Controls published with rules are visible here but not editable.
Prefix Property
Child/descendant controls of custom read-only controls are named with a prefix value. The prefix of these contained controls is editable and will be defaulted properly whenever the custom control is dragged more than once onto the form. In this way, you can write a separate rule to manipulate these controls within the 2nd and greater instances of the custom control. Refer to this documentation for the details.