N M

Documentation of Changes To JavaScript for SP 2016 Forms 4.3.0.11

Discussion created by N M on Dec 8, 2017
Latest reply on Jan 8, 2018 by N M

I recently upgraded to the newest version of Nintex for SharePoint 2016 in an attempt to see if it would correct a few other issues I was having, but failed to respect the fact that the Nintex Forms javascript code-base was updated... resulting in a LOT of broken things. 

 

This was totally my fault, but the frustration of trying to figure out what in the heck was changed and where has made me think that creating some type of community documentation of any found changes might be worth while. 

 

I know that the changes I'm listing here are not going to be useful / used by a lot of people, but if you happen to come across any changes in your own work, and subsequent workarounds / fixes, please share them here so that we can create some type of log. 

 

♥♥♥ If you're interested in seeing which changes are happening between version, and would like to make that a regular part of the patch notes, consider adding a few votes to my suggestion herePublish JavaScript Change Logs In Patch Notes, or Put Nintex Forms On Github! – Customer Feedback for Nintex ♥♥♥

 

I'll do my best to keep this organized. Instead of just listing the functions that have changed, I'd like to stick to listing the affected controls being that not all of this is relevant at any given time and controls are good way to gauge which things you should be concerned about / looking for, if needed. 

 

Please add your finds in the comments if you have any

 

Thank you!  

 

CLASSIC FORMS:


Controls:

Repeating Section

 

NWF.FormFiller.Functions.AddNewRepeaterRow: Added the code 

if (!isResponsive && NF.LookupHelper && NF.LookupHelper.AdjustCheckBoxRadioButtonListHeight) {
  NF.LookupHelper.AdjustCheckBoxRadioButtonListHeight(newRepeaterRow);
} else if (!isResponsive && !NF.LookupHelper) {
  NF.BaseDataAccessHelper.AdjustCheckBoxRadioButtonListHeight(newRepeaterRow);
}


It does not have error handling in the event that NF.BaseDataAccessHelper is undefined! Error results in Row not being added correctly. 

 

(In my environment, it the NF object exists, but BaseDataAccessHelper is never created / initialized anywhere...

 

 

(Edit (12/8/2017 11:38am EST): as soon as I posted this... it has since started generating... Nothing has changed, but now it works... because.)

 

(Edit (12/8/2017 7:06pm EST): Looks like I'm still having a LOT of trouble with getting this resource to load correctly. For whatever reason, it will be called and loaded in my main form, but I have not gotten it to automatically work on any other Nintex Form on the site. 

 

Because of this I have had to create a workaround that I can throw at the beginning of every form's JS to ensure that the NF namespace is being populated with the things it needs to make Repeating Sections work. It is as follows: 

 

if (NF.BaseDataAccessHelper === undefined) {
    NWF$.getScript(_spPageContextInfo.siteAbsoluteUrl.replace(/sites.+/, "") +
      _spPageContextInfo.layoutsUrl +
      "/NintexForms/JavaScriptStringHandler.ashx?" +
      "resourceType=jsfile&" +
      "fileName=NF.BaseDataAccessHelper.js&" +
      "culture=" +
      _spPageContextInfo.currentCultureName);
  }

 

I tried to make it as universal / generic as I could in the event someone else runs into this issue... bleh. ) 

----------------------------------------------------------------------------------------------------------------------------------

 

The line: 

NWF.FormFiller.Functions.RepositionAndResizeOtherControlsAndFillerContainerHeight(currentControl, heightIncrease, heightIncrease, formFillerDivCurrent);


Has been changed to: 

NWF.FormFiller.Resize.RepositionAndResizeOtherControlsAndFillerContainerHeight(currentControl, heightIncrease, heightIncrease, formFillerDivCurrent);


subsequently, you will need to change any calls to RepositionAndResizeOtherControlsAndFillerContainerHeight from the old way 'NWF.FormFiller.Function..." to "NWF.FormFiller.Resize...."

----------------------------------------------------------------------------------------------------------------------------------

 

Calculated Control


Calculated Controls received an update to when they are actually updated. In previous versions (4.2.2.0 guaranteed) , you could return a value from an Immediately Invoke Function Expression (see: IIFE - Glossary | MDN ), and the return value would be used to update the Calculated Control. 

 

This has since been changed (though I do not know precisely which version after 4.2.2.0 it occurred), and now the function that handles updating the control (NWF.FormFiller.Functions.ProcessCalculation) includes a few checks to make sure that the control is not inside of a repeating row section that is still hidden, and that the target control has some jQuery data attached to it called "GetControl". 

 

An example of what used to work: 

// Old
(function (sourceContext, rowIDClass) { 

  // Get the Calc Control's Value
  var rowIDValue = sourceContext.find("." + rowIDClass + " input").val();

  // If Control has no value...
  if (!rowIDValue) {

    // Create a new value of "Hello World"
    rowIDValue = "Hello World";

    // Notify what the new value will be.
    alert("setting Control's Value to: " + rowIDValue);
  }
 
  // Returned the value, and watch it be applied
  return rowIDValue;
}(sourceContext, "internalRow"))


However, using that code now will result in the alter showing (3) times before the value is actually set. 

 

You'll need to change your code to something like this, to ensure that you are only doing things once:

// New
(function (sourceContext, rowIDClass) { 

  // Get the Calc Control's Value
  var rowIDValue = sourceContext.find("." + rowIDClass + " input").val();
 
  // Get the Topmost Div of our Calculated Control
  var resultControl = sourceContext.find("." + rowIDClass + ".nf-filler-control");
 
  // noControl = true if the .data("GetControl") is undefined
  var noControl = resultControl.data("GetControl") === undefined;

  // If our Calc Control is empty
  // And if noControl is **false** (meaning that
  // .data("GetControl") *does* exist)...
  if (!rowIDValue && !noControl) {
   
    // Create a new value of "Hello World"
    rowIDValue = "Hello World";

    // Notify what the new value will be.
    alert("setting Control's Value to: " + rowIDValue);
  }
 
  // Return either our populated value
  // or the original Calulated Control's Value
  // if noControl === true
  return rowIDValue;
}(sourceContext, "internalRow"))

 

This is particularly important if you're updating other Controls from within the formula of a Calculated Control! 

 

Calculated Controls that rely on other Calculated Controls, particularly the ones that reside inside of a Repeating Section, will ALSO be affected by this new change! 

Consider a sum() Calculated Control that sits inside of a Repeating Section's Row called Row Total, and a Calculation Control that sits on the Form proper called Section Total, which adds up the value of whatever each Row Total adds up to. 

 

When you add a new row, the Row Total Control inside of the Repeating Row will distribute it's undefined value out to the Section Total control. You could end up with a passed in array of something like "[300, 440,]" (notice the extra comma with no value behind it). Depending on how you are handling this data, it could lead to bad times. 

 

Be careful out there if you're rolling your own way of handling money! 

----------------------------------------------------------------------------------------------------------------------------------

 

Input Controls (Single Line Text & More):

 

 

The Event Handlers for all Input Controls that have the attribute type set to "text" have been changed and will require you to update any code that is pushing values to an input element if you want the change in value to be noticed by any Calculated Formula, Rule, or Script where that control has been referenced.

The way events were attached to input controls in version 4.2.2.0 is as this truncated example shows: 

// Old Version (4.2.2.0) from FormFiller.js, Copyright Nintex,
// but modified / truncated for clarity
NWF.FormFiller.Functions = function() {

  var formFillerDivCurrent = NWF$("#formFillerDiv");

  return {
    AttachOnChangeEvents: function(formFillerDivCurrent) {
     
      // Input:Text Handled Here!!!
      formFillerDivCurrent.on('blur', 'input:text', function(event) {
        NWF.FormFiller.Functions.ResetCircularReferenceRelatedVariables();
        NWF.FormFiller.Functions.ProcessOnChange(NWF$(this));
      });
      formFillerDivCurrent.on('change', 'input:radio', function(event) {
        NWF.FormFiller.Functions.ResetCircularReferenceRelatedVariables();
        NWF.FormFiller.Functions.ProcessOnChange(NWF$(this));
      });
      formFillerDivCurrent.on('change', 'input:checkbox', function(event) {
        NWF.FormFiller.Functions.ResetCircularReferenceRelatedVariables();
        NWF.FormFiller.Functions.ProcessOnChange(NWF$(this));
      });
      formFillerDivCurrent.on('input propertychange blur change', 'textarea', function(event) {
        NWF.FormFiller.Functions.ResetCircularReferenceRelatedVariables();
        NWF.FormFiller.Functions.ProcessOnChange(NWF$(this));
      });
      formFillerDivCurrent.on('change', 'select', function(event) {
        NWF.FormFiller.Functions.ResetCircularReferenceRelatedVariables();
        NWF.FormFiller.Functions.ProcessOnChange(NWF$(this));
      });
    }
  };
};


However. In version 4.3.0.11 the code has been changed to: 

// New Version (4.3.0.11) from FormFiller.js, Copyright Nintex,
// but modified / truncated for clarity
NWF.FormFiller.Functions = function() {

  var formFillerDivCurrent = NWF$("#formFillerDiv");

  return {
    AttachOnChangeEvents: function(formFillerDivCurrent) {

      // Input Text Handled Here!!!
      formFillerDivCurrent.on('focus', 'input:text', function(event) {
        NWF$(this).data("OldValue", this.value);
      });
      formFillerDivCurrent.on('focus', 'textarea', function(event) {
        NWF$(this).data("OldValue", this.value);
      });

      // Input Text ALSO Handled Here!!!
      formFillerDivCurrent.on('blur', 'input:text', function(event) {
        if (NWF$(this).data("OldValue") !== this.value) {
          NWF.FormFiller.Functions.ResetCircularReferenceRelatedVariables();
          NWF.FormFiller.Functions.ProcessOnChange(NWF$(this));
        }
      });
      formFillerDivCurrent.on('change', 'input:radio', function(event) {
        NWF.FormFiller.Functions.ResetCircularReferenceRelatedVariables();
        NWF.FormFiller.Functions.ProcessOnChange(NWF$(this));
      });
      formFillerDivCurrent.on('change', 'input:checkbox', function(event) {
        NWF.FormFiller.Functions.ResetCircularReferenceRelatedVariables();
        NWF.FormFiller.Functions.ProcessOnChange(NWF$(this));
      });
      formFillerDivCurrent.on('input propertychange blur change', 'textarea', function(event) {
        if (NWF$(this).data("OldValue") !== this.value) {
          NWF.FormFiller.Functions.ResetCircularReferenceRelatedVariables();
          NWF.FormFiller.Functions.ProcessOnChange(NWF$(this));
        }
      });
      formFillerDivCurrent.on('change', 'select', function(event) {
        NWF.FormFiller.Functions.ResetCircularReferenceRelatedVariables();
        NWF.FormFiller.Functions.ProcessOnChange(NWF$(this));
      });
    }
  };
};

 

As you can see from the new version, the blur event, when handled, will check the jQuery Data (see: jQuery.data()) Object's property of "OldValue" to compare it to the current value of the element. However, that Data Object is set in the newly introduced focus event handler, which will set the data property to the value present in the element at the moment of focus

 

This can cause problems if you are setting the value of an input in an unorthodox way (via a function, rule, or control formula) because without calling the focus on the element before altering its value, the 'OldValue' property will not be set, which could lead to the control being inaccurately handled (to the developer) during the blur event.

 

To correct for this change. Use the following example code to set your input text fields:

(function () {
  var inputControl = NWF$("#" + singleLineTextControlID);   
  inputControl.trigger("focus").val("").trigger("blur");
}())

 

This will...

 

First, trigger the focus event, setting the OldValue property to whatever is contained as the value of the input

 

Second, it will update the current value to whatever you'd like (whatever you put inside of the val("") function).

 

Third, will then trigger the blur event, which will compare the OldValue vs. the current (new) value, which will, if they are not equal, invoke the functions to update any dependent controls where this input is referenced. 

 

----------------------------------------------------------------------------------------------------------------------------------

Outcomes