SharePoint Nintext NewForm Custom Javascript Require Attachment


Badge +2

Hello - New to nintex and javascript.

I have a SharePoint custom list with a nintex NewForm including custom javascript (and HTML, CSS) and I need to make the attachment control required.  I have found many solutions and suspect the reason I have not been able to solve the issue is placement of the attachment validation within the existing javascript:

var queryString = function(field, url)
{
 var href = url ? url : window.location.href;
 var reg = new RegExp('[?&]' + field + '=([^&#]*)','i');
 var string = reg.exec(href);
 return string ? string[1] : null;
};NWF$(document).ready(function()
{
 NWF$(".nf-form-footer").hide();
 NWF$("#"+ IIStatus).hide(); 
 NWF$("#"+ ICStatus).hide();  var isEditMode = document.location.pathname.indexOf("/EditForm.aspx") > -1;
 if(!isEditMode )
 {
 NWF$("#"+ SumComment).val('Please Approve.'); 
  var amount = NWF$("#"+ IAmount).val();
  var location = NWF$("#"+ ILocation).val() == null ? '' : NWF$("#"+ ILocation)[0].options.length > 0 ? NWF$("#"+ ILocation)[0].options[NWF$("#"+ ILocation)[0].selectedIndex].title : null;
  var department = NWF$("#"+ IDepartment).val() == null ? '' : NWF$("#"+ IDepartment)[0].options.length > 0 ? NWF$("#"+ IDepartment)[0].options[NWF$("#"+ IDepartment)[0].selectedIndex].title : null; 
  
  NWF$("#"+ IAmount).blur(function(){
   amount = this.value;
   getApprover(amount,location,department);
  });    NWF$("#"+ ILocation).change(function()
  {  
   if(this.value != '')
   {   
    location = this.options[this.selectedIndex].title;
    getCoder(location);
    getApprover(amount,location,department);
   }
  });     
  
  NWF$("#"+ IDepartment).change(function()
  {
   if(this.value != '')
   {
    department = this.options[this.selectedIndex].title;
    getApprover(amount,location,department);
   }
  });
 }
 else
 {
  if(NWF$("#"+ ICStatus).val() == "Rejected" || NWF$("#"+ IIStatus).val() == "Rejected")
  {
   NWF$("#"+ IIStatus).val('Coder Approval Pending');
   NWF$("#"+ ICStatus).val('Pending Approval');
  }  
 }
}); 
   
function getApprover(amount,location,department)
{ 
 approverClear();
 if(amount != '' && location != '' && department != '')
 {  
  NWF$.ajax({ 
   url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Approver Detail')/Items?$orderby=Amount asc&$select=Location/Title,Department/Title,Amount, Title&$expand=Location, Department&$filter=Location/Title eq '" + location + "' and Department/Title eq '" + department + "'",   type: "GET", 
   headers: {"accept": "application/json;odata=verbose"}, 
   success: function (data)
   { 
    debugger;
    if(data != undefined && data.d != undefined && data.d.results != undefined && data.d.results.length > 0)
    { 
     var maxAmt = true;
     for(var cnt = 0; cnt < data.d.results.length; cnt++)
     {
      if(data.d.results[cnt].Role != "Coder 1" && data.d.results[cnt].Role != "Coder 2")
      {
       if(data.d.results[cnt].Amount == amount) 
       {
        AddUserToPPL(data.d.results[cnt].Title);
        maxAmt = false;
        return false;
       }
      }
     }     if(maxAmt)
     {
      for(var cnt = 0; cnt < data.d.results.length; cnt++)
      {
       if(data.d.results[cnt].Role != "Coder 1" && data.d.results[cnt].Role != "Coder 2")
       {
        if(data.d.results[cnt].Amount > amount) 
        {
         AddUserToPPL(data.d.results[cnt].Title);
         return false;
        }
       }
      }
     }
    } 
   }, 
   error: function (xhr) { 
    alert(JSON.stringify(xhr));   
    alert(xhr.status + ': ' + xhr.statusText); 
   } 
  }); 
 }
}function AddUserToPPL(ApproverEmail) 
{
 var requestUri = _spPageContextInfo.webAbsoluteUrl + "/_api/web/siteusers/getbyemail(@v)?@v='" + ApproverEmail + "'";
 try 
 {
  NWF$.ajax({
   url: requestUri,
   type: 'GET',
   headers: { 'ACCEPT': 'application/json;odata=verbose' },
   success: GetApproverSuccess,
   error: GetApproverError
  });
 }
 catch (err) 
 {
  //jQuery('#errorMsg').html('getListData Error: ' + err);
 }
}function GetApproverSuccess(data) 
{
 //var Approver = data.d.GetUserProfilePropertyFor;
 var Approver = data.d.LoginName;
 var ApproverPicker = new NF.PeoplePickerApi('#' + IAssign);
 ApproverPicker.search(Approver).done(function (data) 
 { 
  //ApproverPicker.clear();
  ApproverPicker.add(data[0]);
 });
}function GetApproverError(sender, args) 
{
 //$get("results").innerHTML = "Error: " + args.get_message();
 approverClear();
} function getCoder(location)
{ 
 coderClear(); if(location != '')
 {  
  NWF$.ajax({ 
   url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Approver Detail')/Items?$select=Location/Title,Title,Role&$expand=Location&$filter=Location/Title eq '" + location + "'",
   type: "GET", 
   headers: {"accept": "application/json;odata=verbose"}, 
   success: function (data)
   { 
    if(data != undefined && data.d != undefined && data.d.results != undefined && data.d.results.length > 0)
    { 
     for(var cnt = 0; cnt < data.d.results.length; cnt++)
     {
      if(data.d.results[cnt].Role == "Coder 1" || data.d.results[cnt].Role == "Coder 2")
      {
    if(cnt == 0)
   AddCoderToPPL(data.d.results[cnt].Title);
      }
     }
     //data.d.results[0].Title 
    } 
   }, 
   error: function (xhr) { 
    alert(JSON.stringify(xhr));   
    alert(xhr.status + ': ' + xhr.statusText); 
   } 
  }); 
 }
}function AddCoderToPPL(ApproverEmail) 
{
 var requestUri = _spPageContextInfo.webAbsoluteUrl + "/_api/web/siteusers/getbyemail(@v)?@v='" + ApproverEmail + "'";
 try 
 {
  NWF$.ajax({
   url: requestUri,
   type: 'GET',
   headers: { 'ACCEPT': 'application/json;odata=verbose' },
   success: GetCoderSuccess,
   error: GetCoderError
  });
 }
 catch (err) 
 {
  //jQuery('#errorMsg').html('getListData Error: ' + err);
 }
}
function GetCoderSuccess(data) 
{
 //var Approver = data.d.GetUserProfilePropertyFor;
 var Coder = data.d.LoginName;
 var CoderPicker = new NF.PeoplePickerApi('#' + ICoder);
 CoderPicker.search(Coder).done(function (data) 
 { 
  //CoderPicker.clear();
  CoderPicker.add(data[0]);
 });
}
function GetCoderError(sender, args) 
{
 //$get("results").innerHTML = "Error: " + args.get_message();
 coderClear();
} 
function coderClear()
{
 var CoderPicker = new NF.PeoplePickerApi('#' + ICoder);
 CoderPicker.clear();
}
function approverClear()
{
 var ApproverPicker = new NF.PeoplePickerApi('#' + IAssign);
 ApproverPicker.clear();
}
function cancel()
{
 window.location = "MyCompanyURL";
} 
//url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Approver Detail')/Items?$orderby=Amount&$select=Location/Title,Department/Title,Amount, Title&$expand=Location, Department&$filter=Location/Title eq '" + location + "' and Department/Title eq '" + department + "' and Amount ge '" + amount + "'", function InvoiceUpdate()
function InvoiceUpdate()
{
  if(isEditMode )
  {
  debugger;
  var itemType1 = GetItemTypeForListName("Invoice Pending");
  var item1 = {
   "__metadata": { "type": itemType1 },
   "Title": NWF$("#" + _invoiceID).val(),
   "LocationId":NWF$("#" + ILocation).val(),
   "DepartmentId": NWF$("#" + IDepartment).val(),
   "InvoiceAmount": NWF$("#" + IAmount).val(),
   "InvoiceDate": NWF$("#" + _InvoiceDate).val(),
   "InvoiceDueDate": NWF$("#" + _InvoiceDueDate).val(),
   "AssignTo": NWF$("#" + IAssign).val(),
   "Coder": NWF$("#" + ICoder).val(),
   "Vendor": NWF$("#" + _VendorName).val(),
   "VendorNo": NWF$("#" + _VendorNum).val(),
   "Comment": NWF$("#" + _cmts).val()
  };  updateListItem(NWF$("#_invoiceID").val(), "Invoice Pending", _spPageContextInfo.webAbsoluteUrl, item1, function () {
   //amtCount = amtCount + 1;
   //updatePendingInvoice(amtCount); 
  }, function () { errorMsg = true; });
  }
  // Run Validation on Attachments
 function ValidateAttachment(source, arguments) {
      var Control = NWF$("#" + CtrlControl).val();
      var elm = NWF$("table[id*=idAttachmentsTable]");      // PRE ATTACHMENT CONTROL CHANGE
           var elmAttachmentRow = NWF$("table[id*=idAttachmentsRow]");      // POST ATTACHMENT CONTROL CHANGE           var elmAttachmentRow = NWF$("div[id*=idAttachmentsRow]");
      if ((elm != null && elm.prop('rows').length > 0) && (elmAttachmentRow != null)) {
           arguments.IsValid = true;
      }
      else {
           if (Control == "Yes") {
           arguments.IsValid = false;
           alert("Attachment Required")
           }
      }
 }
}function GetItemTypeForListName(name) {    return "SP.Data." + name.charAt(0).toUpperCase() + name.split(" ").join("").slice(1) + "ListItem";
}function getListItemWithId(itemId, listName, siteurl, success, failure) 
{
 var url = siteurl + "/_api/web/lists/getbytitle('" + listName + "')/items?NWF$filter=Title eq '" + itemId + "'";
    NWF$.ajax({
        url: url,
        method: "GET",
        async: false,
        headers: { "Accept": "application/json; odata=verbose" },
        success: function (data) {
         
            if (data.d.results.length == 1) {
             data.d.results[0].urls = url;
                success(data.d.results[0]);
            }
            else {
                failure("Multiple results obtained for the specified Id value");
            }
        },
        error: function (data) {
           
            failure(data);
        }
    });
}function updateListItem(itemId, listName, siteUrl, item, success, failure) {
 
    getListItemWithId(itemId, listName, siteUrl, function (data) 
    {
     NWF$.ajax({
            url: data.__metadata.uri,
            type: "POST",
            contentType: "application/json;odata=verbose",
            data: JSON.stringify(item),
            headers: {
                "Accept": "application/json;odata=verbose",
                "X-RequestDigest": NWF$("#__REQUESTDIGEST").val(),
                "X-HTTP-Method": "MERGE",
                "If-Match": data.__metadata.etag
            },
            success: function (data) { success(data); },
            error: function (data) { failure(data); }
        });
    }, function (data) { alert('Failed2'); failure(data); });
}

11 replies

Userlevel 2
Badge +11

For Nintex Forms 2016 on-prem, there exists a Validation setting on the Attachment control where you can set the minimum number of attachments.

Using JS you have the function/method "NWF.FormFiller.Attachments.GetAttachedFilesCount" at your dispossal.

 

PS: if I understand Custom JavaScript requirements correctly, it is recommended to have the following snippet:

NWF$(document).ready(function(){
}

Within you can handle change events for certain controls. Any client click methods can be defined outside.

 

Hopefully this is enough to resolve your question.

Userlevel 5
Badge +14

If you're looking to Validate the Control, I'd suggest using the built in Rule System (either on the Control, or via the Rule Panel proper) to do it. 

However, if you're already doing that and you're just trying to call your ValidateAttachment() function from a rule, you're not going to be able to because its within the scope of the function named InvoiceUpdate(). Move ValidateAttachment() out to the global space if you're trying to invoke it from elsewhere. 

 

Maybe you're not trying to do either of those things and are instead just trying to do custom Validation completely bypassing the validation built into the forms. If that's the case it would be a lot more difficult to help you with the information provided (as you'd need to be handling the cancellation of the Submit, and in more explicit terms, show how you're calling / invoking the code below to initiate the Validation in the first place). 

----------

I wrote a giant post a long time ago on how to, for lack of a better term, 'self-reference' controls from the Rules that are being ran on them. Using the below pattern for a Validation Rule: 

(function(rowIndex) {
  "use strict";

  var internalElement = sourceContext.find("#" + rowIndex);

  return false;
}(rowIndex));‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

You should be able to easily do any particular tests and return either true (Failure) or false (Passing) based on what your code finds. 

Here is the link to the original post: https://community.nintex.com/t5/Community-Blogs/Breaking-The-Rules-A-Dive-Into-The-Nintex-Forms-Rule-System/ba-p/78874 


 

Badge +2

Thanks for the replies.

jpmhuls - I dont know where or how to include the document ready line or how to make use of it for requiring attachments.

 

The JS I posted is in Form - Settings - Custom Javascript&colon;

1760i242D9A64DFD5B9E2.png

 

The Validation in the Attachements Control does nothing and my thinking is the Custom JS is overridding it?

1761iAE26804BD239E03E.png

nmarples giant post will certainly be a great resource as I learn enough to understand it but not as helpful from my current lack of skills/understanding and immediate need.

Would the suggested pattern for a Validation Rule be placed on my submit button?

 

I will be very grateful for any further assistance.

Userlevel 5
Badge +14

Because you have named a lot of functions in the global namespace, you very well might be overriding something, but off the top of my head, I wouldn't know if that's the case. 

Ideally when you have custom Javascript as you have, it's best to create a single new global variable that contains all of the functions / values that you'd like to reference or set during the course of your time spent on the form. 

instead of: 

function mySpecialFunction(){
  return "Hello World"; 
}

function theAnswerToLife(){
  return 42;
}


and then calling those functions like:

mySpecialFunction(); // "Hello World"
theAnswerToLife(); // 42


You can instead encapsulate them inside of a single Object:

var myNamespace = (function(){
  var mySpecialFunction;
  var theAnswerToLife;

  mySpecialFunction = function() {
    return "Hello World";
  };

  theAnswerToLife = function(){
    return 42;
  };

  return {
    mySpecialFunction: mySpecialFunction,
    theAnswerToLife: theAnswerToLife
  };
}());

 
Which you can then call using the following notation: 

myNamespace.mySpecialFunction(); // "Hello World"
myNamespace.theAnswerToLife(); // 42


As for the whole: 

NWF$(document).ready(function(){}


You don't really need to use that as your Javascript will load completely fine without it. 

To answer your question of, "Would the suggested pattern for a Validation Rule be placed on my submit button?", nope! You'd select your Attachment Control and then use the Add Rule button to place a new Validation Rule onto the control. 

So. At the top of your Ribbon in your Form Editor, look for this button: 
1765i3C2A7A254FC1382C.png

 

And you should see the Rule Panel pop out on the right side:
1766i6EE1D8D1C96C47B6.png

 

Select the Attachment Control in question, and again, at the top of your Ribbon click on the Add Rule button: 

1767iA2999374037E270D.png

 

A new Rule will pop up inside of the Rule Panel on the right side of your screen. From there you can configure the name, type, and add your code to the Rule which will run when the form tries to Validate the control: 
1768iB144567D7D519487.png

 

Just copy / paste the following code into the textarea:

(function(rowIndex) {
  "use strict";

  var internalElement = sourceContext.find("#" + rowIndex);
  debugger;

  /* This will always be invalid because we're returning true */
  return true;
}(rowIndex));‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍


As you can tell from the comments, this will ALWAYS make the control invalid. Additionally I have added in a Debugger line so if you open the Dev Console you should be able to play around during this validation phase to figure out what you *actually* want inside of there to meet whatever validation needs you have. 

Remember, in the real world you're not just going to always return true, and you'll need to obviously return true or false depending on your needs as mentioned previously. 

I hope that this helps you along the way. 


Badge +2
Sincerely appreciate the clear detailed help!
When I put the rule as suggested on the attachments control and the debugger stops the page, I don't know how to interpret the results. When I remove the debugger the page freezes and validate attachments.

I would be glad to mark this as the solution as I am sure it would work although not for me specifically. I am in too deep and will have to find another approach entirely. I don't know how to work with code at this level well enough for these options.
Userlevel 5
Badge +14

Because I didn't exactly gather what it you were attempting to validate for the control, or in which way, I intentionally left it open ended. 

If you'd be willing to elaborate more on the problem you're trying to solve, in terms of your validation conditions, it would be easier to cook up something more specific that you might just be able to drop in and have work. 

(Note: That being said, when you open your developer tools (typically accessed by pressing F12 on your keyboard while in a browser), and you run into your debugger code, from there you should be able to play around in the console (typing javascript directly into there) in order to suss out exactly what you would end up putting into your code / validation rule proper so that it would happen every time the control ran it. The way that I wrote the rule will not really do anything on its own, and again is incredibly open ended and not meant for production stuff. Just to create a stopping point where you can play around with the Form in the state right before the final validation state is passed back to the function that determines whether or not to mark the control as Valid or Invalid.)




Badge +2
I see! All I am looking to do is require the users attach one file to the form before a new item is created in SharePoint list.

Thanks so much!
Userlevel 5
Badge +14

You made a reply earlier where you input a minimum amount of attachments for the Control (link: https://community.nintex.com/t5/Nintex-for-SharePoint/SharePoint-Nintext-NewForm-Custom-Javascript-Require-Attachment/m-p/93539/highlight/true#M67189), but I suppose that didn't work? 

If you're unsure whether or not  your custom Javascript is interfering with this, I'd recommend just removing all of your custom code and then testing to see if the form will validate correctly. 

From there you can slowly add back functions one by one, to see if they mess with anything. 

You shouldn't need a custom function to do what you're trying to do. 

Badge +2

I have tried to use the OOB minimum amount setting and it does not work in PROD however it does in a DEV site.  I am going to look for differences between the two and try your suggestions to iterate through adding in functions... I'll update when I get through some of that.

 

Thanks for your continued support!

Badge +2

I found a setting in the form which is causing the attachment validation to not work. There is a Save button on the form with InvoiceUpdate(); in the "Client click" field under Advanced settings. When the InvoiceUpdate(): is present the attachment validation doesn't work, when I remove it the validation works perfectly. The "Causes validation" does not change attachment validation as far as my testing went. Below is the button config - DEV on the left and PROD on the right:

1792iDBCF66609AA80A4B.png

Next, I need to try to trace the purpose of InvoiceUpdate(); in PROD before removing it to get the attachment validation I need. Any advice for vetting out the functions of InvoiceUpdate(); in my environment?

 

My original post is the Custom JavaScript from the form settings and includes some functions associated with InvoiceUpdate(); and I wonder if that JavaScript can be adjusted to validate attachments or at least stop causing the OOB validation to not work.

 

I appreciate your continued replies!

Userlevel 5
Badge +14

Giving a quick lookover your code, it would seem that InvoiceUpdate() is making some kind of ajax call and updating an item in a list elsewhere.

 

There are a few problems with doing this.

 

A: It's not particularly easy!

 

B: You're not preventing the form from submitting, and you're not waiting for the call to finish before figuring out what to do based on its outcome (success / failure / error).



Ideally, the update that you're trying to make should be done inside of a workflow that runs after your item has been updated (IE: Submitted), and that way you're keeping your Form Logic and your SharePoint updates completely separate and can better guarantee that they actually work correctly.

 

To access the FormData from the Workflow, if the Control is associated to a Column on the List / Library, then it’s as simple as referencing the Current Item’s Column. However, if it isn’t, you can actually access a property called Form Data that is an XML representation of the Controls and their subsequent values. It should be noted however that if you have not NAMED a Control on your form, the XML name that it will be given in that data will simply be a GUID which is not particularly easy to read. So name your controls!

Reply