Validation on SharePoint Forms – Part Two

In my previous post I outlined a few approaches to validation in SharePoint forms.  In this post, I’ll give some simple examples of how you might validate some date inputs.  For this example, I’m using a little Paid Time Off (PTO) application I built for one of my clients.  The basic idea was that the employees could make a request for time off, and the request could be approved or denied by their manager (using SharePoint Designer-based workflow).  There was some reporting around all of this as well.

I built a  List called Requests for PTO, and of course got the default forms (NewForm.aspx and EditForm.aspx).  On both forms, I converted the List Form Web Parts (LFWPs) to Data View Web Parts (DVWPs) so that I could better control their layout.  (This was due to some other requirements I won’t cover here.  The same scripting below would work on the default LFWP with a few tweaks.)  There were several more changes which the client wanted to make to those forms:

  • The client had offices in the US and UK, so whatever the date format, it confused half the population, so they wanted to require use of the date picker control rather than allowing direct date entry.
  • They wanted to cleanly track PTO within calendar years, so no one request could cross year boundaries.
  • Start Date and End Date were required, but also should indicate a valid range.

Let’s focus on the second bullet above.  I added my helper functions to the page by including a source .js file to it.  I usually create a Document Library at the root of the Site Collection called JavaScript to contain these files for centralized editing and reuse.  So in the page, I added the following script tag:

<asp:Content ContentPlaceHolderId=”PlaceHolderAdditionalPageHead” runat=”server”>
    <META Name=”CollaborationServer” Content=”SharePoint Team Web Site”>
    <script type=”text/javascript” src=”/JavaScript/Utilities.js”></script>

Note the location of this script tag: right at the top of the asp:Content area called PlaceHolderAdditionalPageHead.

Next, I added in the code which was specific to the page directly, taking advantage of my helper functions.  (In the code below, I’ve included my helper functions inline to make following along easier.  I also have left in some commented out debugging lines which I’ve found helpful to understand exactly what’s going on.  This example is from NewForm.aspx.)

// Validate the PTO Request input
function validateInput() {
    startDate = getValueWithFieldName('input', 'Request Start Date', 'ff1_new');
    endDate = getValueWithFieldName('input', 'Request End Date', 'ff2_new');
    //alert(startDate + endDate);
    startYear = startDate.substring(startDate.length, startDate.length - 4);
    endYear = endDate.substring(endDate.length, endDate.length - 4);
    //alert(startYear + endYear);
    if(startYear != endYear) {
        return 'Start date and end date must be in the same year.  To enter a PTO request which crosses into the next year, please enter two requests.';
    }
    if(startDate.length == 0) {
        return 'Please enter a start date.';
    }
    if(endDate.length == 0) {
        return 'Please enter an end date.';
    }
    return '';
} 

// getValueWithFieldName: Get a form field value using its tagName, fieldname, and identifier to find it in the page
// Arguments:
//        tagName:    The type of input field (input, select, etc.)
//        fieldName:    The name of the list column
//        identifier:    The identifier for the instance of the fieldName (ff1, ff2, etc.)
//
    function getValueWithFieldName(tagName, fieldName, identifier) {
        //alert('getValueWithFieldName: tagName=' + tagName + ' fieldname=' + fieldName + ' identifier' + identifier);
        var theInput = getTagFromIdentifierAndFieldname(tagName, fieldName, identifier);
        //alert('getValueWithFieldName: theInput.id=' + theInput.id + ' theInput.value=' + theInput.value);
        return theInput.value;
    } 

// getTagFromIdentifierAndFieldname: Get an input field's tag object using its tagName, fieldname, and identifier to find it in the page
// Arguments:
//        tagName:    The type of input field (input, select, etc.)
//        fieldName:    The name of the list column
//        identifier:    The identifier for the instance of the fieldName (ff1, ff2, etc.)
//
    function getTagFromIdentifierAndFieldname(tagName, fieldName, identifier) {
        //alert('getTagFromIdentifierAndTitle: tagName=' + tagName + ' fieldname=' + fieldName + ' identifier' + identifier);
        var len = identifier.length;
        var tags = document.getElementsByTagName(tagName);
        for (var i=0; i < tags.length; i++) {
            if (tags&#91;i&#93;.title == fieldName) {
                //alert('HIT tags&#91;' + i + '&#93;.title' + tags&#91;i&#93;.title + ' tags&#91;' + i + '&#93;.id=' + tags&#91;i&#93;.id + ' identifier=' + identifier);
                if((tags&#91;i&#93;.id == identifier) || (tags&#91;i&#93;.id.indexOf(identifier) > 0)) {
                    return tags[i];
                }
            }
        }
        return null;
    }

In my form, I altered the onclick event on the Submit button, like so:

<td nowrap="" class="ms-vb">
    <input type="button" value="Submit" name="btnSubmit">
     <xsl:attribute name="onclick">
      errmsg = validateInput();
      if(errmsg.length > 0) {
       alert(errmsg);
      } else {
       <xsl:value-of select="ddwrt:GenFireServerEvent('__commit;__redirectsource')" />
      }
     </xsl:attribute>
    </input>
    <input type="button" value="Cancel" name="btnCancel" onclick="javascript: {ddwrt:GenFireServerEvent('__cancel;__redirectsource')}" />
   </td>

The validateInput JavaScript function does three tests:

  • Ensures that Start Date and End Date fall in the same year
  • Ensures that the Start Date is not blank
  • Ensures that the End Date is not blank

If any of these tests fails, the function returns a string with a message to the user.  The script on the button’s onclick event then either shows an alert with that message or commits the change if all is well.  The same approach works on the EditForm.aspx page, with a few tweaks to take into account the slightly different structure of the LFWP or DVWP.

27 Comments

  1. Hi Marc
    i am following your post to do validation in NewitemForm with datasource as Sql Server but i keep getting error object excepted even if there is only alert in validate() function i have double check on src=”” path thats looks fine to me.i am using IE8

    any idea on this

    Thanks

    Reply
    • Hi Marc

      here is finding once i put function inline instead of referncing js file in Src tag its works fine.i have double check on path of file but still no luck

      Reply
      • HI Marc,
        BY Placing JS in aspk page and modifying to parse DOM as per my page i got its working but it would be better if i can put this JS in seperate file and upload this file in doc lib but dont know once i do this keep getting error Object excepted

        Thanks

        Reply
        • I’m having a little troble following what you’re doing, but let me know if you need my help! You cna use the contact for above if you want to reach me directly.

          M.

          Reply
          • I have a sharepoint form field control in my visual webpart .i need to update this controls data into my custom list and need to catch the list data exception .can any one help on this?
            thank u in before

            Reply
  2. Mark, you are really doing a great job . I am working on Jquery and your function saved me from hell today. I really appreciate you and your’s efforts in this Blog

    Reply
  3. Hey, small world eh. Thanks to google I found this and it was a life saver. There is so little out there on this, that xsl with the input button was the trick! I just need to remember to hack around more with xsl when it comes to things like this.

    Reply
  4. How to set to null ,via javascript , a SharePoint:DateTimeControl ?
    if i try to set the .value property to “” then after the save in the list the field has the value of today date.

    Reply
    • Roberto:

      It may be that you aren’t setting the right element in the DOM. Date/time controls are compound controls, and you need to make sure you are setting the element which actually is posted back.

      M.

      Reply
  5. i’ve searched high and low on how to incorporate radio buttons into this validation script… i’ve struggled to find any article that indicates a radiobutton’s tagname (for a sharepoint form field). i’m positive it’s possible to include radiobuttons into the script above, but the tagname continues to stump me!

    thank so much for the solution posted above–as always, your solutions are well-thought-out and simple to execute. XD

    Reply
    • You bet, it’s possible to select radio buttons and checkboxes on the page. The issue is that SharePoint doesn’t give you good ‘hooks’ into them.

      I’m at SPTechCon today and don’t have my laptop running, but take a look at how I find the controls at the top of the SPArrangeChoices function in SPServices.

      M.

      Reply
      • alright, so i poured over SPArrangeChoices–perRow, FillInChoice, columnName look about right…

        Since columnName || DisplayName, I miss the boat because i’ve modified my DisplayName in the form i’m working with, but i’m thinking that i can use fieldName in its place?

        still sketching out what i’m thinking as far as morphing the two..

        Reply
        • I was trying to point out (maybe not successfully) this technique:

          // There's no easy way to find one of these columns; we'll look for the comment with the columnName
          var searchText = RegExp("FieldName=\"" + opt.columnName + "\"", "gi");
          // Loop through all of the ms-formbody table cells
          $("td.ms-formbody").each(function() {
          	// Check for the right comment
          	if(searchText.test($(this).html())) {
          		// Do stuff...
          	}
          });
          

          In the case of SPArrangeChoices, I’m using the DisplayName, but it sounds like you want to use the StaticName, so use this line instead:

          // There's no easy way to find one of these columns; we'll look for the comment with the columnName
          var searchText = RegExp("FieldInternalName=\"" + opt.columnName + "\"", "gi");
          

          The comment you are trying to use as your selector looks like this:

          <!--  FieldName="Lead Source"
          			 FieldInternalName="Lead_x0020_Source"
          			 FieldType="SPFieldMultiChoice"
          		   -->
          

          M.

          Reply
  6. Thank you Marc, as your posts have been very useful. I am trying to do something slightly different. I am using the DataFormWebPart new item form. which uses the {ddwrt:GenFireServerEvent(‘__commit;__redirect={…}’)}” in the save button. I need to run a Javascript (called finishedsuccessfully()) that will be called only if the commit has commited correctly, but obviously before the redirect. In short, I am trying to squeeze in a javascript inbetween the __commit and __redirect which are both xsl. Alternatively, I would like to know if there is a way to test if the __commit works, and then run the Javascript (where I can place the redirect there).

    Thanks in advance,
    Bitul Torah

    Reply
    • Bitul:

      That’s tricky. When the DVWP submits the form to the server for the commit, it’s basically gone and you can’t get it back. Take a look at the SPRedirectWithID function in SPServices. I managed to come up with a way to sort of do what you are looking for, but it’s messy. It also used to work in browsers other than IE, but doesn’t at the moment.

      M.

      Reply
  7. I am trying to use your code for validating that a Choice field named Category (which Allows ‘Fill-in’ choices) is not empty. My javascript doesn’t work:

    CategorySelect = getValueWithFieldName(‘select’, ‘Category’, ‘ff4_new’);
    CategoryInput = getValueWithFieldName(‘input’, ‘Category’, ‘ff4_new’);
    if((CategorySelect.length == 0) && (CategoryInput.length == 0)) {
    return ‘Category must be entered’;
    }
    //
    return ”;

    It breaks on the select portion. Please help. Thanks.

    Reply

Have a thought or opinion?