MaestroThe UI design product. | Form Builder | Platform Developer
Maestro allows you to create various business rules in the Code View or in the Rule Editor accessed from the Rules section of the Properties pane. Before you start creating your own business rules, you need to get some basic understanding about this concept and specifics pertaining to the Maestro product.
All rule functions can access the current data value of the field to which they are attached using the variable value
(see Shortcuts). In addition to that, they receive several other parameters, injected references and shortcut variables as described below.
All Maestro business rules get packaged within a function body, providing various parameters and variables described further below. The returned value from this function is the result of the rule. So, for example the following validation rule would result in a Boolean true (meaning validation passed) or an error message string depending on the value of the field.
if (value > 17) {
return true;
} else {
return "Not old enough";
}
However, for a rule that only returns one calculation, you can enter a simple expression and omit the return
keyword. It will be added into the generated function automatically. So the same rule could be expressed simply as follows:
value > 17 ? true : "Not old enough";
Due to the semantics of the JavaScript OR (||
) operator, this can be simplified further:
value > 17 || "Not old enough";
The general rule of thumb is that if the result can be expressed as a simple calculation then it doesn't need the return
keyword. If it requires control flow, nested functions or any other kind of statements then you should use return
where appropriate.
Maestro business rule functions receive three parameters:
from
and to
properties that identify the pages being navigated from and to. To inspect the value of the info parameter for a specific rule in the JavaScript console add the following line:console.log(info);
As explained above, a new data context is created for each instance of a repeat. To navigate to the parent context use data.$p
. To navigate to the repeat item's own data entry in the parent context (which will be an array of all the data contexts in that repeat) use data.$r
. To find out a data context's index within a repeat, use data.$i
.
So, to get to a sibling context - say the previous instance in a repeat - you would first navigate to the repeat's data entry and then use square-brackets to address the relevant child context in the array
data.$r[data.$i - 1];
If the item for which the rule is defined is in a container, you can access that container item using item.$parent
.
Within any given container item, the child items are held in an array of rows, each of which is an array of child items within that row. So the first child of a containers first row would be item.rows[0][0]
.
In this way you can navigate up, down and across the view hierarchy. However, to get to a specific item it's easier to look it up in the flattened map held in Form.items
, such as
Form.items.myRadioButton;
The properties of each item are available on the key properties
. So to get the placeholder text of a Text Field with ID firstName
you would use
Form.items.firstName.properties.placeholder;
You can easily get an ID of the current page by using the Form.getCurrentPageId()
API or the property (where the page controller stores there a name of a current page) maestro.Form.$Pages.currentPage.id
. To get a current name of a Modal page, use maestro.Form.pageName
that returns a valid String only when a Modal page is displayed. For a Dialog, use maestro.Form.dialogName
returns either be an empty String when no Dialog is shown, or the Dialog name when a Dialog is shown. You can use the following script to simplify obtaining a current page ID:
function customGetCurrentPageId() {
if(maestro.Form.pageName === "") {
// Non-modal page
return maestro.Form.getCurrentPageId();
} else {
// Modal page
return maestro.Form.pageName;
}
}
In the previous section we saw the a reference to an object called Form
- in that case to access its items
property, containing an ID-keyed map of all the view items. There are several of these global objects. (They are not global in the sense of being available on the window
object, but they are injected into the scope of each business rule function using Angular.) These objects (or "Services" in the Angular terminology) are described below. A full reference of API methods available on each of them can be found using the API Methods Reference link in the Code View.
$rootScope
The Angular $rootScope
object - can be useful if you are an Angular expert. See the Angular documentation.
$q
The Angular $q
object. This can be used for creating and aggregating Promises. See the Angular documentation, as well as general documentation about JavaScript Promises.
$timeout
The Angular timeout function. This is similar to JavaScript's standard setTimeout function, but ensures that any data updates are rendered again in the form. Also, it can be used with a callback, as follows:
$timeout(function() {
data.status = "Success";
}, 5000);
or it can be used to return a prommise, as follows:
$timeout(5000).then(function() {
data.status = "Success";
});
The Form service contains a number of properties and methods for general form interactions such as validation and submission.
The Calc service provides some useful methods for running calculations on data.
The Resource service provides methods for working with external resources, such as formatting image URLs.
The Util service provides general utilities to assist with tasks such as searching and sorting arrays, walking the row/child view hierarchy and so on.
The DynamicData service provides methods for interacting with Dynamic Data Services published in Transact Manager.
The Scroll service provides a way to scroll to and focus on particular form fields.
The Insights services enables rules to register events with Transact Insights analytics.
The Translation service has some helper methods for working with multi-lingual forms, although for basic access to localized terminology use the T
shortcut described further below.
This is a shortcut to the current data value for the field to which the rule is attached. So for a rule on a Text Input with ID lastName it is the equivalent of using data.lastName
.
In the final published form, all the business rules are contained within a single Rules service. This shortcut lets you navigate to and even run business rules from within other business rules.
Business rule functions have names formatted as follows:
<rule-type>_<item-id></item-id></rule-type>
Basic standard rule types are ok
(validation), us
(editability), sh
(visibility) and eq
(calculation). Other action rule types have their full action name, such as click
, blur
and change
. Components can also provide their own custom rule types.
So, the click rule on a button with ID verify
could be accessed with
Rules.click_verify;
and the validation rule for a Decimal Field with ID age
would be
Rules.ok_age;
Whilst you could call the age validation rule from another rule in the same data context with the code
Rules.ok_age(data, Form.items.age);
It would only return the validation state of that field. To properly validate the field, including display of approriate errors and setting form validation state, you should use the validation methods available in the Form
service object.
This is the hierarchical map of all terminology items for the currently selected language in a multi-lingual form. So, to get the translated placeholder text for an item with ID firstName you could use
T.firstName.properties.placeholder;
This is a shortcut to the SystemProfile node of the form's data, which contains metadata about the submission provided by Transact Manager.
This is a shortcut to the Job section of the forms SystemProfile data. Useful for some collaboration job scenarios.
When the rule is triggered, the value of the field is passed in as a value
variable. For instance, the Select Language component defines a Change rule as Translation.fetch(value).catch(function(error) { alert("Cannot find language code '"+value+"' in form")}).
So, when you select a language from the dropdown list, the value
variable will be the value of the entry you have selected.
The rule execution context also exposes the following objects, which you can call via API methods:
The Rule editor provides a quick access to the API documentation, so click API Methods to open Maestro API Reference documentation in a new tab.
While you are writing your business rules, you should keep in mind the following suggestions:
Sometimes, the Rule editor can become confused about exactly what you are trying to achieve, and can interpret what you are typing in the wrong way. If you are getting unexpected results, you may want to switch to Source mode, and review the underlying HTML. You may have accidentally injected HTML tags you didn't expect.
When you are developing rule's formulae, test your work often. Several types of errors can result in your form not displaying correctly in Preview mode. The more often you test, the more likely you are to find the bit of code that is causing the problem.
A calculation is assumed to be code, and therefore everything is interpreted as JavaScript. When you insert a field into a calculation, simply reference the field id as data.firstname
.
For example:
"Welcome " + data.firstname
A caption or display field is interpreted as text, and so you need special syntax in order to have everything interpreted as code. Maestro uses double curly braces to inject code into the string.
For example, to insert a field value into a piece of text, such as a caption or a display field, use the following notation:
{{ data.firstname }}
You can perform calculations within the braces, like this:
{{ data.firstname + " " + data.lastname }}
The braces mean: "Treat whatever is inside the braces as a calculation rather than as text.
Sometimes a text display can look strange if only some of the values that are used within it have been completed. To avoid this, simply create a Visibility rule that looks something like this:
data.firstname != "" && data.lastname != ""
or
+data.term != 0 && +data.frequency != 0
All data is inherently treated as text. If you want to force a field's value to be treated as a number, prefix it with a "+". (If you don't do this, when you try to add two numbers, they will be concatenated as strings rather than added together. You can also use JavaScript functions to format the result. For example:
(+data.term * +data.frequency).toFixed(2)
or
(+data.term) + (+data.frequency).toFixed(2)
All data is inherently treated as text. If you want to force a field's value to be treated as a boolean or logical value, prefix it with !!
. For example:
if (!!data.is_married) ...
If you have, for example, some values in a dropdown list, and want to turn these into more human readable values, you could use an if statement or a switch statement. However, if you want to embed the logic inside some text, you can use the following object key lookup syntax:
{{ {'52': 'per week', '24': 'bi-monthly', '12': 'per month'} [data.frequency] }}
This is a shortcut for:
Depending on the values of [data.frequency], use the following lookup values:
This defines an in-line JavaScript object, and then uses the field's data value to index into that object. More information is available at this website.
Note that if there is no match, then the first pair will be used, and so this can be used as a default.
Next, learn how to create a rule.