Tip |
---|
Looking for a section on this page? Hover your cursor over the Table of Contents icon to the right to quickly navigate this page. |
...
You can build forms/workflows In 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 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; } |
...
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 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.
...
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 dropdown control. Expand the section after the exaple example for details aobut about how to create this rule in the Rule Builder.
...
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.
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; |
...
Info |
---|
|
A Date or Date/Time control can be converted to a js Date object as:
Code Block |
---|
var date = new Date(Field.value); |
A Time field can be converted to a js Date object as:
Code Block |
---|
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. The rule below can be used to initialize a form with a date and date/time control. Notice the Date.Value and Date+Time.value rule identifiers have a single digit for the month.
...
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 hours:minutes format :.
Code Block |
---|
var if (StartDateTime.value !== '' && EndDateTime.value !== '') { var d = StartDateTime.value.split('-'); var d1 = d[2].split('T'event = form.load; var endDate = null; var startDate = null; var millisDiff = 0; if (StartDateTime.value && EndDateTime.value) { startDate = new Date(StartDateTime.value); endDate var t= new Date(EndDateTime.value); millisDiff = d1[1].split('Z')[0]; var t1 = t.split(':');endDate.getTime() - startDate.getTime(); } var hours = Math.floor(millisDiff / 1000 / 60 / 60); millisDiff var-= startDatehours =* new Date(d[0],d[1],d1[0], t1[0], t1[1], t1[2])1000 * 60 * 60; var dminutes = EndDateTimeMath.value.split('-'); d1 = d[2].split('T');floor(millisDiff / 1000 / 60); t = d1[1].split('Z')[0]; var t1Duration.value = t.split(':'); var endDate = new Date(d[0],d[1],d1[0], t1[0], t1[1], t1[2]); var diff = endDate.getTime() - startDate.getTime(); var hours = Math.floor(diff / 1000 / 60 / 60); diff -= hours * 1000 * 60 * 60; var minutes = Math.floor(diff / 1000 / 60); TimeToComplete.value = (hours < 9 ? "0" : "") + hours + ":" + (minutes < 9 ? "0" : "") + minutes; }(hours < 9 ? "0" : "") + hours + ":" + (minutes < 9 ? "0" : "") + minutes; |
Today's Date and Time
Use the today and timeofday functions in the 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.
...
This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript.
You may want to calculate Calculating a date in a workflow based on a five day work week . This is a common business scenario and may be helpful if you are using the Escalations feature. It is not possible to select calendar or working days for the Days interval of the Escalation feature at this time but this enhancement is planned for a future release. As a work-around, you can calculate X 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. Then while configuring escalations, use the ‘Complete By’ condition and select the Date control.
Here is the business function/rule that will add 3 working days to the current date to give you the escalation 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 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; } |
...
Code Block |
---|
// 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 hr offset
var Central = 5; // Minimum 5 hr 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;
} |
Hours >= 4 and <= 6 Apart
This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. 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. Also start time must be on or after 1:00. The times must be entered in military units. TS is the name of the Time Start control and TE is the name of the Time End control.
...
Code Block |
---|
if (TS.value.length > 0 && TE.value.length > 0) { var sTime = TS.value.split(':'); var sHour = sTime[0' + y + ' ' + h + ':' + min + ' ' + timezone[DST]; var sMin DateTime.value = sTime[1]todayStr; } |
Hours >= 4 and <= 6 Apart
This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. 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.
Code Block |
---|
var event = form.load; var sMinssTime = sHour * 60 + parseInt(sMin,10); null; var eTime = TE.value.split(':')null; var eHourmillisDiff = eTime [0]; if (TS.value && TE.value) { var eMinsTime = eTime [1]new Date('1970-01-01T'+TS.value); eTime = var eMins = eHour * 60 + parseInt(eMin,10); new Date('1970-01-01T'+TE.value); millisDiff = eTime.getTime() - sTime.getTime(); if ((eMins - sMins) millisDiff < 4*603600*1000 || (eMins - sMins) millisDiff > 6*3600*601000) { TE.valid = false; } else { TE.valid = true; } } |
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.
...
This rule is not yet supported in the Visual Rules Builder and thus still requires some JavaScript. Irrespective of your date control's Date Format, the server stores that value in a connical canonical yyyy-dd-mm format. Thus the Date Format affects only the format the user sees while using the form and not the actual stored value. The connical 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 connical 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.
...
Info |
---|
Please see this documentation if initializing user data in a workflow with linked steps. |
Populate Form Fields with User Details for Another user
...
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; } } |
...
Code Block | ||
---|---|---|
| ||
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.
Section | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
...
Populate Table Rows based on Query Results
Tip |
---|
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. Note that you can also do this using the Doc URI approach which is discussed in the Database Connector Tutorial. However, if you prefer or need to use business rules for your use case, you The business rule can dynamically set the min and max properties of the table or repeat based on the length of the query results. You can also , 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:
Code Block |
---|
/*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; } } } |
...
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"/>'; } |
...