Pains using JavaScript in Nintex Forms

  • 28 November 2017
  • 20 replies
  • 140 views

Badge +7

Hi All

I asked this question at the end of an answred thread ( https://community.nintex.com/message/73682-re-print-a-js-var-value-to-a-single-line-text-box-nintex-forms?commentID=7368… ) but I am having a lot of pains with incorporating JavaScript into Nintex forms. 

I have some form fields that are assigned variable names that start with nField...(Nintex field).

211048_pastedImage_1.png

I have written a script that works in standard html/JS but refuses to work in Nintex Forms. I have a button that triggers the function from the script but the page just reloads/resets which so far, through testing, appears to be an indication that Nintex is not able to handle the JavaScript that is being called. 

 

There are some script specific variable used for storing values for a calculation in the script that are not in the form.

The idea is that the user inputs the information in the fields and then clicks a button to get a recommendation based on those choices. There are quite a few rules hat determine what recommendation they get. That recommendation is then shown in  a box at the end of the form.

 

There are no errors reported o the console. Just a page reload.

Can anyone suggest where I am going wrong here? Do I need to insert NWF$ all over the script and if so are there any guides or references of where to put them? I can't seem to find any. I have been trying various combinations but nothing works and it's getting very frustrating now. 

Can you even use standard JavaScript in Nintex forms?

Appreciate any help that can be provided.

The JS is configured here:

211052_pastedImage_3.png

A button on the form calls the JS function:

211053_pastedImage_4.png


20 replies

Userlevel 5
Badge +14

each control that has defined js variable should have been referred to as

NWF$('#' + nFieldBusinessUnit)

however, there is not an unified/straightforward way how get/set a form control values. for different type of controls this is done in a different way.

even for a single control type different approach might be needed based on how the control is rendered.

that all depends on how nintex builds DOM model for single controls.

a nice example of these different approaches might be the choice control that you seem o use heavily on your form.

see following examples how to set choice control value when rendered different ways

choice as checkboxes
 

choice as radio switch
How can I set Choice control value when in Option button display format 
          
choice as tabs/buttons
how to set Tab selected value 

choice as dropdown list
 Auto populate value in drop-down menu                                                                       

hope it helps make the things clearer.

Badge +9

Variables You define in Nintex Forms as 'Client ID javascript variable name' are containing the ID of a control not the value or property. Nintex Forms uses a copy of the jQuery library that you can access through NWF$.

To get a controls value use jQuery as You do it at the end of the script to set control values e.g.

NWF$("#" + nFieldBusinessUnit ).val()

Badge +7

Hi Manfred and Marian 

Thank you for your replies.

This should be interesting since I don't know JQuery, but if I understand correctly, when I reference a variable from the form ( a drop-down or text control that has been given a "Client ID JavaScript variable name" I would need to use the jQuery reference to access their values?

Example:

if NWF$("#" + nFieldProjectType ).val() === "Fast Track" && NWF$("#" + nFieldProjectDuration ).val() != "< 4 Weeks") {
alert("Validation Error: Your project does not meet the Fast track criteria. Duration must be < 4 weeks for fast Track projects. Review cost and duration before continuing.");

else if ( ) { } ....

And for any variables used not in the form, I can stick with standard JS referencing? 

Example:

var numExistCustomer = 0;

Thanks

Badge +9

Now You are on the right track.

I recommend You to read the jquery documentation, especially

https://api.jquery.com/category/selectors/ 

https://api.jquery.com/val/ 

https://api.jquery.com/prop/ 

Badge +7

Thank Manfred.

I will have a read but just to check my thinking on this so I can get started, is this a correct adjustment to an if statement?

Specifically, do I still use the JavaScript parentheses to hold the JQuery? 

Example JavaScipt parentheses in bold:

else if (NWF$("#" + nFieldScopeClarity).val() === " ") {
alert("Validation Error: Scope Clarity field must be selected to continue");
throw new Error("Validation Error: Scope Clarity");

Or do I have to structure it differently?

Thanks again

Badge +7

So I tried this as a test:

JavaScript in Bold:

function getPlan(){
   if (NWF$("#" + nFieldBusinessUnit).val() === "BLC") {
      alert("You selected BLC");
      }
}

It looks like I do need to keep the JavaScript parenthesis to hold the JQuery name for the control variable name.

So from this test, I am assuming I should only need to adjust the variable references to the JQuery format (let's hope).

Will update when I have updated the entire script.

Fingers crossed.

Badge +9

Hi Harry

jQuery is javascript. It's a library written in javascript, to make DOM access and AJAX calls easier.

Kind regards

Manfred

Badge +7

So I updated the code to include the changes mentioned above and it still doesn't work despite replicating across the simple test outlined previously.

Can anyone please help point out what I am doing wrong? Do I need to add a document ready function? It's driving me a little crazy now.

Code:

*Removed

Badge +7

I think I found the problem. There is a problem with this bit of code which checks for empty fields:

//Check for empty Fields
if (NWF$("#" + nFieldPortfolioSector).val() === "") {
alert("Validation Error: A Portfolio/Sector must be selected to continue");
} else if (NWF$("#" + nFieldLeadTheme).val() === "") {
alert("Validation Error: A Lead Theme must be selected to continue");
throw new Error("Validation Error");
} else if (NWF$("#" + nFieldBusinessUnit).val() === "") {
alert("Validation Error: A Business Unit must be selected to continue");
throw new Error("Validation Error");
} else if (NWF$("#" + nFieldFundingSource).val() === "") {
alert("Validation Error: A Funding Source must be selected to continue");
throw new Error("Validation Error");
} else if (NWF$("#" + nFieldExistingCustomer).val() === "") {
alert("Validation Error: Existing Customer field must be selected to continue");
throw new Error("Validation Error");
} else if (NWF$("#" + nFieldSectorAlignment).val() === "") {
alert("Validation Error: Sector Alignment field must be selected to continue");
throw new Error("Validation Error");
} else if (NWF$("#" + nFieldTechnicalCapability).val() === "") {
alert("Validation Error: Technical Capability field must be selected to continue");
throw new Error("Validation Error");
} else if (NWF$("#" + nFieldScopeClarity).val() === "") {
alert("Validation Error: Scope Clarity field must be selected to continue");
throw new Error("Validation Error");
}

Solved:

I hadn't assigned nFieldSectorAlignment to a control. Now the code stops and mostly works except there seems to be a validation problem.

New Problem:

The code above is not picking up the field as Empty.

211064_pastedImage_1.png

I tested with an actual choice from the menu and it worked. For some reason the code above is not picking up the value as empty. I tried "Please select a value.." and it still didn't work.

Anyone got any ideas?

Userlevel 5
Badge +14

It would be exceptionally helpful to have access to an export of your form so that we could actually test this with a full set of controls, however despite that, there are a few things that I would consider fixing in your code before you continue. 

Validation

Leave validation to the Validation Rules inside Nintex Forms. Using this generic rule - 

isNullOrEmpty({Control:Self})

You can make sure that every field has some value before being allowed to continue. Additionally, this also guarantees that ALL of your fields are set to a logical value before you start trying to calculate the resulting Risk and Plan. 

On the Submit button (which should have 'Causes Validation' set to Yes), you can put in the Client Click portion something like: 

(function(event) {
  if (!Page_ClientValidate() || !getPlan()) {
    NWF$(".nf-validation-summary").hide();
    event.preventDefault();
  }
}(event));‍‍‍‍‍‍

This will first make sure that the form is valid (if it isn't then it'll prevent the form from submitting), and if the Page_ClientValidate() returns true (meaning the form is valid), it will run getPlan().

Writing it this way however will require you to update the getPlan() function with a return value of true or false based on your own conditions. 

ex: 

function getPlan(){
 
  var returnValue = someBooleanCondition;
  //... some magical code
 
  return returnValue;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

Conditional Logic:

Specifically, your Main Function (getPlan).

Besides trying to validate a number of fields inside of this function, it would seem that you have some condition logic that needs to be sorted out. 

The biggest culprit being

if (
      (NWF$("#" + nFieldFundingSource).val() === "Innovate UK" && totalRiskScore > 100) ||
      (NWF$("#" + nFieldFundingSource).val() === "Other UK Public Funding" && totalRiskScore > 100) ||
      (NWF$("#" + nFieldFundingSource).val() === "Horizon 20/20 or EU" && totalRiskScore > 100)) {
    planType = "A";
  } else if (
      (NWF$("#" + nFieldFundingSource).val() === "Innovate UK" && totalRiskScore > 70 && totalRiskScore < 100) ||
      (NWF$("#" + nFieldFundingSource).val() === "Other UK Public Funding" && totalRiskScore > 70 && totalRiskScore < 100) ||
      (NWF$("#" + nFieldFundingSource).val() === "Horizon 20/20 or EU" && totalRiskScore > 70 && totalRiskScore < 100)) {
    planType = "B";
  } else if (
      (NWF$("#" + nFieldFundingSource).val() === "Innovate UK" && totalRiskScore < 70) ||
      (NWF$("#" + nFieldFundingSource).val() === "Other UK Public Funding" && totalRiskScore < 70) ||
      (NWF$("#" + nFieldFundingSource).val() === "Horizon 20/20 or EU" && totalRiskScore < 70)) {
    planType = "C";
  } else if (
      (NWF$("#" + nFieldFundingSource).val() === "Direct Customer Funding" && totalRiskScore > 100) ||
      (NWF$("#" + nFieldFundingSource).val() === "Membership Drawdown" && totalRiskScore > 100)) {
    planType = "D";
  } else if (
      (NWF$("#" + nFieldProjectDuration).val() === "12 Weeks" && NWF$("#" + nFieldFundingSource).val() === "Direct Customer Funding" && totalRiskScore > 70 && totalRiskScore < 100) ||
      (NWF$("#" + nFieldProjectDuration).val() === "12 Weeks" && NWF$("#" + nFieldFundingSource).val() === "Membership Drawdown" && totalRiskScore > 70 && totalRiskScore < 100) ||
      (NWF$("#" + nFieldProjectDuration).val() === "> 12 Weeks" && NWF$("#" + nFieldFundingSource).val() === "Direct Customer Funding" && totalRiskScore > 70 && totalRiskScore < 100) ||
      (NWF$("#" + nFieldProjectDuration).val() === "> 12 Weeks" && NWF$("#" + nFieldFundingSource).val() === "Membership Drawdown" && totalRiskScore > 70 && totalRiskScore < 100)) {
    planType = "E";
  } else if (
      (NWF$("#" + nFieldProjectDuration).val() === "< 12 Weeks" && NWF$("#" + nFieldFundingSource).val() === "Direct Customer Funding" && totalRiskScore < 70) ||
      (NWF$("#" + nFieldProjectDuration).val() === "< 12 Weeks" && NWF$("#" + nFieldFundingSource).val() === "Membership Drawdown" && totalRiskScore < 70)) {
    planType = "F";
  } else if (
      (numProjectValue < 1000) || (NWF$("#" + nFieldForum).val() === "Yes") ||
      (NWF$("#" + nFieldProjectDuration).val() === "< 4 Weeks")) {
    planType = "G";
  } else if (
      (NWF$("#" + nFieldFundingSource).val() === "Core Research Project") ||
      (NWF$("#" + nFieldFundingSource).val() === "Internal Capability Funding") ||
      (NWF$("#" + nFieldFundingSource).val() === "HVM Catapult Funding")) {
    planType = "H";
  } else if (
      (NWF$("#" + nFieldBusinessUnit).val() === "BLC") && (NWF$("#" + nFieldLeadTheme).val() !== "") && (NWF$("#" + nFieldFundingSource).val() === "Direct Customer Funding") ||
      (NWF$("#" + nFieldBusinessUnit).val() === "BLC") && (NWF$("#" + nFieldLeadTheme).val() !== "") && (NWF$("#" + nFieldFundingType).val() === "Fixed Price")) {
    planType = "I";
  } else if (
      (NWF$("#" + nFieldBusinessUnit).val() === "BLC") && (NWF$("#" + nFieldLeadTheme).val() === "") && (NWF$("#" + nFieldFundingSource).val() === "Direct Customer Funding") ||
      (NWF$("#" + nFieldBusinessUnit).val() === "BLC") && (NWF$("#" + nFieldLeadTheme).val() === "") && (NWF$("#" + nFieldFundingType).val() === "Fixed Price")) {
    planType = "J";
  }

Now I have intentionally messed the formatting up a bit to make it easier to read, but there are a few issues worth noting. 


First off, there is no consistent condition that you are checking in every outcome, meaning that neither nFieldFundingSourcetotalRiskScorenFieldProjectDurationnumProjectValuenFieldLeadTheme, or nFieldFundingType are being checked in EVERY condition. Because some of these values are not obviously set by logic prior, I'd expect that in certain instances you would meet more than one condition which is bad. I will try to show you this visually. 

Field Funding Source Condition
211065_pastedImage_6.png

Field Funding Source Condition:

211066_pastedImage_8.png

Project Duration Condition: 

211067_pastedImage_10.png

Why is any of that important? 

Because it makes it incredibly difficult to tell when you are or are not meeting the certain conditions that you're expecting to meet. 

Ideally, there should be something that is of the highest importance which overrides all other logic based on whether or not it is true. 

For instance, is the riskRating doing most of the driving? If so, that breaks things down into three very distinct tiers. 

if (riskRating === "H") {

  } else if (riskRating === "M") {

  } else {

  }

That's pretty straight forward. We know that ideally we should have a riskRating, so it would make sense to set up the logic in this way. 

Additionally this also remove the need for you to write out the 'totalRisk > ?? && totalRisk < ???', because it's already been done for us when we set the riskRating variable.

Or perhaps maybe it's the Funding Source that is important. 

var fieldFundingSource = NWF$("#" + nFieldFundingSource).val();

  if (fieldFundingSource === "Innovate UK") {

  } else if (fieldFundingSource === "Other UK Public Funding") {

  } else if (fieldFundingSource === "Horizon 20/20 or EU") {

  } else if (fieldFundingSource === "Direct Customer Funding") {

  } else if (fieldFundingSource === "Membership Drawdown") {

  } else if (fieldFundingSource === "Core Research Project") {

  } else if (fieldFundingSource === "Internal Capability Funding") {

  } else if (fieldFundingSource === "HVM Catapult Funding") {

  } else {
   
  }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Either way, that leaves out certain things that seem important like the Business Unit, Funding Type, and Project Value...

Create a hierarchy of importance for your values, and trickle the logic down into discrete units that have ALL combinations accounted for. Otherwise you run the risk of selecting a combination of things that result with no planType being applied. 

Last But Not Least

When you put javascript into you form directly as you have, make sure to remove the comments. I have found several instances of comments totally breaking the javascript interpretation because it's evaluating it during runtime, but in a strange way (within CDATA xml brackets or something). 

It's always a good place to start when you are trying to figure out if something is actually broken. 

Final Thoughts:

A lot of what you're trying to accomplish seems as if it could be done inside of Calculated Fields right there on the form WITH validation baked in where you could not only inform your users in a more meaningful way, but also lock down certain controls that in the event that the control values that they are dependent on have not been properly filled out. 

If it wouldn't be too much trouble, again, it might be worth exporting your form so that people here can take a look at what's happening. 

Just be sure to describe any column connections necessary, and environment details (SP version, Nintex ver., etc. etc.). 

I hope that this helps. 

Badge +7

Hi nmarples

That was really thorough, thank you for your time and effort. 

Some of the conditions apparently don't care about project duration or total risk score. If those conditions are met, then they present a different plan type regardless of the other fields. It is basically a copy of an existing process that I had to replicate. 

I guess it's back to the drawing board.

Thanks again.

Badge +7

hi nmarples

re: Validation

I want to execute the getPlan() function before the form is submitted so that the user can see the results before submitting. 

It appeared to me that the quickest and easiest way to do that would be to make the getPlan() function do the checking for blank values and then stop the rest of the function if any was blank.

The problem of course is that NWF$("#" + nFieldPortfolioSector).val() === "" doesn't see the control as empty. It will see a value like "some choice" just fine but not blank "".

I can't figure out why it doesn't see a blank but does see a specific choice.

Userlevel 5
Badge +14

No problem. We here can still help you work through it. 

Some of the conditions apparently don't care about project duration or total risk score. If those conditions are met, then they present a different plan type regardless of the other fields.

That's part of the problem. If two or more conditions are met at any given time, just for your own sanity, it would be nice to know explicitly when one thing should have precedent over another. That would fulfill hierarchical nature of how you'd probably want your logic to be set up. 

It also might be an opportunity to create a new standard inside of the company, or clear up something that before was otherwise rather confusing. 

As your logic stands, your conditional outcomes (ie: planType)  are in a nice and tidy English speaking reading order from A ~ J, however, creating that arbitrary pattern prevents approaching the problem of 'which conditions should apply first' mostly impossible. 

Userlevel 5
Badge +14

Right. 

Assuming that you are using a Choice Control with a Drop Down and no Fill In Choices, that particular line of Javascript should work so long as you have the variable assigned to the JavaScript ID (in the Control's Options) set to the same name as the variable you are using there. 

The reason I suggest moving your validation to individual rules on the controls is that it can show a user ALL of the problems when something is incorrect. 

The way that you have the validation set up in your getPlan() function, as soon as an error condition is met, it's alerted... but what about the errors after that? An alert isn't a particularly graceful way of indicating an error and does not visually impart the user with any information after they have closed that window.

A Validation Rule on the other hand does that very well. 

Additionally if you validated all of the fields that your function (getPlan()) relies on, you could leave your 'Client Click' set up just the way you do on your button. 

Lastly. If you'd like more pointed help, it might be worthwhile of creating a mockup form that you can export and attach here that has all of the controls which are needed for this stuff to work. 


Badge +7

Hi nmarples

I am a bi uneasy about exporting the form as it contains a lot of information about a company process as such I have removed the code from the rest of the post as everything except the validation seems to be working.

Firstly, apologies for my lack of understanding here. 

I don't quite understand what this is doing on the "get plan and risk" button

(function(event) {
  if (!Page_ClientValidate() || !getPlan()) {
    NWF$(".nf-validation-summary").hide();
    event.preventDefault();
  }
}(event));

Could I use another fucntion in my script to check if the validation rules are true like this?

function checkValidation() {

if (Page_ClientValidate()) {
return true;
}
else {
return false;
}
}

Then an if statement to check if checkValidation() === true?

Userlevel 5
Badge +14

I wouldn't worry about that bit of code I wrote as it would be unnecessary in the event that you set up Form based validation to prevent the submission of the form if things weren't filled out. 

However, to explain a bit. 

the Page_ClientValidate function is a function that is created when the Nintex Form is generated. It checks the validity of the Form's controls, and returns either a true if all of the controls passed their validation tests, or a false if any of them didn't. 

Additionally if you were to set up your getPlan function so that it would return a boolean value, writing the following: 

if (!Page_ClientValidate() || !getPlan()) {
    NWF$(".nf-validation-summary").hide();
    event.preventDefault();
  }

would basically mean that

Page_ClientValidate() would run, and if something failed because it was invalid, would return false. Because I placed an exclamation point before this function, it would invert the boolean value from false to true. Because this is an OR (||) statement. As soon as it reaches a condition that is TRUE, it would stop evaluating anything beyond that point and fall into if block. 

If Page_ClientValidate() passed, and returned true, then that value would be inverted to false. This would not fulfill the OR statement, and it would continue on to the getPlan() function. Assuming you had that function set up to return a true condition if everything inside of it worked out, and a false value if something inside failed, then the same inversion would occur. If the function returned false, then the program would continue inside of the if statement's block, prevent the default event (of submitting the form) from occurring. 

This is really just a very clever way of saying 

if (Page_ClientValidate() === false || getPlan() === false) {
   // Stop the Form From Submitting
}


Ultimately you would not need any of that stuff if you just validated on your controls using the Nintex Rules System. so you can safely ignore that entire example. 

However if you're interesting in learning about the Truthy / Falsey evaluation of things in Javascript, see: 

JS Comparison Table 

Truth, Equality and JavaScript – JavaScript, JavaScript… 

Truthy and Falsy: When All is Not Equal in JavaScript — SitePoint 

Badge +7

As a test, I have applied the cannot be empty form rule to a choice control.

211159_pastedImage_3.png

Here is the rule:

211158_pastedImage_2.png

I have then set the button to "Cause validation= Yes" and the client click to activate the function. To my mind this reads as it's going to validate the choice control and if it's empty it's going to show the message and not do anything else unless the validation is true.

211157_pastedImage_1.png

Except it's not working. There is no validation and the plan getPlan() gets executed.

I need to trigger the getPlan() before the user moves on to the ""Submit" button.

??

Userlevel 5
Badge +14

Yep! That's the expected (and unfortunate behavior) of the order in which form-wide validation is triggered. 

When you have Javascript in the 'Client Click' portion of your button, that gets executed before (during the 'on click' event) the Form Validation occurs (which is during the 'submit' event). 

This is where that 'Page_ClientValidate' function comes in handy. If you were to replace your Client Click JavaScript with something like: 

(function(event) {
  if (!Page_ClientValidate()) {
    NWF$(".nf-validation-summary").hide();
  } else {
    getPlan();
  }
}(event));

It would check the form validation FIRST, and based on whether it did or didn't pass, would either run your getPlan function or allow the submit event to fire, which would actually highlight all of the bad controls.  

note: using the code above, if the form was invalid, Page_ClientValidate() will return the boolean value of false. When that happens, it forced that Error Summary panel at the top of the form (which describes all of the reasons your form has failed validation) to turn on. However, because the validation highlighting hasn't ran (the code that puts the red boxes around the invalid controls), I opt to hide that summary panel, as it will reappear once the validation highlighting is finished during the form submission event. 

Other Observations

It seems like your validation rule is written in plain text as 

isNullOrEmpty({Self})

Typically the "{Self}" is highlighted as a red link, unless you have made changes to your css code. My guess is that it might actually be wrong and you either need to go into the Insert Reference Dialog (f(x) button), or you can type it in plain text as 

isNullOrEmpty({Control:Self})

which WILL resolve to the reference linked {Self} value. 

To test that your validation is working. remove the code from your Client Click entirely and try to submit the Preview Form using what you know to be as invalid selections. 


Badge +7

Hi nmarples.

Still doesn't work. The validation doesn't happen when clicking the get risk and plan button. Nothing happens, not validation or script trigger.

We have run out of time on this now so I have had to use a very inelegant way to achieve this.

Since empty fields are not being seen by the script, I have had to add a value to the choice controls that reads "Please select a value..." 

I then told the script to look for this value in the conditions and included rules on each control that made the validation false if  ==="Please select a value..."

It was a last resort but the form works as desired now.

Many thanks for all your help. I am very impressed by the community here.

Userlevel 5
Badge +14

Interesting. 

If you ever want to pick this back up, I highly suggest making a dummy form with dummy inputs but ultimately built in the same way so that it can be exported and shared without the risk of damaging intellectual property / confidential information. 

Custom JavaScript is probably the biggest culprit of sad times in the hard streets of Nintex Forms. 

Good to hear you have a workable approach at the moment. 

Reply