Skip to main content

So, following along with this tutorial: How to design a Multi-Step form with Nintex Forms for Office 365 , I got pretty far on my multi-page form! But, I want to show certain pages conditionally, which led me to this question: Skip pages in a multi-step form . 

 

My case is slightly different, however, as I'm not checking exactly a checkbox status, I'm seeing if a checkbox is checked in a multi-select choice control. As I'm AWFUL at Javascript (read: useless) I'm a bit stuck. I found this comment from Sean Fiene‌, https://community.nintex.com/message/25778#comment-26033  about identifying the multi-select checkbox bits, and I got his "showMe" function to work great, but I guess I'm stuck on putting the puzzle pieces together.

 

Right now my form looks a bit like this:

200727_pastedImage_4.png

 

The "IT Request Information" should only show if "IT Request" is checked, and there are other panels that are the same way (ex there's a panel for "Mobile Device" and "Employee Transfer TO your team" etc). 

 

If anyone could also tell me, how to auto-check "IT Request" and "Facilities Access Request" if the user checks "New Hire," and not let them uncheck it, that'd be great. Or if there's a better way to force them to fill out those bits of the form if they select New Hire, that's fine too.

 

The final piece here is making sure they fill out each piece of the panels they're shown (not letting them advance "next" unless they've filled out all the parts of that panel). 

 

Whew! That's a lot. But this is my first Javascript adventure, so any help at all would be amazing!

 

Perhaps ‌, ‌, or ‌ can help this poor noob out?

Hi Courtney,

Can you upload your form somewhere so we can take a look at it? I might be able to help you get this bit sorted out.

Cheers!

Aaron


Hey Aaron! I've been kind of trial and erroring it, and here's the code I've got so far:

function goStep(step)
{
var hiddenTxtBox = NWF$("#"+hiddenTxtBoxId);
hiddenTxtBox.val(step);
NWF.FormFiller.Functions.ProcessOnChange(hiddenTxtBox);
}

var alreadyShown[];

function showMe() {
var c = [];
NWF$('#' + multiChoices + ' :checkbox:checked').each(function(i) {
c = NWF$(this).val();
console.log(c);
if(c=="IT Request"){
goStep(it);
alreadyShown.unshift(c);
}
else if(c=="Facilities Access Request")
{
goStep(facilities);
alreadyShown.unshift(c);
}
else if(c=="Request Mobile Device for Employee")
{
goStep(mobile);
alreadyShown.unshift(c);
}
else if(c=="Request SECU Client and Access for Employee")
{
goStep(secu);
alreadyShown.unshift(c);
}
else if(c=="Employee Transfer TO your team")
{
goStep(transferTo);
alreadyShown.unshift(c);
}
else if(c=="Employee Transfer FROM your team")
{
goStep(transferFrom);
alreadyShown.unshift(c);
}
else
{
goStep(turnover);
alreadyShown.unshift(c);
}
});
}

The plan here, is that the first Next button calls the showMe function, and then all subsequent next buttons call goStep(c[0]) and the previous buttons call goStep(alreadyShown[0])

But again, I know so little about js this might not work at all.

And here is a link to my form export: Microsoft OneDrive - Access files anywhere. Create docs with free Office Online. 

This is my first time sharing code or a form, so please let me know if I need to change it at all.


Your JS is breaking something on my end

I will have to step through it and get some more details, but you are in good hands with ‌!


Hey Jesse!

I made a quick change before posting it, which I realized afterwards, broke it 😨 Bad Courtney.

I've made a bit of progress and my current code looks like this:

function goStep(stepNumber)
{
var hiddenTxtBox = NWF$("#"+hiddenTxtBoxId);
hiddenTxtBox.val(stepNumber);
NWF.FormFiller.Functions.ProcessOnChange(hiddenTxtBox);
console.log(alreadyShown);
console.log(stepID);
}
var alreadyShown = [];
var stepID = [];
function showMe() {
var c = [];
NWF$('#' + multiChoices + ' :checkbox:checked').each(function(i)
{
c = NWF$(this).val();
console.log(c);
if(c=="IT Request")
{
goStep(2);
alreadyShown.unshift(c);
stepID.unshift(2);
}
else if(c=="Facilities Access Request")
{
goStep(3);
alreadyShown.unshift(c);
stepID.unshift(3);
}
else if(c=="Request Mobile Device for Employee")
{
goStep(4);
alreadyShown.unshift(c);
stepID.unshift(4);
}
else if(c=="Request SECU Client and Access for Employee")
{
goStep(5);
alreadyShown.unshift(c);
stepID.unshift(5);
}
else if(c=="Employee Transfer TO your team")
{
goStep(6);
alreadyShown.unshift(c);
stepID.unshift(6);
}
else if(c=="Employee Transfer FROM your team")
{
goStep(7);
alreadyShown.unshift(c);
stepID.unshift(7);
}
else if(c=="Employee Turnover")
{
goStep(8);
alreadyShown.unshift(c);
stepID.unshift(8);

}
else
{
goStep(9);
alreadyShown.unshift(c);
stepID.unshift(9);
}
});
}

This is *currently* not breaking anything on my end, but it is also not 100% working. I am somehow skipping whatever the second page ought to be. As in, I get the landing panel, then when I click next, it always skips the "first" thing that should be next, if that makes sense. Like, if I have selected "IT Requests" then that should be the next page, but it won't be, it gets skipped. And that's persistent across whichever ones I select. But I've made some progress!

The first "Next" button (on the landing page) is set to call showMe() and all subsequent "Next" buttons call gostep(stepID[0]) so I think something is screwy with my array. The "Previous" buttons all call goStep(stepIDD1]) (except the "IT Requests" previous, since that should always go to the landing page, it just calls goStep(1))


Hey Aaron Labiosa‌, any luck? I'm trying a different method now, but I'm getting an error array.length does not exist, even though the array does contain content.

For reference, this is what I'm currently working with:

function goStep(stepNumber)
{
var hiddenTxtBox = NWF$("#"+hiddenTxtBoxId);
hiddenTxtBox.val(stepNumber);
NWF.FormFiller.Functions.ProcessOnChange(hiddenTxtBox);

}

var c = ];
var options = =];

function showMe() {

c = NWF$('#' + multiChoices + ' :checkbox:checked');
{
console.log(c);

var arrayLength = c.length();

for (var i = 0; i < arrayLength++; i++)
{
var arrayValue = (NWF$(c).val);
console.log(arrayValue);
options.push(arrayValue);
console.log(options);
}

if(optionsn0]== "IT Request")
{
goStep(2);
}
else if(optionsn0]== "Facilities Access Request")
{
goStep(3);
}
else if(optionsn0]== "Request Mobile Device for Employee")
{
goStep(4);
}
else if(optionsn0]== "Request SECU Client and Access for Employee")
{
goStep(5);
}
else if(optionsn0]== "Employee Transfer TO your team")
{
goStep(6);
}
else if(optionsn0]== "Employee Transfer FROM your team")
{
goStep(7);
}
else if(optionsn0]== "Employee Turnover")
{
goStep(8);

}
else
{
goStep(9);
}
}
}

function nextPage()
{
var arrayLength = options.length();
var currentPage = NWF$("#"+hiddenTxtBoxId);
console.log(currentPage);
var currentPageIndex = options.indexOf(currentPage);

if (arrayLength - 1 == currentPage)
{
goStep(9);
}
else
{
var nextPageIndex = currentPageIndex++;
var nextPage = optionsnnextPageIndex];
goStep(nextPage);
}

}

function prevPage()
{
var currentPage = NWF$("#"+hiddenTxtBoxId);
console.log(currentPage);
var currentPageIndex = options.indexOf(currentPage);
if (currentPageIndex == -1)
{
arrayLength = options.length();
prevPageIndex = arrayLength--;
}
else
{
var prevPageIndex = currentPageIndex--;
}

var prevPage = optionsnprevPageIndex];
goStep(prevPage);

}


Hi Courtney,

Apologies, I was sick over the weekend and did not really go online much. I am going to take a look at what you have this afternoon.

Cheers!


No worries, like I said before, I'm just starting out with JS so I'm sure it's something I'm missing or misunderstanding. I really appreciate any assistance you can provide! Hope you're feeling better 


So I have some ideas about how this could be done however, my tenant is pending an update and I need to wait for the IT team to come online to apply the update.... :

That said, I would take a look at iterating the checkboxes and then attaching an onchange event to each that could then execute show()/hide() on the panels instead of using the built in validation (it just makes things simpler). To identify the panels with jquery, you could give them a css class like 'ntxITRequestPanel'. Once I have access to Forms again, I will mock something up for you to go off of.

Here is a quick summary of what I have worked up so far:

  1.     for (var item in NWF$('#' + multiChoices + ' :checkbox:checked')) {
  2.  
  3.         if (item.val() == "IT Request") {
  4.             item.onchange = function () {
  5.                 NWF$(".ntxITPanel").show()
  6.             }
  7.         }
  8.  
  9.     }

Hey Aaron,

Thanks for the lightning fast response! Perhaps I'm misunderstanding you, but wouldn't that show the panels as they checked the boxes, rather than when they click "Next," showing the next panel (based on which checkboxes they've selected)? Which, is similar to utilizing the built-in rules capability of Nintex Forms, no?

If so, and I'm understanding you correctly, would there be a way to leverage part of your solution still to achieve what I'm looking for? And while I'm at it, let me make sure I've fully illuminated my goal here:

At form start, the only visible panel is the one that ends with the Next button. Then there is a panel with controls in it for each of those checkbox options (ex IT Request has the fields for the stuff we need to know if they want to make an IT Request). I only want to show one panel at a time, flipping through them with the Next and Previous buttons I've put on each panel, and only showing the panels for which they've indicated they'd like to complete a request. The first panel will always be the "initial info" type panel you see at the top of my form screenshot, and there is also a final end panel  that will confirm submission and ask if there are any additional details. This final panel will have a Save and Submit, a Cancel, and a "Previous" button.

Does this make sense? Thanks again!


Hi Courtney,

Ahhh, yes you are correct! In that case instead of doing the show/hide in the onchange event, you could have it throw those values into an array and then iterate the array on the button press and call show/hide in the button press function. Again, I would probably pull the logic for show/hide out of the built in validations and just use js for it since you are building the rest of your logic there (it just makes things easier).

As soon as I get access to my environment I will check this out more as well.

Cheers!

Aaron

Sent from Mail<https://go.microsoft.com/fwlink/?LinkId=550986> for Windows 10


Hey Aaron,

So I tried this:

var c =[];
var options = [];

function showMe() {

{
console.log(c);

for (var item in NWF$('#' + multiChoices + ' :checkbox:checked'))
{
     switch(item.val())
     {
          case "IT Request":
               options.push(1);
               break;
          case "Facilities Access Request":
               options.push(2);
               break;
          case "Request Mobile Device for Employee":
               options.push(3);
               break;
          case "Request SECU Client and Access for Employee":
               options.push(4);
               break;
          case "Employee Transfer TO your team":
               options.push(5);
               break;
          case "Employee Transfer FROM your team":
               options.push(6);
               break;
          case "Employee Turnover":
               options.push(7);
     }
}
}
}

To try to create the "options" array through which I could move, as my nextPage() and prevPage() functions look like this:

function nextPage()
{
     var arrayLength = options.length();
     var currentPage = NWF$("#"+hiddenTxtBoxId);
     console.log(currentPage);
     var currentPageIndex = options.indexOf(currentPage);
     
     if (arrayLength - 1 == currentPage)
     {
          goStep(9);
     }
     else
     {
     var nextPageIndex = currentPageIndex++;
     var nextPage = options"nextPageIndex];
     goStep(nextPage);
     }
     
}

function prevPage()
{
     var currentPage = NWF$("#"+hiddenTxtBoxId);
     console.log(currentPage);
     var currentPageIndex = options.indexOf(currentPage);
     if (currentPageIndex == -1)
     {
          arrayLength = options.length();
          prevPageIndex = arrayLength--;
     }
     else
     {
               var prevPageIndex = currentPageIndex--;
     }

     var prevPage = optionsprevPageIndex];
     goStep(prevPage);
     
}

goStep() is the same as the tutorial. 

But when I try all this code, the first "Next" button throws out an error of " item.val is not a function":


Try wrapping the item in jquery: NWF$(item).val()

Does that work?

Sent from Mail<https://go.microsoft.com/fwlink/?LinkId=550986> for Windows 10


I see the issue I believe,

Try setting up the following for your iteration:

var items = NWF$('#' + multiChoices + ' :checkbox:checked');

for (var item in items) {

 switch(itemssitem].val())
 {

 }

}


Unfortunately, I'm getting essentially the same error:

items[item].val is not a function


Strange, that works for me. Try doing the NWF$(items[item]).val() in there.

Sent from Mail<https://go.microsoft.com/fwlink/?LinkId=550986> for Windows 10


Like this?

This gives me

Also, gotta ask, are you working in 365? And am I missing something somewhere else, possibly? Do I need to be including anything here?


Made some progress, but the var item (the array of the multi-select boxes) are storing random-ish things and they end with _0 for the first _1 for the second, etc. 

And the middle part (between ctl14 and the _0) changes every time. So I was trying to evaluate what it ends with, but I think I'm doing something wrong with that:

function showMe() {
{
var items = NWF$('#' + multiChoices + ' :checkbox:checked');
console.log(items);
for (var item in items)
{
     var currentValue = NWF$(item).val();
     console.log(currentValue);

     if (currentValue.endsWith(0))
     {
     options.push(1);
     }
     else if (currentValue.endsWith(1))
     {
     options.push(2);
     }

Because currentValue is empty, and so we can't check the .endsWith against that.


var currentLocation = 1;
var dict = {};
dict["Landing Page"] = 1;
dict["IT Request"] = 2;
dict["Facilities Access Request"] = 3;
dict["Request Mobile Device for Employee"] = 4;
dict["Request SECU Client and Access for Employee"] = 5;
dict["Employee Transfer TO your team"] = 6;
dict["Employee Transfer FROM your team"] = 7;
dict["Employee Turnover"] = 8;
dict["Final Page"] = 9;
var chosenOptions;


function goStep(stepNumber) {
    console.log("Navigating to " + stepNumber);
    var hiddenTxtBox = NWF$("#"+hiddenTxtBoxId);
    hiddenTxtBox.val(stepNumber);
    NWF.FormFiller.Functions.ProcessOnChange(hiddenTxtBox);
}


function showMe() {
    var i = 1;
    chosenOptions = u];
    console.log("Adding option " + i + ": " + "Landing Page");
    chosenOptionsai++] = dicta"Landing Page"];
    NWF$('#' + multiChoices + ' :checkbox:checked').each(function(index) {
        var value = NWF$(this).val();
        console.log("Adding option " + i + ": " + value);
        chosenOptionsi++] = dictnvalue];
    });
    console.log("Adding option " + i + ": " + "Final Page");
    chosenOptions<i] = dicta"Final Page"];
    console.log(chosenOptions);
    goStep(chosenOptionso++currentLocation]);
}


function nextPage() {
    goStep(chosenOptionsa++currentLocation]);
    console.log("Nexting to page " + currentLocation + ": " + chosenOptionspcurrentLocation]);
}


function prevPage() {
    goStep(chosenOptionsp--currentLocation]);
    console.log("Preving to page " + currentLocation + ": " + chosenOptionsacurrentLocation]);
}

This is the final working javascript; it doesn't validate the required fields prior to them clicking "Next," that's the next step for me. But, this allows me to go between pages based on the checkboxes that are checked.


Hi Courtney Vargo‌ ! 

Great work. Seems like you have sorted out most of your questions already. The only missing piece I am seeing here is the validation on "Next" or "Previous" button click.  With the fact that button action is configured to fire a javascript function it would be very messy as we will have to maintain a variable for each panel as I presume you have mandatory fields in side each panel. And then we will need to have very customised function to validate fields within each panel based on that panel validation variable. Personally I wouldn't go down that messy path although it is doable...   

Just a thought, it seems you have configured your panels to show automatically based on checkbox values. Do you still need the next or previous buttons?  What I am picturing (and this is how I would tackle this scenario) is have only one submit button that is configured to Save and Submit. And then have some generic javascript validation functions that only validates when fields are visible bound to those mandatory fields in your form. This way when a user clicks submit it will always validate them for the mandatory fields in those visible panels. 

See my example below on validating a textbox field. 

javascript function that can be applied to all your text fields

function validateText(source, arguments) {
arguments.IsValid = false;


// Ensure field is visible (not hidden by form Rules)
var fieldDisplayed = NWF$("#" + source.controltovalidate).is(":visible");

//auto skip validation when fields not visible
if (!fieldDisplayed) {
arguments.IsValid = true;
return;
}

if (arguments.Value === undefined || arguments.Value === null || arguments.Value === "") {
arguments.IsValid = false;
return;
}
else {
arguments.IsValid = true;
}
return;
}

Let me know your thoughts on this. Happy to discuss and assist further


Hey Thomas,

I'm not showing the panel immediately upon clicking the checkbox, each panel is a "page" and which pages get shown while pressing Next and Previous are determined by which checkboxes are checked (ex if the user doesn't want to have an Employee transfer to their team, we'll skip that panel/page). So, I *do* need the "Next" and "Previous" buttons. And if I don't make sure that they have filled the fields before clicking next, I think it would be a nightmare to show them where they missed a field since they're all separate "pages". I was thinking of having each panel that they've selected show on the last page as well, that way it's kind of a "final check" and if any of the fields aren't entered, they can see them right there. And then, the final "Save and Submit" button can do all of the final validations and checks. Thoughts on that method?


I see. In that case you may need to add a function validateStep in your nextPage and previousPage functions. Based on the current step it validates the mandatory fields in that panel shown for that step.  In your final check you probably only need to validate the last page as all previous page validations would have already been completed. 


Hey Thomas,

Could you go into a bit more detail about that method?


Ok I have not tested the code below but just put it here to give you an idea. You will need to associate all your mandatory fields with a unique css class name . Then validate each panel/step by calling a different function . I have included validateLandingPage as an example below. In that function you need to validate each mandatory control that you want to validate on that page by the unique css class name. This way you can reuse the validation function, i have put validateText as an example. Let me know if it make sense. Think it should work in your scenario. 

function nextPage() {
     if(validateStep(currentLocation))
     {
     goStep(chosenOptionsn++currentLocation]);
     console.log("Nexting to page " + currentLocation + ": " + chosenOptions=currentLocation]);
     }
}


function validateStep(currentLocation)
{
     var validStep=false;
     
     if(currentLocation ==1)
     {
          validStep= validateLandingPage()
          
     }
     
     //insert other location logic
     
}

function validateLandingPage()
{
  var validLandingPage=true;
 
     if(!validateText(".mandatoryTextControl_1"))
     {
          validLandingPage=false;
     }
     
     if(!validateText(".mandatoryTextControl_2"))
     {
          validLandingPage=false;
     }
     
     //Insert all other mandatory controls for this page
     
}

function validateText(cssClass)
{
     var valid= false;

     // Ensure field is visible (not hidden by form Rules or javascript)
     var fieldDisplayed = NWF$(cssClass).is(":visible");
     
     //auto skip validation when fields not visible
     if (!fieldDisplayed) {
          valid = true;
          return;
     }
     if (arguments.Value === undefined || arguments.Value === null || arguments.Value === "") {
          valid = false;
          //highlight border in red
          NWF$(cssClass).addClass("nf-error-highlight").change();
          return;
     }
     else {
          valid = true;
           NWF$(cssClass).removeClass("nf-error-highlight").change();
     }
     return valid;
     
}


Hey Thomas,

I really appreciate your help with this. For some reason, it's always finding the value as undefined, even when text is entered.

I added some logging to your original file and here's what I get:

The [".mandatoryTextControl_2"] that's logged is when I have console.log(arguments); and the following undefined is console.log(arguments.Value);

It is highlighting the text box correctly though!


Made some edits to the function and tested this time

function validateText(cssClass)
{
     var valid= false;

     // Ensure field is visible (not hidden by form Rules or javascript)
     var fieldDisplayed = NWF$(cssClass).is(":visible");
     
     //auto skip validation when fields not visible
     if (!fieldDisplayed) {
          valid = true;
          return;
     }
     
      var fieldValue=NWF$(cssClass +" input")n0].value
     if (fieldValue === undefined || fieldValue === null || fieldValue === "") {
          valid = false;
          //highlight border in red
          NWF$(cssClass).addClass("nf-error-highlight").change();
          return;
     }
     else {
          valid = true;
           NWF$(cssClass).removeClass("nf-error-highlight").change();
     }
     return valid;
    
}

Reply