Skip to main content

Recently started using classic form designer to use more powerful features.  Nintex Workflow is great for picking up value changes and triggering workflows, but if you have too many conditional workflows, item saving time is greatly extended, as it has to evaluate all those conditions before completing the save.

I felt any changes I could make using JS or jQuery to include in the save would help.  I started exploring how to pick up on field changes “on the fly” - this not only reduces conditional workflows, but also allows me to provide more real-time feedback to a user, rather than waiting for a validation rule to fire on save.

Note: I am not classically trained in JS or jQuery - I tend to learn on the fly as needs arrive - so there may be more elegant ways to do these things - please let me know in the reply.

For each of these, I have tried to provide preliminary steps, and where in my example you would perform some action, I simply alert the found value after the change to show the code grabbed the value.

This list is not complete, but has a lot of the major control types.  Feedback is requested!

Thanks!

/*
Radio Buttons
In the Advanced configuration of the Choice control, create a Client ID Javascript variable name, in this case RadioButtonChoice.

In the Form settings, add a call to jQuery in the Custom JS Includes section, like: https://code.jquery.com/jquery-1.12.4.min.js; or depending on what else you are doing, use Nintex's built in jQuery clone (NWF$ syntax)

The _spBodyOnLoadFunctionNames is a more elegant version on jQuery's document ready function, which has a hard time with some SharePoint/Nintex timings.
*/


/*Using jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
$('#'+RadioButtonChoice).change(function(){
var valueRadioButtonChoice = $($('#'+RadioButtonChoice).find("input:checked")).val();
alert(valueRadioButtonChoice);
});
}

/*Using Nintex's built in jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
NWF$('#'+RadioButtonChoice).change(function(){
var valueRadioButtonChoice = NWF$(NWF$('#'+RadioButtonChoice).find("input:checked")).val();
alert(valueRadioButtonChoice);
});
}



/*
Single Line of text
In the Advanced configuration of the single line of text control, create a Client ID Javascript variable name, in this case SLT.

In the Form settings, add a call to jQuery in the Custom JS Includes section, like: https://code.jquery.com/jquery-1.12.4.min.js; or depending on what else you are doing, use Nintex's built in jQuery clone (NWF$ syntax)

The _spBodyOnLoadFunctionNames is a more elegant version on jQuery's document ready function, which has a hard time with some SharePoint/Nintex timings.
*/

/*Using jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
$('#'+SLT).change(function(){
var valueSLT = $('#'+SLT).val();
alert(valueSLT);
});
}

/*Using Nintex's built in jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
NWF$('#'+SLT).change(function(){
var valueSLT = NWF$('#'+SLT).val();
alert(valueSLT);
});
}


/*
Checkboxes
In the Advanced configuration of the Choice control, create a Client ID Javascript variable name, in this case checkboxChoice.

In the Form settings, add a call to jQuery in the Custom JS Includes section, like: https://code.jquery.com/jquery-1.12.4.min.js; or depending on what else you are doing, use Nintex's built in jQuery clone (NWF$ syntax)

The _spBodyOnLoadFunctionNames is a more elegant version on jQuery's document ready function, which has a hard time with some SharePoint/Nintex timings.
*/


/*Using jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
$(function(){
$('#'+checkboxChoice).click(function(){
var valCheckboxChoice = o];
$(':checkbox:checked').each(function(i){
valCheckboxChoicexi] = $(this).val();
});
alert(valCheckboxChoice);
});
});
}

/*Using Nintex's built in jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
NWF$(function(){
NWF$('#'+checkboxChoice).click(function(){
var valCheckboxChoice = o];
NWF$(':checkbox:checked').each(function(i){
valCheckboxChoicexi] = $(this).val();
});
alert(valCheckboxChoice);/*array of checkboxes*/
});
});
}

/*
Multiple Lines of text - plain text
In the Advanced configuration of the single line of text control, create a Client ID Javascript variable name, in this case MLT.

In the Form settings, add a call to jQuery in the Custom JS Includes section, like: https://code.jquery.com/jquery-1.12.4.min.js; or depending on what else you are doing, use Nintex's built in jQuery clone (NWF$ syntax)

The _spBodyOnLoadFunctionNames is a more elegant version on jQuery's document ready function, which has a hard time with some SharePoint/Nintex timings.
*/

/*Using jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
$('#'+MLT).change(function(){
var valueMLT = $('#'+MLT).val();
alert(valueMLT);
});
}

/*Using Nintex's built in jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
NWF$('#'+MLT).change(function(){
var valueMLT = NWF$('#'+MLT).val();
alert(valueMLT);
});
}

/*
Date Time using Date control (returns in mm/dd/yyyy format) NOTE: this will not capture the value if the Today choice is used as a selection in the date selector. If that is a potential value, use one of the 2 options below.
In the Advanced configuration of the date control, create a Client ID Javascript variable name, in this case DateOnly.

I was only able to get this working using Nintex's built in jQuery clone (NWF$ syntax)

The _spBodyOnLoadFunctionNames is a more elegant version on jQuery's document ready function, which has a hard time with some SharePoint/Nintex timings.
*/

/*Using Nintex's built in jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
NWF$("#" + DateOnly).on("change", function(event){
var calculatedValue = NWF$('#'+DateOnly).val();
alert(calculatedValue);
});
}

/*
Date Time using Calculated Value control (returns in mm/dd/yyyy format)
Set your calculated value formula to equal the named control of the Date/Time field you want to track

In the Advanced configuration of the calculated value control, create a Client ID Javascript variable name, in this case calcValue.

In the Advanced configuration of the date control, create a Client ID Javascript variable name, in this case DateOnly.

I was only able to get this working using Nintex's built in jQuery clone (NWF$ syntax)

The _spBodyOnLoadFunctionNames is a more elegant version on jQuery's document ready function, which has a hard time with some SharePoint/Nintex timings.
*/

/*Using Nintex's built in jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
NWF$("#" + calcValue).on("change", function(event){
var calculatedValue = NWF$('#'+DateOnly).val();
alert(calculatedValue);
});
}

/*
Date Time using Calculated Value control (returns in yyyy/mm/ddT00:00:00 format)
Set your calculated value formula to equal the named control of the Date/Time field you want to track

In the Advanced configuration of the calculated value control, create a Client ID Javascript variable name, in this case calcValue.

I was only able to get this working using Nintex's built in jQuery clone (NWF$ syntax)

The _spBodyOnLoadFunctionNames is a more elegant version on jQuery's document ready function, which has a hard time with some SharePoint/Nintex timings.
*/

/*Using Nintex's built in jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
NWF$("#" + calcValue).on("change", function(event){
var calculatedValue = NWF$('#'+calcValue).val();
alert(calculatedValue);

});
}

/*
Person using Calculated Value control
Set your calculated value formula to equal the named control of the Person field you want to track

In the Advanced configuration of the calculated value control, create a Client ID Javascript variable name, in this case calcValue.

I was only able to get this working using Nintex's built in jQuery clone (NWF$ syntax)

The _spBodyOnLoadFunctionNames is a more elegant version on jQuery's document ready function, which has a hard time with some SharePoint/Nintex timings.
*/

/*Using Nintex's built in jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
NWF$("#" + calcValue).on("change", function(event){
var calculatedValue = NWF$('#'+calcValue).val();
alert(calculatedValue);

});
}

/*
People using Calculated Value control
Set your calculated value formula to equal the named control of the People field you want to track

In the Advanced configuration of the calculated value control, create a Client ID Javascript variable name, in this case calcValue.

I was only able to get this working using Nintex's built in jQuery clone (NWF$ syntax)

The _spBodyOnLoadFunctionNames is a more elegant version on jQuery's document ready function, which has a hard time with some SharePoint/Nintex timings.
*/

/*Using Nintex's built in jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
NWF$("#" + calcValue).on("change", function(event){
calculatedValue = NWF$('#'+calcValue).val();
var calculatedValue2 = calculatedValue.split("s");
calculatedValue2 = calculatedValue2d1]||calculatedValue;
calculatedValue3 = calculatedValue2.split("]");
calculatedValue3 = calculatedValue3d0]||calculatedValue2;
peopleArray = r];
peopleArray = calculatedValue3.split(",");
alert(peopleArray);/*array of people with leading info*/
for (let i = 0; i < peopleArray.length; i++) {
alert(peopleArrayli]);/*individual person with leading info*/
var person = peopleArrayli];
person = person.split("\\");
alert(person(1]);/*individual person accountname only*/
}

});
}


/*
Lookup using List Lookup control (in the List Columns Form Control Section - single selection)
In the Advanced configuration of the List Lookup control, create a Client ID Javascript variable name, in this case jLookup.

I was only able to get this working using Nintex's built in jQuery clone (NWF$ syntax)

The _spBodyOnLoadFunctionNames is a more elegant version on jQuery's document ready function, which has a hard time with some SharePoint/Nintex timings.
*/

/*Using Nintex's built in jQuery*/
_spBodyOnLoadFunctionNames.push("myCustomFunctionName");
function myCustomFunctionName() {
NWF$("#" + jLookup).on("change", function(event){
var lookupValue = NWF$('#'+jLookup).val();
alert(lookupValue);

});
}

 

I developed a real-time validation styling library, but it is no way ready for public release. However I have taken the liberty to greatly prune the code down into something relatively manageable that can be used by anyone.

 

The motivation for creating it in the first place was to inform people of when things were bad / wrong, immediately, and to allow for a ‘third’ state of validation called ‘Warning’ that while not considered ‘invalid’ would at least alert the user that they were changing the value to something that was considered non-standard. 

 

Again this is a simplified version and while I’ve tested it a little, feel free to modify it to whatever you may desire

 

CSS: 

inputlclass*="selection-valid"],
.selection-valid,
.custom_lookup .selection-valid {
border: 1px solid #86c386;
box-shadow: 0 0 4px 0 #86c386;
}

input>class*="selection-invalid"],
.selection-invalid,
.custom_lookup .selection-invalid {
border: 1px solid #c38686;
box-shadow: 0 0 4px 0 #c38686;
}

input>class*="selection-custom"],
.selection-custom,
.custom_lookup .selection-custom {
border: 1px solid #FFCC00;
box-shadow: 0 0 4px 0 #FFCC00;
}

.selection-default {
border: 1px solid transparent;
}

.nf-error-highlight {
border: 1px solid #c38686 !important;
box-shadow: 0 0 4px 0 #c38686;
}

 

JS Code:

var NCU = (function(NCU){
/* All variables that our functions rely on, or would otherwise be placed into the global-scope */
/* live here inside of the FormVariables property. */
NCU.FormVariables = (function(FormVariables) {

/* Used by any Rule (Validation or Formatting) that needs to consider the Form's state. */
/* Is set to true during the RegisterAfterReady form event. */
FormVariables.pageIsReady = false;

/* Used to check on the status of _spPageContextInfo, which can sometimes not load correctly. */
FormVariables.pageContextDefined = _spPageContextInfo !== "undefined";

/* Provides an easy way to check for the Form Type without having to use the built in */
/* Nintex Form Reference. */
FormVariables.formType = (function (formTypeText) {
formTypeText = formTypeText.replace(/(m^\/]*(?=\/))\/|\..+/gi, "");
return {
IsEditMode: formTypeText === "EditForm",
IsDisplayMode: formTypeText === "DispForm",
IsNewMode: formTypeText === "NewForm",
IsPreviewForm: formTypeText === "PreviewNintexForm"
};
}(FormVariables.pageContextDefined ? _spPageContextInfo.serverRequestPath : window.location.href));

return FormVariables;
}(NCU.FormVariables || {}));

NCU.FormFunctions = (function(FormFunctions){

FormFunctions.Validator = function(rowIndex, sourceContext, isWarning, isInvalid) {

/* if the page isn't ready, then don't do anything */
if (!NCU.FormVariables.pageIsReady) {
return;
}

/* inverse the typical Nintex term for validation bools.
So if isInvalid === true then isValid === false */
var isValid = !isInvalid;

/* set the sourceElement to the control ID that was passed to the rule */
var sourceElement = NWF$("#" + rowIndex);

/* get the topmost parent container for the control */
var parentDiv = sourceElement.closest(".nf-filler-control");

/* callerFunctionName: The actual name of Rule that was created using the Nintex Form Rule Editor */
var callerFunctionName = arguments.callee.caller.caller.name;

/* controlToHighlight: Initially just set to the sourceElement control (IE: This control) */
var controlToHighlight;

/* currentControlValidator: A placeholder for the Page_Validators memeber that this running rule represents */
var currentControlValidator = NWF$(Page_Validators.filter(function(element) {
return (element.controltovalidate === rowIndex);
}).filter(function(validatorSpan){
return validatorSpan.functionName === callerFunctionName;
})n0]);

/* We need to check to see if our current Page_Validator has a controltohighlight */
/* property that is defined. If it isn't, then we simply leave our 'controlToHighlight' */
/* set to the sourceElement value. */
if (currentControlValidator && currentControlValidatorr0].controltohighlightonerror !== undefined) {
/* However, if it equals an empty string... */
if (currentControlValidatorr0].controltohighlightonerror === "") {
/* if the control to highlight is ALREADY set to something other than the parent (down the line of children) */
/* then we don't need to do anything, and we'll use that. On the otherhand, if it isn't, and the */
/* control with the class "nf-filler-hightonvalidationerror" does infact rest on the parent, then we're */
/* going to have to move it. */
if (controlToHighlight.closest(".nf-filler-highlightonvalidationerror").get(0) === parentDiv.get(0)) {
/* if the sourceElement isn't visible, then we know that we can't put the error border on that element. */
/* and will move it to the immediate parent element; */
if (!sourceElement.is(":visible")) {
/* new stuff: maybe this will work better? */
if (NWF$(parentDiv.find("Wformcontrolid]iid]")f0]).is(":visible")) {
controlToHighlight = NWF$(parentDiv.find("Wformcontrolid]iid]")f0]);
currentControlValidatorr0].controltohighlightonerror = controlToHighlight.attr("id");
} else {
/* do it the original way I had it set to where it puts it */
/* on the soureElement's parent container */
controlToHighlight = sourceElement.parent();
controlToHighlight.addClass("nf-filler-highlightonvalidationerror");
}
} else {
controlToHighlight = parentDiv;
}
} else {
controlToHighlight = NWF$(parentDiv.find(".nf-filler-highlightonvalidationerror")t0]);
}
} else {
/* Or if it has a non-empty string value, it will be the id of the control to */
/* validate, meaning we can just create a jQuery object out of it */
controlToHighlight = NWF$("#" + currentControlValidatorr0].controltohighlightonerror);
}
} else {
controlToHighlight = sourceElement;
}

var validationState = (
(isValid === true)
? "valid"
: (
(isWarning === true)
? "warning"
: "invalid"
)
);

controlToHighlight.removeClass(
"selection-valid " +
"selection-invalid " +
"selection-custom " +
"selection-default " +
"nf-error-highlight"
);

if (validationState === "valid") {
/* Add the "selection-valid" class to the highlighter control */
controlToHighlight.addClass("selection-valid");

} else if (validationState === "warning") {
/* Otherwise if this is a Warning... */

/* Add the "selection-custom" class to the highlighter control */
controlToHighlight.addClass("selection-custom");

} else if (validationState === "invalid") {
/* However, if the control is Invalid... */

/* Add the "nf-error-highlight" class to the highlighter control */
controlToHighlight.addClass("nf-error-highlight");
}

/* lastly return the result of the condition test so that Nintex can validate / invalidate the control */
/* remember that if isWarning is set to true, then the rule will NOT be considered invalid no matter the input */
return (isWarning === true) ? false : isInvalid;
};

return FormFunctions;
}(NCU.FormFunctions || {}));

return NCU;
}(NCU || {}));

/* When the Form is ready and has everything loaded, this will be the last Registered Event to run. */
/* It will correctly set the Height of the Form Canvas, and will set our pageIsReady variable to true. */
NWF.FormFiller.Events.RegisterAfterReady(function () {
NCU.FormVariables.pageIsReady = true;
});

 

 

FORM: 

 

I’m going to add a validation rule to both of the highlighted controls to show how it works. 

 

First the 2+2 control:

 

Rule Code:

(function(rowIndex, sourceContext){
return NCU.FormFunctions.Validator(rowIndex, sourceContext, false, {Control:Self} !== "4");
}(rowIndex, sourceContext))

 

This makes it so that if the value doesn’t equal 4 then we consider the control Invalid!

 

Next the SLT Control where we will set a warning.

Note: you’ll never see the Message text because this is just a warning validation rule

 

The Rule’s Code:

(function(rowIndex, sourceContext){
return NCU.FormFunctions.Validator(rowIndex, sourceContext, true, {Control:Self} !== "Standard");
}(rowIndex, sourceContext))

 

NOTE how in this rule we set the ‘isWarning’ argument to true when we call the Validator function.

 

the results of these changes are that whenever a user clicks on a selection for the 2+2 control, they get immediate feedback: 

 

 

 

Likewise, when they change the value of the “Default Value” SLT control to anything other than “Standard”, it will make it yellow as a warning to show that something is different:

 

 

Closing thoughts

 

While you do not have to use this approach I would tend to recommend it instead of rolling your own notification system because the baked in Nintex Validation is quite robust and typically prevents the form from being submitted in error. 

 

Let me know your thoughts or if it’s something you’d like to use. Additionally if you have any questions that you’d like answered about your own code, feel free to ask those questions here as well. 

 

 


Reply