Breaking The Rules: A Dive Into The Nintex Forms Rule System

  • 23 January 2023
  • 0 replies
  • 471 views

Userlevel 5
Badge +20

Introduction -

 

I have been wanting to make this blog post for a little while, and even though (due to time constraints) it probably will not be as thorough as I had originally planned, it will ultimately serve the purpose of introducing the community to a few advanced approaches to using the Nintex Form rule system. Though you will need a comfortable understanding of javascript to get the most benefit from this, but if you're curious and aren't keen on programming, don't let that stop you from learning! 

 

The Rule system, as it stands, is a good tool to validate control inputs and to alter the appearance of controls based on the Form's state. However, as I found myself enjoying their usefulness, I also found myself wishing that I could do other simple and useful things with them such as populating a field based on the amount of choices, normalizing the inputs of a field, or doing some type of advanced validation based on wild requirements. 

 

Though I cannot account for all of the ways that you might want to use the rule system, I can at least teach the fundamentals of how to use them in a more explicit / specific way, that will hopefully provide you more opportunity to develop form logic that would otherwise be tedious. 

 

On The Surface -

 

The Basics

 

The Rule system is broken up into several types of rules but the two that are presented via the Rules button inside of the Form Editor are Validation and Formatting [1].

 

Note [1]: If you are using the Responsive Form, there is a new type of Rule that is sometimes called an 'Action' rule, but is listed as a 'Set Value' rule. For all intents and purposes, this rule behaves in the same way that a Formatting rule does, but then goes on to evaluate whatever value is listed inside of the 'Value' formula dialog.

 

Validation Rules - are rules that will run, typically, whenever the control they are placed on has been changed or updated in some capacity, and also create a <span> element in the html of the form that is nested under the control in question. That span element can be thought of as the "physical" manifestation of the rule and contains references to the target control along with information about how the rule should be evaluated and the randomly generated rule name that is created each time you create a new rule. 

Formatting Rules - are rules that will run, typically, whenever a control that they have referenced has been changed or updated in some capacity. They do not create an element in the html tree. 

 

The Rule Editor - is the interface that allows you to Create, Delete, Setup, and otherwise Manipulate all of the Rules for the Form and its Controls. In Classic Form Mode it looks like: 

 

While in Responsive Form Mode it looks like: 

 

On The Differences Between Editors: The biggest changes between the two visually are the terms used to label each section, and the fact that the Responsive Forms Rule Editor has removed the Rule Type and replaced it with the ability to choose a single direct outcome (which can be selected in the 'Then' drop-down

 

Rule Conditions are the logical circumstances that will dictate whether or not the Rule Outcome is applied. Rule Conditions are set up inside of the Condition dialog box in the Classic Form Rule Editor:


And inside of the When dialog box in the Responsive Form Rule Editor: 

 

Rule Outcome, while not an actual term that is used in the Rule Editor, this is the 'result' of what will happen in the event that your evaluated Rule Conditions resolve to true. In the Responsive Form Rule Editor, this could be thought of as whatever you have selected inside of the 'Then' drop-down. 

 

On Rule Outcomes and Conditions: Whatever you'd like the outcome to be, whether it is invalidation, formatting, disablement, or hiding, the conditions that you write for the rule to evaluate must resolve to a Boolean true.

 

The Beyond

 

Referenced Named Controls - (the red link-like references [2] that you see inside of the formula dialog ) are replaced when the form is 'live' (as in being Previewed, Viewed, Edited, or invoked as a New form) with the following example code: 

NWF.FormFiller.Functions.GetValue('ad065df4-c555-44f7-aebe-5491cabcbd2c', sourceContext);‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

(Note [2]: While it is true that referencing other controls inside of the rule condition builder dialog will often result in the red link-like reference being inserted, when referencing the current control, you can type out the plain-text equivalent {Control:Self} (yes - with the curly brackets), and it will be replaced just the same. In many of my own examples, you will see me use the long-form version {Control:Self} as it can be easily copy / pasted without needing to insert it manually as you do for each of the other Named Controls)

 

Referenced Runtime Functions - are replaced when the form is 'live' with the full calls to their location inside of the NWF namespace as shown by the following example:

NWF.RuntimeFunctions.isNumeric.call(nullOrContext, someValue)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

On Reference Replacement: The way that replacement works for both Control References, RunTime Functions, etc. is that it looks for a particular expression and attempts to 'remap' it if it recognizes it. For Referenced Controls it is looking for {SomeProperty:SomeValue}, whereas for Runtime Functions it is looking for FunctionName(). 

 

The RunTime Function replacements can be incredibly confusing and frustrating if you are trying to use a Native or jQuery (NWF$) function with an identical name, as it will be replaced with a call to the function that is native to the NWF object. 

 

If you write out: 

NWF$.isNumeric("444");‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

During run time it will be replaced like:

NWF$.NWF.RuntimeFunctions.isNumeric.call(someContext, "444");‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

Which will result in an error. You can sidestep this 'correction' by making your code a little sloppier. Putting a space between the function name (in this case 'isNumeric') and the open parenthesis for the arguments, it will fail to meet the conditions for a function that should be evaluated / replaced, and will be left alone. 

 

NWF$.isNumeric ("444");

// Yay! No error!‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

whew...

 

The Helpers

 

There are several Nintex created variables and functions that can help with the manipulation and utilization of the Rule System. Though I will not be covering them all, I do feel like these are the most useful for the majority of users

 

Variables

 

  1. Rules: All rules can be found using the 'Rules' variable, which returns an array of Objects.
     
  2. Page_Validators: All Validation rules can be found using the 'Page_Validators' variable, which returns an array of <span> elements. 

 

Functions

 

  1. NWF.FormFiller.Functions.ProcessOnChange(someControl): This function should receive a Form Control as the argument:
    NWF$("#" + someValue).val("Hello World");

    NWF.FormFiller.Functions.ProcessOnChange(NWF$("#" + someValue));‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

    and will evaluate any 'on change' rules that have yet to fire on the target control. Because you can change the value of a control without triggering the events that would normally be triggered to evaluate the rules, this is a handy way of getting that done. It should be noted though that most of the time you can accomplish the same thing by triggering the "change" or "blur" event manually using jQuery: 
    NWF$("#" + someValue).val("Hello World").trigger("blur");
    // Same results as above!‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

     

  2. Page_ClientValidate(): This function takes no arguments but will force the entire form to evaluate all of the Validation Rules upon execution. This is incredibly helpful in the event that you'd like some type of two stage process (for instance, a button that creates a confirmation "Are you sure you wanna submit this?" dialog which must be interacted with before submission) to test the page validity before submitting it to SharePoint. 

    This does however come with a few quirks. Firstly, it doesn't actually apply any of the invalidation styles that would normally get applied had the form been submitted proper. Secondly, it does create the top warning dialog that lists all of the current invalidation messages. 

    That being said, Caroline Jung created a handy little function that can apply those invalidation css classes to your controls right over here: https://community.nintex.com/thread/11554#comment-75684 
     
  3. NWF.FormFiller.Functions.GetParentContext(someControl): So... This function doesn't exactly do anything in terms of Validation, however it is incredibly useful for getting what is often called the 'sourceContext'. While the sourceContext is always passed into a Rule of any type, knowing how to get a correct context to any control can be helpful in developing your own functions and system that work with or sit on top of the existing Nintex Forms Rule system in the future.

    All a 'Context' is, is simply the closest 'containing' control that the target control is inside of. The context of a control that is inside of a Repeating Section is the row of the repeating section that particular control is inside of. However the context of a control that is just on the form proper, would be the form proper! 

Down The Rabbit Hole -

 

The Evaluated Conditions

 

While it is easy to suspect that things being evaluated inside of form Rules are special functions and value references, in reality, what's being evaluated is nothing more than plain ol' JavaScript [3][4]. As shown above, all of the references to named controls and functions are replaced at run time and evaluate accordingly. Because of that, we can actually do some really interesting things. This is especially true when we start to consider the usage of Immediately Invoking Function Expressions (simply known as IIFE. More info about those can be found here: An Introduction to IIFEs - Immediately Invoked Function Expressions - A Drip of JavaScript). 

 

Note [3]: Rules aren't the only things that evaluate javascript. The formulas for the Calculated Control can also evaluate JS.

 

Note [4]: So long as the return value of whatever it is you are evaluating as the Rule Conditions returns either a true or false boolean value, it will 'drive' the rule. This could be as simple as writing: 

 

true‍‍‍‍‍‍‍‍‍‍‍‍‍

 

Yes. It's just one word, but that would make the Rule apply either its formatting, invalidation, or action (in the case of responsive forms) from the very beginning and stay that way throughout the form session.

 

Why might this knowledge be useful? Well, to most people who aren't comfortable with javascript, it isn't! However, for people who find it easier to think using the functions that are either exposed via the DOM's api or jQuery, it can be a lot faster to prototype in a text editor (Sublime Text, Brackets, VS Code, Notepad++, etc. etc.) and then copy paste that into a Rule's condition area rather than to sit there and type it all out inside of a html rendered <textarea> element which is a terrible place to type code. 

 

Consider the following (terrible) Rule. Let's say that we want a specific Single Line Text control to always normalize the value inside of it to "0.00" if it's blank, or to the second decimal point if it isn't (so - 1.00, or 4.31, but never ever 4.311): 

 

(function(controlValue, myControlID) {
  "use strict";

  // Using our passed in control's ID in a variable
  // we'll set the variable internalElement to
  // our control.
  var internalElement = NWF$("#" + myControlID);

  // If the value of the control isn't empty...
  if (internalElement.val()) {

    // Then we'll need to test if it's Numeric
    // Notice that I am using the Nintex Forms function
    // of isNumeric rather than the jQuery function.
    if (isNumeric(internalElement.val())) {

      // If it is Numeric, then we can split at the decimal point delimiter
      var splitValue = String(internalElement.val()).split(".");

      // Test to see if there is anything in the integer portion
      // *that is, the value on the left side of the decimal point*
      // If there is a value. Keep it.
      // If there isn't. Set it to 0.
      var intPart = (splitValue[0]) ? splitValue[0] : "0";

      // Then we'll take the value to the right of the decimal point
      // If it exists...
      //
      //    Then we'll check the length of that value.
      //
      //      If it's under 2 then we'll add a 0 to the end of it.
      //
      //      If it's above or equal to 2 then we'll truncate off the numbers
      //      beyond the second decimal place.
      //
      // If it doesn't exist we'll just set it to "00"
      var fractionPart = ((splitValue[1]) ? ((splitValue[1].length < 2) ? splitValue[1] + "0" : splitValue[1].slice(0, 2)) : "00");

      // Then we'll put both halfs back together
      var joinedValue = intPart + "." + fractionPart;

      // And we will set the control to that value
      internalElement.val(joinedValue);
    } else {
      internalElement.val("0.00");
    }
  } else {
    // But if it is empty, we'll set the value to "0.00"
    internalElement.val("0.00");
  }

  // We're not actually doing anything with this rule other than
  // what is the equivalent of an event handler, so we don't
  // even need the rule to apply anything to the control.
  // Because of that, we can always return false.
  return false;

}({Control:Self}, myControlID));

// We'll pass in the Value of the Control
// and the javaScript ID of the Control‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

Alright. That does what we want... but as mentioned in the code comments. It's not really any more flexible than just setting up and using a standard Event Handler. 

 

Currently the only advantage over using an event handler is that you don't have to target your controls... and speaking of which... wouldn't it be awesome if you could reference the target control of a rule from within the rule itself? 

 

Self Referencing Part 1 - A New Hope

 

Being able to reference the control that the rule is currently evaluating on is what would ultimately make the rule system a wonderful tool to use... But there is no immediately obvious way to do this from the onset. 

 

While the above example code will only work for the control that has the javascript ID exposed (through the control properties) as the variable 'myControlID', we could make a 'universal' rule if we were only able to capture the control that was being referenced by the rule that was executing our code. 

 

Luckily the rule system provides a few bits of information that can help us achieve a self-reference, however, before we get into what those are, it is important to know how Rules are represented as javascript. 

 

Every rule, which is named dynamically upon the building of the Form essentially looks like this: 

function fnSomeVeryLongGUIDThingHere(sourceContext, rowIndex){
  return

0 replies

Be the first to reply!

Reply