cancel
Showing results for 
Search instead for 
Did you mean: 
cmikhaiel
Nintex Newbie

Javascript to do save and force validation on specific fields

I have a single form that contains multiple questions and sections. 

We have placed each question in a panel, and we show and hide each panel when the user selects tabs (radio buttons rendered as buttons) . 

The "Next" and "Previous" buttons basically move from one tab to the other. So if you are at question 1, and click Next, a javascript will run and show the next question(#2) and so on. 

The Javascript is something like this. 

function GoToQ2 ()
 {

 NWF$('#'+ formTabs).find('input:radio[value="2"]').prop("checked","true").change();
return false;

}

We need that "Next" button to do validation (and possibly save and continue as well) of the questions.

The tricky part is want to do validation only for the current tab before it moves on to the next question.Is it possible to do partial validation given that all of them are technically on the same form ?!

How can we do that, and can we cause validation only on specific rules on each button?

Labels: (1)
0 Kudos
Reply
5 Replies
Automation Master
Automation Master

Re: Javascript to do save and force validation on specific fields

So, this is a doable, but it would be helpful to know a little bit about your environment.

Ideally...

  • Which version of Nintex Forms are you running? 
  • Which version of SharePoint are you running? 
  • Does this have to work in Mobile (nothing works in mobile! Gotcha!)? 
  • Which web browsers will be used to access the Form?
  • Do you have a version of the form with your control layout that you can upload (obviously without private / proprietary info) so that the solution can be bolted right on? 

The solution will ultimately be a big ol' custom blob of madness, but those above things might also make it more or less mad depending on your answers. 


0 Kudos
Reply
Highlighted
Automation Master
Automation Master

Re: Javascript to do save and force validation on specific fields

I can answer these questions in Christine's absense... 

- Most up to date version of Nintex Forms

- SP 2013
- Nope! Not for the mobile app, anyway. Agreed @ nothing works on Mobile.
- All. 

- That part, I can't help with. 

But hope that helps. D: 

0 Kudos
Reply
Automation Master
Automation Master

Re: Javascript to do save and force validation on specific fields

Out of curiosity, can people click on both the independent Javascript Button Controls and the Radio Buttons of the Choice Control (rendered as Tabs)? 

Or are the Tabs just a visual aide, and the navigation controls are the only way to move through the selections? 


0 Kudos
Reply
Automation Master
Automation Master

Re: Javascript to do save and force validation on specific fields

So... This is just going to be a brief post as I gotta split, however, I'm uploading (what should be) a fully functional Nintex Form example of this type of Validation in Action. 

The Custom Javascript code that I'm using is: 

/* Tabbed Form code */


/*
  We're going to create a convenient Namespace to store all of our
  Form Variables and Functions in, so that we can keep those out of the
  Global Scope
*/


var NCU = (function(NCU) {
  "use strict";

  /*
    Now we can set up the Variable property in our Namespace which
    will hold all of our custom Variables

    tabControlName = The element class on the controls inside of
    the panel that you'd like to target for validation

    tabbedPanelClass = The element class on the Panel containing the
    controls to be validated
  */

  NCU.FormVariables = (function(obj) {
    obj.tabControlName = "tabsChoiceControl";
    obj.tabbedPanelClass = "tabPanel";
    return obj;
  }(NCU.FormVariables || {}));


  /*
    Now we'll set up our FormFunctions property which will contain
    all of our custom Functions.
  */

  NCU.FormFunctions = (function(obj) {

    /*
      This code is brought to you by Nintex, and has only been slightly modified.
      what has changed is that instead of just allowing it to use the Page_Validators
      from the form, we are passing in an array (targetValidators) which contains a
      specific number of Validators that we'd like to target.

      This will allow us to generate the Validation Summary at the top of the page but
      only for the elements we're concerned with.
    */

    obj.CustomValidationSummaryOnClick = function(validationGroup, targetValidators) {
      if (typeof(Page_ValidationSummaries) == "undefined") {
        return;
      }
      var summary;
      var sums = 0;
      var s;
      var ruleIds = new Array();
      var headerSep;
      var first;
      var pre;
      var post;
      var end;

      for (sums; sums < Page_ValidationSummaries.length; sums += 1) {
        summary = Page_ValidationSummaries[sums];
        if (!summary) {
          continue;
        }
        summary.style.display = "none";
        if (!Page_IsValid && IsValidationGroupMatch(summary, validationGroup)) {
          var i;
          if (summary.showsummary != "False") {
            summary.style.display = "";
            if (typeof(summary.displaymode) != "string") {
              summary.displaymode = "BulletList";
            }
            switch (summary.displaymode) {
              case "List":
                headerSep = "<br>";
                first = "";
                pre = "";
                post = "<br>";
                end = "";
                break;
              case "SingleParagraph":
                headerSep = " ";
                first = "";
                pre = "";
                post = " ";
                end = "<br>";
                break;
              case "BulletList":
              default:
                headerSep = "";
                first = "<ul>";
                pre = "<li>";
                post = "</li>";
                end = "</ul>";
                break;
            }
            s = "";
            if (typeof(summary.headertext) == "string") {
              s += summary.headertext + headerSep;
            }
            s += first;
            for (i = 0; i < targetValidators.length; i += 1) {
              if (!targetValidators[i].isvalid && typeof(targetValidators[i].errormessage) == "string") {
                if (targetValidators[i].ruleId != undefined) {
                  if (NWF$.inArray(targetValidators[i].ruleId, ruleIds) == -1) {
                    s += pre + targetValidators[i].errormessage + post;
                    ruleIds.push(targetValidators[i].ruleId);
                  }
                } else {
                  s += pre + targetValidators[i].errormessage + post;
                }
              }
            }
            s += end;
            summary.innerHTML = s;
            window.scrollTo(0, 0);
          }
          if (summary.showmessagebox == "True") {
            s = "";
            if (typeof(summary.headertext) == "string") {
              s += summary.headertext + "\r\n";
            }
            var lastValIndex = targetValidators.length - 1;
            var i = 0;
            for (i; i <= lastValIndex; i++) {
              if (!targetValidators[i].isvalid && typeof(targetValidators[i].errormessage) == "string") {
                switch (summary.displaymode) {
                  case "List":
                    s += targetValidators[i].errormessage;
                    if (i < lastValIndex) {
                      s += "\r\n";
                    }
                    break;
                  case "SingleParagraph":
                    s += targetValidators[i].errormessage + " ";
                    break;
                  case "BulletList":
                  default:
                    s += "- " + targetValidators[i].errormessage;
                    if (i < lastValIndex) {
                      s += "\r\n";
                    }
                    break;
                }
              }
            }
            alert(s);
          }
        }
      }
    };

    /*
      These functions (nextTabPosition & previousTabPosition) run when you click on the
      Next or Previous buttons r function is what runs when you click on the respective
      Form Button.
    */

    obj.nextTabPosition = function(tabControlName) {

      tabControlName = tabControlName || NCU.FormVariables.tabControlName;
      var tabChoices = NWF$("[data-controlname='" + tabControlName + "'] input:radio");
      var currentChoice = tabChoices.toArray().filter(function(input, index) {
        return input.checked;
      })[0];
      var choicePos = NWF$.inArray(currentChoice, tabChoices);
      if (choicePos > -1 && choicePos < tabChoices.length - 1) {
        NWF$(tabChoices[choicePos + 1]).trigger("click");
      }
    };

    obj.previousTabPosition = function(tabControlName) {
      tabControlName = tabControlName || NCU.FormVariables.tabControlName;
      var tabChoices = NWF$("[data-controlname='" + tabControlName + "'] input:radio");
      var currentChoice = tabChoices.toArray().filter(function(input, index) {
        return input.checked;
      })[0];
      var choicePos = NWF$.inArray(currentChoice, tabChoices);
      if (choicePos > -1 && choicePos > 0) {
        NWF$(tabChoices[choicePos - 1]).trigger("click");
      }
    };


    /*
     This function will validate everything inside of a given Panel, specified
     by an element class passed into the function directly, or (if not passed),
     by the class specified the value in property NCU.FormVariableds.tabbedPanelClass.
    */

    obj.validatePanel = function(tabbedPanelClass) {

      tabbedPanelClass = tabbedPanelClass || NCU.FormVariables.tabbedPanelClass;

      /* Set selectedPanel to the visible Panel at the class specified */
      var selectedPanel = NWF$("." + tabbedPanelClass + ".nf-filler-control:visible");

      /* Get all of the Controls that are contained inside of that particarl Panel */
      var panelControls = selectedPanel.find(".nf-filler-control");

      /* Set the Default Value of our allControlsValid variable to true.
        This will be set to false if any of the panelControls FAIL their validation */

      var allControlsValid = true;

      /* For every control to be validated... */
      panelControls.each(function(index, targetControl) {

        /* Create an Array of EVERY validator for the current Control being iterated */
        var controlValidatorsArray = NWF$(targetControl).find("*").filter(function(index, element) {
          /* First we FILTER out any child element that has the 'Validators' property on it */
          return element.Validators !== undefined;
        }).map(function(index, targetElement) {
          /* Then we MAP out the CONTENTS of the property 'Validators' */
          return targetElement.Validators;
        }).toArray();
        /* And then we finish by converting that into a basic native Array */


        /* If the Control has Validators assigned to it... */
        if (controlValidatorsArray.length > 0) {

          /* Then we set the variable 'controlValidators' to the controlValidatorsArray */
          var controlValidators = controlValidatorsArray;

          /* However, if there is more than (1) Validator in the array... */
          if (controlValidators.length > 1) {

            /*
              We'll need to reduce them down so that we don't end up validating the Same Validator twice
             
              To learn more about the Array.prototype.reduce() function, see:
              https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce             
            */


            controlValidators = controlValidatorsArray.reduce(function(keptValidators, currentValidator, index, currentArray) {
             
              /*
                If this is the first pass we change the value of keptValidators from just a regular <element>
                into an Array containing that single <element>.

                Otherwise, if this is a pass greater than the first, because we are returning an Array, we
                know that we can just leave the value alone.
              */

              if (!(NWF$.isArray(keptValidators))) {
                keptValidators = [keptValidators];
              }

              /* keptValidators = (NWF$.isArray(keptValidators)) ? keptValidators : [keptValidators]; */

              /*
                Now we need to store the IDs of any of the Validators that are located in
                the keptValidators Array.
              */

              var keptValidatorsTargets = keptValidators.map(function(validator) {
                return validator.id;
              });

              /* We'll get the ID of the Validator that we're currently looking to keep */
              var currentValidatorTarget = currentValidator.id;

              /* And if we don't it's ID in the Array of Existing IDs, then... */
              if (keptValidatorsTargets.indexOf(currentValidatorTarget) === -1) {

                /* We add it to the Array of Validators we'll later process */
                keptValidators.push(currentValidator);
              }

              /* Finally, we'll return the Array, and iterate again if needed */
              return keptValidators;
            });
          }
     
           
          /*
            Now that we have an Array of all of the Validators that are on the currentTarget Control,
            we'll Validate each one.
          */

          controlValidators.forEach(function(validator) {

            /*
              ValidatorValidate is the code property of Nintex and will set the value of the
              'isvalid' property on a Validator to either false or true.
            */

            ValidatorValidate(validator);

            /* If the Validator IS NOT valid... */
            if (!validator.isvalid) {

              /* If allControlsValid isn't yet false, set it to false */
              if (allControlsValid) {
                allControlsValid = false;
              }

              /*
                Apply the default Invalid Styling to the Control.

                Note: If you had a custom styling class that you wanted to apply instead,
                this is where it would go!
              */

              NWF$("#" + validator.controltovalidate).addClass("nf-validation-error nf-error-highlight").attr("title", validator.errormessage);
            } else {

            /* If the Validator IS valid... */

              /* Remove the Invalid Styling from it */
              NWF$("#" + validator.controltovalidate).removeClass("nf-validation-error nf-error-highlight").attr("title", "");
            }
          });

          /* Nintex Code that Sets the Page_IsValid to true / false */
          ValidatorUpdateIsValid();

          /*
            And then we call our custom Summary generator that will,
            create the Validation Error Summary at the top of the page
          */

          obj.CustomValidationSummaryOnClick(undefined, controlValidators);
        }
      });
     
      /*
        Once we have ran Validation on all of the Controls,
        we'll return whether or not they all passed.

        Remember. If even ONE of them fails, This will return false!
      */

      return allControlsValid;
    };

    /*
      This fixes the incorrect way that Nintex Forms increase / decrease the
      height of the Form Canvas. By leaving in the FormFiller Events below,
      all instances where the height would have a chance to grow out of control
      in either direction, should be fixed
    */

    obj.setCanvasContainerHeight = function() {
      if (outerDiv.data("outerDivHeight") !== outerDiv.height()) {
        outerDiv.outerHeight(outerDiv.height());
        outerDiv.data("outerDivHeight", outerDiv.height());
      }
    };

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

  /*
    The below FormFiller.Events are in place to handle how the Form will
    incorrectly set the Form Height as Controls are Hidden and Shown
    via the Rule System and other interactions.
  */

  NWF.FormFiller.Events.RegisterBeforeReady(function() {
    outerDiv.outerHeight(outerDiv.height());
  });

  NWF.FormFiller.Events.RegisterControlShowHidePropagated(function() {
    if (arguments[0].data("RepositionControls")) {
      NCU.FormFunctions.setCanvasContainerHeight();
    }
  });

  NWF.FormFiller.Events.RegisterControlHeightChangePropagated(function() {
    NCU.FormFunctions.setCanvasContainerHeight();
  });

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

    /*
      This is the only Exception.
      Here we are adding an eventHandler to the 'Tab Buttons' of our
      Choice Control. This way when they are clicked, they trigger the
      Validation code which will either allow the Click Event to continue
      or to stop dead in its tracks until you correct the error.

      This way, the only thing our Next and Previous buttons need to do
      is to invoke the .on("click") event on the next radio button, so
      we don't have to write extra code allowing the user to use both ways
      of navigating through the tabs.
    */

    NWF$("." + NCU.FormVariables.tabControlName + " input[type='radio']").on("click", function(event){

      /* If the Controls in the Panel ARE NOT valid... */
      if (!NCU.FormFunctions.validatePanel()) {

        /* Prevent the Radio Buttons from working */
        event.preventDefault();

        /* Stop this event from bubbling */
        event.stopImmediatePropagation();
      }
    });


    outerDiv.data("outerDivHeight", outerDiv.height());
  });

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


This is designed in such a way that you should be able to drop the above code into the Custom Javascript textarea in your form settings, and then add the following code to your Next Button: 

NCU.FormFunctions.nextTabPosition();

and then the following code to your Previous Button: 

NCU.FormFunctions.previousTabPosition();

Any who, make a new list, import the Form (there are no dependencies), and tell me if that works across everything. I tested it in Chrome because IE hurts my soul, but I think that it should be A-OKAY

(PS: Big shout out to Patrick Abel for the initial easy to grab Nintex Form with a Tabs and all of the CSS already baked in! Super helpful and so much faster than making my own!)

(PPS: I'll probably come back and make a proper blog post about how to target specific controls for validation once I have the time, and can sit down and write out a more generic version of the above code, as it could be incredibly helpful in certain situations)

If y'all got any more questions, just shoot em my way! 


Reply
Automation Master
Automation Master

Re: Javascript to do save and force validation on specific fields

Was there ever any movement on this? 

0 Kudos
Reply