Using jQuery to Prefill and Disable Required Columns in a SharePoint Form

Here’s a question I got in the forums at the USPJ Academy today. It’s a nice simple question, but the answer points out a cool thing about SharePoint forms: the PreSaveAction.

I was watching the excellent video posted by Marc Anderson: Enhancing the User Experience with jQuery – Chaining from Selector.mp4.  In this video Marc runs through some interesting scenarios, which I tried on my own. The following scenario gave me issues: Required field that has either a disabled or hidden control that is prefilled. The problem is that when I hit OK or Save, the form never sees the pre-filled value. e.g. Simple text field (Say the out-of-the-box Title field.) Let’s pre-fill with “Hello World” and disable the field. The words “Hello World” do show up, but I am not allowed to save the form by clicking on OK. Any thoughts?

If you want to watch that “excellent video”, you’ll have to become a student at the Academy! However, this is a really good question. When an element in a form is disabled, its value effectively won’t be submitted along with the form. If the column is required, like is the case with the Title column, then when you submit the form you’ll get the validation error:

image

You need to do a little sleight of hand to make this work. Here’s a little example:

<script language="javascript" type="text/javascript" src="../../jQuery%20Libraries/jquery-1.4.2.min.js"></script>
<script language="javascript" type="text/javascript">
	$(document).ready(function() {
		$("input[Title='Title']").val("Hello World");
		$("input[Title='Title']").attr("disabled", "disabled");
	});

	function PreSaveAction() {
		$("input[Title='Title']").attr("disabled", "");
		return true;
	}
</script>

So when the document is ready, you can set the Title column’s value to ‘Hello World’, and then disable the column as in the example above.

image

The trick is to re-enable it when the form is submitted using the PreSaveAction. You can read more about the PreSaveAction in my blog post Validation on SharePoint Forms – Part Four (and others), but the basic idea is that the PreSaveAction is a function stub that SharePoint always calls on a form submit. If you want to do some additional things, like your own validations, this is where you put them. By re-enabling the Title column, your form will submit just fine.

Note that if you want the form to submit, you return true from the PreSaveAction. If you don’t want the form to submit, then you’d return false. So, for instance, if you are checking to see if a Start Date is before the End Date for an Event and it isn’t, you’d probably put a validation error message into the DOM and return false. This keeps the form from submitting, forcing the user to fix the issue.

[important]

As noted in one of my replies to the comments below, this is an old post, and jQuery has moved forward a bit. Depending on what version you are using, the line:

$("input[Title='Title']").attr("disabled", "");

may not work. You may instead need to use:

$("input[Title='Title']").prop("disabled", false);

See the jQuery documentation for .prop().

[/important]

SharePoint 2010 and the jQuery Library for SharePoint Web Services

I decided it was high time to take a look at how my jQuery Library for SharePoint Web Services might hold up in SharePoint 2010.  After all, it’s a whole new product, everything’s new and improved, etc.  Except it turns out that many things are exactly the same.

This is actually not bad news.  For instance, even though the forms to create, edit, and view list items (NewForm, EditForm, and DispForm) look quite a bit different in SharePoint 2010, they have almost exactly the same HTML structure. In SP2010 they generally pop up in modal windows, but the page structure is the same.

This isn’t just good news for my jQuery Library for SharePoint Web Services, it’s good for anyone who has done the work to create scripts that enforce business rules on their forms.  Based on my very quick pass today, I’m guessing that most form scripts will continue to work as they have or may need minor tweaking.

SharePoint Forms Reality Check

As is often the case recently, inspiration for this post came from a thread over at the MSDN SharePoint – Design and Customization forum.  In this particular thread, I suggested using the PreSaveAction (see my post entitled Validation on SharePoint Forms – Part Four for details on how this works) and some script to do some validation on a form.  The reason in this case was that the form was very long (due to a large number of columns) and users weren’t seeing the error messages higher up on the page when they clicked "OK".

Hmmm… that should work, but it means I need to add this javascript to every list I make and change the content of the script based on which fields need to be validated. Unless there is some neat little way for Javascript to figure out which of the fields require content? I can’t imagine so.
From a usability point of view Sharepoint is implementing a pretty bad default way of validating forms here. Well, just gotta work my way around it.
Thanks for the help.

I think you always need to put SharePoint through the 80/20 mental test.  The way the out of the box forms work is absolutely fine for [at least] 80% of the use cases.  IMHO, Microsoft is interested in selling seats to the development community and keeping that ecosystem up and running in a healthy way.  That’s where the other 20% comes in.  The other thing to consider is that these default forms are pretty amazing in that they just work, and they work regardless what types of columns you throw at them.

Validation on SharePoint Forms – Part Four

Trolling the MSDN SharePoint – Design and Customization forum is proving to be a great learning experience all around.  I see questions I can’t answer which cause me to research them, and hopefully, I’m providing some answers which help others.

One of the things I saw over the last few days was a link to a post which explained how to use the PreSaveAction() JavaScript function to validate form data before it is saved.  (Check out my recent previous posts about validation in SharePoint forms: Parts One, Two, and Three.  I’ve posted about many other bits and pieces of this over time as well.)  Greg Osimowicz explains this in a post entitled SharePoint Forms date validation using JavaScript with reference to Edin Kapić‘s post entitled Add JavaScript Date Validation into List Item forms.  Read through both posts if you want to see how this holds together.

The cool thing is that there is a JavaScript function called PreSaveAction() which is called by FORMS.JS if it is defined in the form (NewForm.aspx or EditForm.aspx).  I had always converted the default List Form Web Part (LFWP) on forms to a Data View Web Part (DVWP) in order to “hook in” my JavaScript into the submit button for validation but it turns out that you don’t necessarily need to do that if you use the PreSaveAction function instead.

So, for example, if you want to validate that a Start Date is before an End Date (as shown in the posts above), you’d add this JavaScript to your page:

<script language="javascript" type="text/javascript">
function PreSaveAction() {
    var date1 = getTagFromIdentifierAndTitle("INPUT","DateTimeFieldDate","Start Time");
    var date2 = getTagFromIdentifierAndTitle("INPUT","DateTimeFieldDate","End Time");
    var arrDate1 = date1.value.split("/");
    var useDate1 = new Date(arrDate1[2], arrDate1[0]-1, arrDate1[1]);
    var arrDate2 = date2.value.split("/");
    var useDate2 = new Date(arrDate2[2], arrDate2[0]-1, arrDate2[1]);
    if(useDate1 > useDate2)
    {
        alert("The End Date cannot happen earlier than the Start Date");
        return false; // Cancel the item save process
    }
    return true; // OK to proceed with the save item
}

// getTagFromIdentifierAndTitle: Find a form field object using its tagName, identifier, and title to find it in the page
// Arguments:
//                            tagName:            The type of input field (input, select, etc.)
//                            identifier:            The identifier for the instance of the fieldName (ff1, ff2, etc.)
//                            title:                       The title of the list column
//
function getTagFromIdentifierAndTitle(tagName, identifier, title) {
    var len = identifier.length;
    var tags = document.getElementsByTagName(tagName);

    for (var i=0; i < tags.length; i++) {
        var tempString = tags[i].id;
        if (tags[i].title == title && (identifier == "" || tempString.indexOf(identifier) == tempString.length - len)) {
            return tags[i];
        }
    }
    return null;
}

</script>

Pre-filling Column Values in a SharePoint Form

Christian Ståhl left me a good question in another post:

Have you tried to ‘formfill’ a SharePoint column for date and time with ‘today’s date’, that would be cool if its possibly?

Of course it’s possible!  You can pre-fill any columns value based on rules which you decide.  There are probably a few common situations:

  • Passing values on the Query String to pre-fill column values.  I have a post which talks a little about this scenarios already entitled Using Query String Variables to Populate SharePoint Form Fields.  That post was fairly specific to a problem I was trying to solve, but it should give you the basic steps for other situations.
  • Pre-filling based on some existing data (like the today’s date question above) . Let’s go through this scenario here.

The easiest way to set a column to today’s date and time is by using JavaScript.  Here’s an example of how this can work.  I had a client ask me to set a Date/Time column’s time to be 11 PM on NewForm.aspx (it caused some downstream issues if the time was the default 12 AM), and the code to do this is below.  The column’s name was ‘Request End Time’, and its form field label was ff2.

<script type="text/javascript">    
    _spBodyOnLoadFunctionNames.push("setRequestEndTime"); 

    // Set the Request End Time to '11 PM'    
    function setRequestEndTime() {     
        //alert('setRequestEndTime');     
        var tags = document.getElementsByTagName('select');     
        for (var i=0; i < tags.length; i++) {     
            //alert(' tags&#91;' + i + '&#93;.id=' + tags&#91;i&#93;.id);     
            if(tags&#91;i&#93;.id.indexOf('ff2_new') > 0 && tags[i].id.indexOf('DateTimeFieldDateHours') > 0) {     
                //alert('HIT tags[' + i + '].id=' + tags[i].id);     
                innerspans = tags[i].getElementsByTagName('option');     
                //alert(innerspans.length);     
                for (var j=0; j < innerspans.length; j++) {     
                    //alert('innerspans&#91;' + j + '&#93;.value=' + innerspans&#91;j&#93;.value);     
                    if (innerspans&#91;j&#93;.value == '11 PM') {     
                        innerspans&#91;j&#93;.selected = true;     
                        break;     
                    }     
                }     
            }     
        }     
    }     
</script>

The _spBodyOnLoadFunctionNames.push function lets you run any JavaScript you’d like when the form page loads.  In this case, I just have it run setRequestEndTime().  The JavaScript may look a little messy, but I’ve left in my debugging alerts in case you want to see how things happen.  The basic idea is that you find the appropriate HTML objects in the DOM in the JavaScript and then set the value you’d like.  Note that the HTML objects will be generated based on the controls which you have on the page.  You’ll want to use the Internet Explorer Developer Toolbar (IE7 and below) or the Developer Tools (IE8) to see what is actually rendered on the page for each control.

To get back to Christian’s actual question, we just need to tweak the JavaScript above a little bit to set the column’s value to today’s date.  In fact, setting the date is even simpler than setting the time:

<script type="text/javascript">   
    _spBodyOnLoadFunctionNames.push("setRequestEndTime"); 

    // Set the Request End Time to today's date   
    function setRequestEndTime() {    
        today = new Date();    
        todayDay = today.getDate();    
        todayMon = today.getMonth() + 1;    
        todayYear = today.getYear();    
        //alert('setRequestEndTime');    
        var tags = document.getElementsByTagName('input');    
        for (var i=0; i < tags.length; i++) {    
            //alert(' tags&#91;' + i + '&#93;.id=' + tags&#91;i&#93;.id);    
            if(tags&#91;i&#93;.id.indexOf('ff2_new') > 0) {    
                //alert('HIT tags[' + i + '].id=' + tags[i].id);    
                tags[i].value = todayMon + "/" + todayDay + "/" + todayYear;    
            }    
        }    
    }    
</script>

Note that we need to add one to todayMon because the month values start at 0 for January.  I’m also building up the value for today’s date in 1033 locale format because I live in the US – you would want to build this up in your locale’s format.