nmarples

The Big Event: A Closer Look At Nintex Form Events

Blog Post created by nmarples on May 1, 2018

Intro

 

The topic of the Nintex Form Events has been covered before, most notably in the document located here: https://community.nintex.com/docs/DOC-1217

 

However, there are a few shortcomings and errors with this document undoubtedly because the Official SDK Documentation Help Page contains a lot of erroneous information (https://help.nintex.com/en-us/sdks/sdk2013/FormSDK/Topics/SDK_NF_CON_NF_Events.htm):

 

Though it would be easy to make an almost identical Blog or Document containing the corrected information, I wanted to take the time to delve a little deeper into how Nintex Forms fires its custom Events and the options it presents to you as a developer.

 

I should also take this time to get one of the bigger caveats of this blog out of the way. This is mainly for the Classic Forms and not the Responsive Forms. While I will get into how you can utilize them in the Responsive Forms, it's a HUGE hassle and ultimately isn't supported by Nintex... sadly. Until that is the case, it won't be particularly useful to that environment .  Actually, scratch that. No time to get into how to do this for Responsive Forms without making this a giant book. Sorry Responsive Users

All that aside… let’s get into it!

Brief Events Refresher

 

For the uninitiated, an event could be thought of as a notification. While Events are not a native part of the JavaScript Language, they ARE a part of the API presented to us by web browsers (specifically the DOM api), and because Nintex Forms are presented to us via the web browser, we have access to all of those events.

The special thing about Events is that you, as a developer, get to choose when you would like to or not like to pay attention, or ‘handle’, the event in question. Some of these exposed events may be familiar to you already. Most people who have done a little JavaScript have probably used or seen the ‘onclick’ event, the ‘change’ event, or the ‘blur’ event to name a few.

When you handle an event, such as ‘onclick’, you are saying to the browser, “Whenever someone clicks *some* element, I want to execute a chunk of code”.

 

Fundamentally it is this simple, but to learn more in depth please read: Introduction to events - Learn web development | MDN 

 

Nintex Form "Events" Primer

That being said, the 'Events' exposed to us in Nintex Forms are not like the events exposed to us by the DOM api / Web Browser. Instead, Nintex Forms Events are more like checkpoints. With a normal native event, you can call any of the exposed functionality built around them to alter the way that event is handled (see: Event.stopPropagation() - Web APIs | MDN). Nintex Form Events are actually called linearly just like any other function / expression, by the code that is executed whenever you interact with the Form in some meaningful way. 

 

You could think of the flow how Nintex Form Events work as being 

 

Native DOM Event (like "onclick") =>

Nintex Forms Processing Functions =>

Nintex Forms 'Starting Event' =>

Any Code That Was Passed Into That Particular "Event's" Collection =>

Continuation  of Nintex Forms Processing Functions =>

Nintex Forms 'Outro Event' => 

That Was Passed Into That Particular "Event's" Collection =>

Done

 

To give a more specific example, let's look at how the Add New Row button works on a Repeating Section. 

 

  1. User "CLICKS" on the Add New Row image. 
  2. This initiates / emits the (native) "onclick" event.
  3. When the Form was rendered, an Event Handler was setup on the form to handle this specific 'click' event on this specific image / link. The handler is as follows (courtesy of Nintex):
    formFillerDivCurrent.on('click', '.nf-repeater-addrow-link', function(event) {
      var addrowImage = NWF$(this);
      var thisRepeaterControl = NWF$(NWF$(addrowImage.closest('.nf-repeater'))[0]);
      NWF.FormFiller.Functions.BulkCalculationExecution = true;
      NWF.FormFiller.Functions.UseServerValueForCalculations = true;
      NWF.FormFiller.Functions.AddNewRepeaterRow(thisRepeaterControl, formFillerDivCurrent);
      NWF.FormFiller.Functions.BulkCalculationExecution = false;
      NWF.FormFiller.Functions.UseServerValueForCalculations = false;
      NWF.FormFiller.Functions.ProcessDeferredCalculations();
    });

    This handler is set to fire when you have clicked on an element that has the class "nf-repeater-addrow-link" and, after executing a few functions, eventually executes the AddNewRepeaterRow() function. 

  4. Inside of the AddNewRepeaterRow function lay all of the code to generate a brand new Repeater Row for you!
    If you were to look at the entire AddNewRepeaterRow function as a whole, it would look a lot like: 
    {
      AddNewRepeaterRow: function(thisRepeaterControl, formFillerDivCurrent) {
        var newRepeaterRow;
        var heightIncrease;

        NWF.FormFiller.Events.ExecuteRepeaterRowAdding(thisRepeaterControl);

        /*
          LOTS OF CODE DOING LOTS OF FUN REPEATING SECTION STUFF HERE
        */


        NWF.FormFiller.Events.ExecuteRepeaterRowAdded(newRepeaterRow);
      }
    }

    You'll notice that *around* the code where all of the 'fun repeating section stuff' happens are two lines of code that may seem familiar. 

    Specifically they are the lines: 
    NWF.FormFiller.Events.ExecuteRepeaterRowAdding(thisRepeaterControl);

    And:

    NWF.FormFiller.Events.ExecuteRepeaterRowAdded(newRepeaterRow);


    You may have also noticed that these Executed "events" are named in a particular way. This is the typical* layout of Nintex Form Events. Usually there is an "Intro" call that invokes things at the very beginning of something BEFORE it has even started, and an "Outro" call that invokes your code at the very END of whatever the main function's purpose was.

    In this case the Intro 'event' can be thought of as the RepeaterRowAdding, while the Outro 'event' can be thought of as RepeaterRowAdded.

    When RepeaterRowAdding is invoked...

  5. Any function that you have passed into the NWF.FormFiller.Events.RegisterRepeaterRowAdding() function, will be executed. 

    For instance, if I had the following code in the Custom Javascript section of the Form Settings (Classic Forms): 
    NWF.FormFiller.Events.RegisterRepeaterRowAdding(function(){
      console.log("The Row Hasn't Been Created Yet!"); 
    });


    Then once the ExecuteRepeaterRowAdding function was called during the AddNewReapeaterRow function, you would see the message "The Row Hasn't Been Created Yet!" in the console window of your browser! 



  6. The Same things will happen with the ExecuteRepeaterRowAdded function. Any number of functions that you have passed using the NWF.FormFiller.Events.RegisterRepeaterRowAdded() function will be executed at this time. 

    For instance, if I had the following code in the Custom Javascript section of the Form Settings (Classic Forms):
    NWF.FormFiller.Events.RegisterRepeaterRowAdded(function(){  
      console.log("The Row Has Been Totally Created!");
    });

    Much like before, you would see the console message "The Row Has Been Totally Created!" after the new Repeater Row had been added to the form. 

  7. With everything finished, the other remainder code executes and the user is none the wiser! 

 

Now that you understand how these work a bit more intricately, let's take a look at a list of all the available Nintex Form Events we have available to us! 

 

Available Form Events

 

Below is a list of every available Nintex Forms Event that will fire during the course of Form Initialization (loading) stage when you load up a form. Any arguments passed to the Event Function has been noted as a comment inside of the code example, along with a few console outputs to help you see exactly what is happening.

 

(Note 1: A few terms I use in here are things like "inner-control" or "outermost filler div". If you are unfamiliar with how Nintex Forms generates its HTML for the controls, it can typically be thought of as: 

<div class="nf-filler-control aka:OUTER CONTROL">
  <div class="nf-filler-control-border">
    <div class="nf-filler-control-inner">
      <div class="something-sometimes">
      <probably your INNER CONTROL(S)/>
      </div>
    </div>
  </div>
</div>

 

This is an terrible exaggeration, but anytime that you seem me write something like NWF$(outermost filler div); you know that the jquery object that is returned to you is referencing the outermost layer of the target control and NOT the innermost element which contains that actual VALUE of that control)

(Note 2: You'll notice that I have used colors for the names of the below events. The key is as follows:


Red: Indicates that the event is fired only ONCE during the loading of the Form, and never again.

 

Blue: Indicates that the event is fired MULTIPLE TIMES, but ONLY during the loading of the Form and never after.

 

Plum: Indicates that the event is fired both during the Form Load, and during the Normal Runtime of the Form after the user is able to interact with it.)

 

Lime: Indicates that the event is fired after the Form has been Loaded during Normal Runtime only!)

 

RegisterBeforeReady: This is the first Nintex Form event that fires AFTER all of the custom JavaScript has been loaded into the Form (Classic). This event will ONLY fire ONCE during a Forms Initialization. 

NWF.FormFiller.Events.RegisterBeforeReady(function () {
  console.log("Executing 'RegisterBeforeReady' ");
  /* arguments[0] === undefined */
});

 

RegisterControlProcessing: This event fires any time a Form Control is being processed during the initial generation of the Form. As far as I know, this is the only time this event fires. 

NWF.FormFiller.Events.RegisterControlProcessing(function () {
  console.log("Executing 'RegisterControlProcessing' ");
  console.log("Processing Control: " + arguments[0].closest(".nf-filler-control").attr("data-controlname"));
  /* arguments[0] === NWF$(inner-control) */
});

 

RegisterControlProcessedThis event fires any time a Form Control has completed processing during the initial generation of the Form. As far as I know, this is the only time this event fires. 

NWF.FormFiller.Events.RegisterControlProcessed(function () {  
  console.log("Executing 'RegisterControlProcessed' ");
  console.log("Processed Control: " + arguments[0].fillerDiv.attr("data-controlname"));

  /* arguments[0] === {
    fillerDiv: NWF$(outermost filler div),
    formControl: fillerDiv.children().children().children(),
    formFillerDivCurrent: NWF$("#formFillerDiv")
  }; */

});

 

RegisterControlHeightChangePropagating: This event fires any time a Form Control has to adjust its height because of something other than a Rule, and subsequently pushes that height change to every Form Container, eventually ending with a resize of the Form Canvas itself. When a Form is initializing, the Repeating Section will typically fire this as it generates its hidden 'root' row, and then again for it's 1st visible row. 

NWF.FormFiller.Events.RegisterControlHeightChangePropagating(function () {

  console.log("Executing 'RegisterControlHeightChangePropagating' ");
  console.log("Height Change Propagating For: " + arguments[0].attr("data-controlname"));
  /* arguments[0] === NWF$(outermost filler div); */
});

 

RegisterControlHeightChangePropagated: This event fires after all of the Control's Siblings have been repositioned, and its Parent Containers and Canvas have been resized. This follows the RegisterControlHeightChangePropagating event. 

NWF.FormFiller.Events.RegisterControlHeightChangePropagated(function () {

  console.log("Executing 'RegisterControlHeightChangePropagated' ");
  console.log("Height Change Propagated For: " + arguments[0].attr("data-controlname"));
  /* arguments[0] === NWF$(outermost filler div); */
});

 

RegisterAllControlsProcessed: This event fires after all of the Form Controls have been placed onto the Canvas and are ready to have their Formatting Rules processed. 

NWF.FormFiller.Events.RegisterAllControlsProcessed(function () {

  console.log("Executing 'RegisterAllControlsProcessed' ");
  /* arguments[0] === NWF$("#formFillerDiv"); */
});

 

RegisterControlShowHidePropagating: This event fires whenever a Formatting Rule that would Hide or Show the Control is being processed. This will only fire if the outcome of the rule would actually change the visibility of the Control.

NWF.FormFiller.Events.RegisterControlShowHidePropagating(function () {

  console.log("Executing 'RegisterControlShowHidePropagating' ");
  console.log("Show Hide Propagating For: " + arguments[0].attr("data-controlname"));
  /* arguments[0] === NWF$(outermost filler div); */
});

 

RegisterControlShowHidePropagated: This event, much like RegisterControlHeightChangePropagated, will accompany the RegisterControlShowHidePropagating event as indication that any height changes that would affect any other control, container, or canvas, have finished and control is about to be given back to the user. 

NWF.FormFiller.Events.RegisterControlShowHidePropagated(function () {

  console.log("Executing 'RegisterControlShowHidePropagated' ");
  console.log("Show Hide Propagated For: " + arguments[0].attr("data-controlname"));
   /* arguments[0] === NWF$(outermost filler div); */
});

 

RegisterRuleProcessed: When a Formatting Rule that Hides / Shows a Control has been successfully propagated, this event will fire indicating that the Rule has been fully processed. This is one of the few events that has no 'Intro' sibling so there is no way to know beforehand when a Rule is 'processing'. 

NWF.FormFiller.Events.RegisterRuleProcessed(function () {

  console.log("Executing 'RegisterRuleProcessed' ");
  console.log("Processed Rule For: " + arguments[0].resultControl.attr("data-controlname"));
  console.log("Hidden Status: " + arguments[0].hide);
 
  /* arguments[0] === {
    hide: Boolean(),
    resultControl: NWF$(outermost filler div)
  }; */

});

 

RegisterBeforeFillerVisible: This event fires right before the Controls on the Form Filler become visible to the User. 

NWF.FormFiller.Events.RegisterBeforeFillerVisible(function () {
  console.log("Executing 'RegisterBeforeFillerVisible' ");
  /* arguments[0] === undefined */
});

 

RegisterAfterReady: This event fires once the Form Filler becomes visible (that is, after 'RegisterBeforeFillerVisible'), and is the LAST event to fire after a form loads and is usable! 

NWF.FormFiller.Events.RegisterAfterReady(function () {
  console.log("Executing 'RegisterAfterReady' ");
  /* arguments[0] === undefined */
});

 

RegisterRepeaterRowAdding: This event fires any time the user clicks on the 'Add New Row' link / image, or the click event is trigged on said link / image. This event takes place BEFORE any of the New Row has been generated! 

NWF.FormFiller.Events.RegisterRepeaterRowAdding(function () {
 
  console.log("Executing 'RegisterRepeaterRowAdding' ");
  console.log("Repeater Row Being Added To: " + arguments[0].closest(".nf-filler-control").attr("data-controlname"));
  /* arguments[0] === NWF$(Row Container ".nf-repeater"); */
});

 

RegisterRepeaterRowAdded: This event fires AFTER a New Row has been added to a Repeating Section.

NWF.FormFiller.Events.RegisterRepeaterRowAdded(function () {
 
  console.log("Executing 'RegisterRepeaterRowAdded' ");
  console.log("Repeater Row Added To: " + arguments[0].closest(".nf-filler-control").attr("data-controlname"));
  console.log("Repeater Row Added: " + arguments[0].attr("id"));
  /* arguments[0] === NWF$(currentRow);*/
});

 

RegisterControlResized: This event, as far as I can tell, will only fire in-between the RegisterRepeaterRowAdding and RegisterRepeaterRowAdded events, and ONLY when a control that is inside of the New Row is hidden (by way of a rule or otherwise) at the time that the row is being generated. 

 

So if you had a control inside of a Repeating Section that was hidden until the user did something to a different control on the form, or in that repeating section, if that control's state was set to hidden upon the clicking of the Add New Row button, then this event would fire. 

NWF.FormFiller.Events.RegisterControlResized(function () {

  console.log("Executing 'RegisterControlResized' ");
  console.log("Control Resized For: " + arguments[0].formControl.closest(".nf-filler-control").attr("data-controlname"));
  console.log("Resize Height Difference: " + arguments[0].heightDifference);
 
  /* arguments[0] === {
      formControl: NWF$(outermost filler div).children().children().children(),
      heightDifference: Number()
    }; */

});

 

RegisterRepeaterRowDeleting: This event is fired when the user clicks on the 'X' of a Repeating Section Row to delete the row, BEFORE the Row has been deleted. It should be noted that, by default, this event will fire EVEN IF you are clicking the delete X on the only available row, meaning that if you have any code attached to this to do something, you'll need to be careful and handle the actual 'click' event on the delete button itself to make sure that the Row in question isn't the ONLY row! Otherwise, you're going to have a bad time. 

NWF.FormFiller.Events.RegisterRepeaterRowDeleting(function () {

  console.log("Executing 'RegisterRepeaterRowDeleting' ");
  console.log("Repeater Row Being Deleted From: " + arguments[0].closest(".nf-filler-control").attr("data-controlname"));
  console.log("Repeater Row Being Deleted: " + arguments[0].attr("id"));
  /* arguments[0] === NWF$(currentRow); */
});

 

RegisterRepeaterRowDeleted: This event is fired AFTER a Row has been successfully deleted.

NWF.FormFiller.Events.RegisterRepeaterRowDeleted(function () {

  console.log("Executing 'RegisterRepeaterRowDeleted' ");
  console.log("Repeater Row Deleted From: " + arguments[0].closest(".nf-filler-control").attr(
    "data-controlname"));
  /* arguments[0] === NWF$(Row Container ".nf-repeater"); */
});

 

(Note 3: There are two other Nitex Form Events, 'RegisterBeforeFillerVisibleFinal' and 'RegisterAfterFillerVisible', but I have never had them actually fire or be activated in the normal course of testing. Because of that I have left them out of the above Events)

 

The Order Of Operations

 

As you may have been able to tell already by the above list, the Nintex Form Events follow a fairly linear path when the form is loading. This gives you several opportunities to infer where you are at in terms of the loading process, and can help you to figure out the best places to do certain operations. While most people will probably stick to just using the RegisterBeforeReady and RegisterAfterReady events, it can still be helpful to know how a Form is loaded proper. 

 

Essentially, the order is as follows:

 

  1. All Javascript Code Loaded from the 'Custom Javascript' section of the Settings => Custom Javascript Panel
  2. Each line of 'http' included JavaScript, in order of appearance, listed in the Settings => Advanced => Custom Javascript Includes
  3. RegisterBeforeReady
  4. Native DOM 'ready' Event (or: NWF$(document).ready) 
  5. RegisterControlProcessing (if there are controls)
  6. RegisterControlProcessed (if there are controls)
  7. RegisterControlHeightChangePropagating (if there are controls that load with Height Changes like a Repeating Section)
  8. RegisterControlHeightChangePropagated (if there are controls that load with Height Changes like a Repeating Section)
  9. RegisterAllControlsProcessed (if there are controls that have been processed)
  10. RegisterControlShowHidePropagating (if there are controls that get hidden by way of a Formatting Rule)
  11. RegisterControlShowHidePropagated (if there are controls that get hidden by way of a Formatting Rule)
  12. RegisterRuleProcessed (if a control has a Formatting Rule that was processed)
  13. RegisterBeforeFillerVisible
  14. RegisterAfterReady (Done!) 

 

Other cases where this might be important would be if you are using the Page Viewer control to load in an iFrame of a different page (or Form) onto  your current one. While the iFrame exists upon the initialization of the main Form, the contents of the iFrame are not loaded until AFTER the RegisterAfterReady has fired! If you were to have Nintex Form with a Page Viewer to a different Nintex Form, you would need to wait until the innermost child Window (that is: the embedded iFrame Form) called its RegisterAfterReady event before you could be certain that everything was loaded! 

 

Freebies (AKA: Default Events I Use)

 

I have a few Nintex Form Events that I use in almost every Form. Below are the ones that get the highest usage, and that shouldn't affect anything unless you're already doing major javascript dev work. 

 

  1. Load NF.BaseDataAccessHelper: If you're a version of Nintex Forms which has the Responsive Forms in it, and are sometimes experiencing instances of your Repeating Sections for the Classic Forms not loading their borders correctly, it could be because of this missing library. This is by no means something that most of you should ever need, but for my environment, it's absolutely necessary for most Forms. Oddly, it loads correctly in a few... but there is no rhyme or reason as far as I can tell  

    NWF.FormFiller.Events.RegisterBeforeReady(function () {
      "use strict";

      try {
        if (NF.BaseDataAccessHelper === undefined) {
          NWF$.getScript(_spPageContextInfo.siteAbsoluteUrl.replace(/sites.+/, "") + _spPageContextInfo.layoutsUrl +
            "/NintexForms/JavaScriptStringHandler.ashx?" + "resourceType=jsfile&" +
            "fileName=NF.BaseDataAccessHelper.js&" + "culture=" + _spPageContextInfo.currentCultureName);
        }
      } catch (error) {
        console.log("Unable to load NF.BaseDataAccessHelper");
      }
    });
  2. Fix Canvas Resizing Issues: This is actually (4) different Nintex Form Events. Because all of the internal code that resizes the Form Canvas incorrectly does so, I have had to implement a low cost and easy solution so that I can resize controls to my heart's delight. By using these four Events and code therein, any control size change that results in the Canvas being resized will not ruin your form! 
    NWF.FormFiller.Events.RegisterControlShowHidePropagating(function () {
      "use strict";
      outerDiv.data("outerDivHeight", outerDiv.height());
    });

    NWF.FormFiller.Events.RegisterControlShowHidePropagated(function () {
      "use strict";
      if (arguments[0].data("RepositionControls") === true && outerDiv.data("outerDivHeight") !== outerDiv.height()) {
        outerDiv.outerHeight(outerDiv.height());
        outerDiv.data("outerDivHeight", outerDiv.height());
      }
    });

    NWF.FormFiller.Events.RegisterControlHeightChangePropagated(function () {
      "use strict";
      outerDiv.outerHeight(outerDiv.height());
    });

    NWF.FormFiller.Events.RegisterAfterReady(function () {
      "use strict";
      outerDiv.outerHeight(outerDiv.height());
    });

     

  3. Fix Validation Rules on Repeating Section: Since SharePoint 2013, there seems to be an issue with how the Validation Rules are attached to the controls on the New Row of a Repeating Section. This is, as far as I know, the easiest way to correct the problem so that your controls will validate (and more importantly, style!) across your entire form as one would expect! 
    NWF.FormFiller.Events.RegisterRepeaterRowAdded(function () {
      "use strict";
      ValidatorOnLoad();
    });
  4. Prevent the Delete Row Button From Firing the RegisterRepeaterRowDeleting Event: As mentioned above, by default, the Nintex Form Event 'RegisterRepeaterRowDeleting / RegisterRepeaterRowDeleted' will fire ANY TIME you click on the Delete Row button even if there is only (1) Row, and nothing to delete! This can cause all sorts of problems if you have code running when those events are fired. While this isn't a normal Nintex Forms Event, it is an actual DOM event, and because of that, we can actually prevent that code that is normally fired when you click that button from ever happening
    NWF$(".nf-repeater-deleterow-image").on("click", function (event) {
      var targetControl = NWF$((event.target) ? event.target : event.srcElement);
      var targetRow = targetControl.closest(".nf-repeater-row");
      var visibleRows = targetRow.siblings(".nf-repeater-row:not(.nf-repeater-row-hidden)");
      if (visibleRows.length < 1) {
        event.stopPropagation();
      }
    });


 

Outro

 

As you can see, there is a surprising amount of information that can be gained by understanding how the Form is behaving during its creation period and while it's 'live' during runtime. Using the above tools, you too should be able to do more interesting things using your Form, or at least create a sensible approach to untangling the mess of "when should this thing fire?". 

 

If you catch any errors in the above post, or you have additional Event related information to share, as always, leave a comment below so that an edit can be made! 

 

I hope this helps some of your developers out there in Nintex Land! 

Outcomes