Skip to main content
FlowForma was designed with flexibility in mind, so that no one should be limited by what it offers out-of-the box. This is where custom rules come into play. Rules are used to perform tasks, when specific events occur on a Form - a question is filled, a step is completed… If you need a external system to be notified when a Form is completed, then you need a rule to contact that external system. They are composed by a pair of two files, one .js file where its logic resides, and one .html file that defines the rule’s management screen and how/what will be displayed on Flow Designer.

Step-by-step guide to create a custom rule

The .js file

Every rule has to have a structure that consists of rule name (in this example “sampleruleaction”), an Execute function and a CreateInit function.
//# sourceURL=callwebservice.js
/// <reference path="~/SharePointRoot\Template\Layouts\FlowForma\js\handlebars-v2.0.0.js" />
/// <reference path="~/SharePointRoot\Template\Layouts\FlowForma\js\jquery-1.9.1.intellisense.js"/>
/// <reference path="~/SharePointRoot\Template\Layouts\FlowForma\js\FlowForma.js" />
/// <reference path="~/SharePointRoot\Template\Layouts\FlowForma\js\kendo.all.js" />

$.extend(FlowForma.Rules.Widgets,
{
    callwebservice: {
        // This fuction is called when the rule is triggered
        Execute: function (action, question, step, expandUndo) {
            // Rule deferred for awaiting rule to complete
            var deferred = $.Deferred();
            deferred.type = 'callwebservice';
            FlowForma.FormContext.Deferred.push(deferred);

            // Getting the definition that was saved in the flow designer
            var definition = $.parseJSON(action.Definition);
            if (definition) {
                // Data that will be passed to the web service
                var data = {};
                data['flowId'] = definition.FlowId;

                // Calling the web service
                $.ajax({
                    url: (FlowForma.FlowFormaServerUrl == '/' ? "" : FlowForma.FlowFormaServerUrl) + '/' + '_vti_bin/FlowForma.svc/GetFlowById',
                    headers: FlowForma.Services.Headers,
                    type: "POST",
                    dataType: 'json',
                    data: JSON.stringify(data),
                    contentType: "application/json; charset=utf-8",
                    success: function (data) {
                        // Checking if the web service returned any data
                        if (data.GetFlowByIdResult != null) {
                            // Getting the question that was saved in the rule
                            var setTargetQuestion = FlowForma.FormContext.GetQuestionByPickerValue(definition.TargetQuestion);
                            // Setting the question with the value that was recieved from the web service
                            FlowForma.Questions.SetQuestionByValue(setTargetQuestion, data.GetFlowByIdResult.Title, function () {
                                // Triggering question update event in order for other rules that are under that question to be triggered
                                $(setTargetQuestion).trigger(FlowForma.Events.Question.Updated, []);
                                // Resolving the deferred in order to know that the rule finished
                                deferred.resolve();
                            });
                        }
                        else {
                            deferred.resolve();
                        }
                    },
                    error: function (qXHR, textStatus, errorThrown) {
                        // Logging errors in case the web service call failed
                        FlowForma.LogDebug('Error getting flow');
                        FlowForma.HandleAjaxQueryFailure(qXHR);
                        deferred.resolve();
                    }
                });
            }
        },
        // This function is called when the rule is being created or opened in flow designer.
        CreateInit: function (action, question, step, container) {
            // Getting the definition that was saved in the flow designer
            var definition = $.parseJSON(action.Definition);
            var pickerValue = null;

            var inpNumber = $(container).find('#inpNumber');

            // Checking if definition exists
            if (definition) {
                // Filling the controls with saved values
                pickerValue = definition.TargetQuestion;
                inpNumber.val(definition.FlowId);
            }

            // Creating the FlowForma picker control (this control allows the user to select steps and questions that are located in the flow)
            var ffPickerControl = FlowForma.Widgets.FlowEntityPicker($(container).find('#ffPicker'), pickerValue);

            // Attaching a function that will be fired once rule saving event will be triggered. [after pressing the save button in the rule configuration dialogue]
            $(FlowForma.FlowContext.Flow).on(FlowForma.Events.Flow.RuleSaving, function (e, args) {
                // Creating an object that will store all of the data that is needed for the rule
                var actionData = {};
                // Reading the data from the controls and setting it to the data object
                actionData.TargetQuestion = ffPickerControl.Value();
                actionData.FlowId = inpNumber.val();

                // Stringifying and saving the data
                action.Definition = JSON.stringify(actionData);
            });
            //#endregion
        },
        ExpandUndo: false
    }
});
  1. Rule name: has to match between both the js and html files because it is used for loading the files
  2. The Execute function
    1. This is where the rule’s core logic resides; it’s the function the Form will look for to execute when the rule is triggered
    2. In this function you can use all of the information that was saved in flow designer while creating the rule
    3. All the information can be accessed through parsing JSON string “action.Definition”
  3. The CreateInit function
    1. This function is executed whenever the rule is opened in the Flow Designer
    2. It is meant for loading the values and initializing widgets (like flow entity picker) in the rule creation mode
    3. FlowForma.Events.Flow.RuleSaving event is meant for saving all the information that was filled in during the rule creation process, on Flow Designer

The HTML file

<script type="text/x-handlebars-template" id="CreateTemplate">
    <table class="ruleEditTbl">
        <tr>
            <td class="flowforma-rule-label">
                Flow id:
            </td>

            <td class="flowforma-rule-control">
                <input type="number" id="inpNumber" />
            </td>
        </tr>
        <tr>
            <td class="flowforma-rule-label">
                Target question:
            </td>
            <td class="flowforma-rule-control">
                <div id="ffPicker" />
            </td>
        </tr>
        <tr>
    </table>
</script>
All of the HTML that needs to be displayed in a rule has to be wraped by the Handlebars script tag like shown above. (Note that the script has to have and “id=“CreateTemplate” attribute, otherwise it won’t be recognized.

Registering the rule

  1. In order for FlowForma to be aware of you rule, it needs to be registered. Here’s how to do it: Both the .js and the .html files need to be placed on the …/FlowFormaAssets/Templates/RuleActions folder; the only way to access it, it by appending the path to the your site’s address, on the browser.
    1. If your site’s address is https://acme/sites/hr/flowforma, you can access the FlowFormaAssets folder through https://acme/sites/hr/flowforma**/flowformaassets**
  2. Once the .js and the .html files have been copied, the Manifest file, located …/FlowFormaAssets/Templates, needs to be edited, and it should look like similar to this
    {
         "Version": "4.0",
         "QuestionTypes": [],
         "RuleActions": [
         {
              "Type": "rulex",
              "Title": "Just another rule action",
              "FileName": "ruleactions/rulex.html",
              "Category":"Demo"
         },
    {
              "Type": "sampleruleaction",
              "Title": "Sample rule action",
              "FileName": "ruleactions/sampleruleaction.html",
              "Category":"Data Integration"
         }
    ]
    }
    
  3. Your rule definition goes into the RuleActions array; the following need to be present:
    1. Type: name of the rule that is extended to flowforma rules
    2. Title: title that will be displayed in the flow
    3. FileName: html file name
    4. Category: defines under which category rule will be placed in flow
  4. The order by which the rules - and you can have several, separated by ”,” - are added determines the order in which they are displayed on the Flow Designer
  5. Once you reopen the Flow Designer, your rule will be ready to be used

Further information

Check out the HideShowStep rule as a reference - hideshowstep.zip