Form Validation and Custom Save Functionality in the Standard SharePoint List Form Reply

Mike BerrymanI have a client that needs to log any interactions with their customers in SharePoint.  This client is proficient enough with SharePoint that they feel perfectly comfortable modifying lists in order to add or remove fields to accommodate their needs without having to go through us.  They are regularly adding new fields to this log list in order to capture some new piece of relevant information that it was decided they needed.  This is important background because when they approached me to create a way for them to generate multiple log entries from one “New Item” SharePoint List Form, I knew I couldn’t just create a custom “New Item” form to accomplish this or they would lose the ability to add new fields without having to either modify this custom “New Item” form, or involve us to make those modifications.

So I had to come up with a solution that would retain the standard “New Item” form (so any fields added/removed from the list would be reflected in the form) but would allow for the user to create an entry in their logs for every customer selected in the form.

Since I had to keep using the Out-of-box (OOB) “New Item” form, my only real option was to use Javascript to modify the form to do what was required.  I used script injection (which I talk about in this blog post) to add a script file to the entire SharePoint site which checks if the user is viewing the “New Item” form for the target SharePoint list.  If the user is on that page, the script will perform some modifications to the form.

I also added a “Multiple Customers” field to the list.  Using the script mentioned above, when viewing the New/Edit/Display forms for the list this field would automatically be hidden from view.  It is only when the user is viewing the “New Item” form with a special query parameter that this field displays.  At this point the script knows the user is intending to do a multi-entry log and so the default Save functionality needs to be overwritten so instead of a single entry getting created when the user hits “Save”, an entry for every chosen customer is created.  The focus of this post is not to go into details regarding the actual save, so to summarize it: The Javascript removed the OnClick event of the “Save” button and replaced it with a custom save function that uses REST calls to create a log entry for each selected customer.  By itself this worked great, however by circumventing the default save functionality of the “New Item” form, validation is also skipped.

This was not acceptable because this log entry consists of quite a few fields and it’s not uncommon for a user to forget to fill something in.  The validation would prevent the user from submitting an incomplete form – without it, fields that normally wouldn’t be allowed to be left blank could now be empty.

At first I tried to utilize the Validation portion of the OOB save functionality.  SharePoint uses Javascript to validate and submit List Forms so I figured I would be able to simply call only the Validate portion of this script.  After deciphering a minified script that handles List Forms I discovered that the object that wraps all this functionality only exposes a handful of functionality publicly, and the exposed function that performs validation will also submit the form if validation passes.  This wouldn’t work for me because I only needed to know if the form was valid and if so perform my own save functionality.

Then I tried to “inject” a version of the save function onto the List Forms object that only does validation.  I had deciphered the minified code so knew exactly what the function needed to do.  However this is where I discovered that objects in javascript that are created via a function do not expose any variables declared within that function outside their own scope.  So while I was able to “inject” a ValidateOnly() function onto the List Forms object, it couldn’t see the variables necessary to actually do what was needed.

After that I tried to create my own version of the List Form object in my script.  I’m not 100% sure why this didn’t work but basically when I tried to “attach” my List Form object to the “New Item” form, it wouldn’t fill in a lot of the private variables used to perform validation such as the list of fields on the form.  My best guess as to why is that the rendering portion of the List Forms object (yes, it does some rendering) is what fills some of these private variables in and since the form already exists on the page by the time my custom version tries to do its thing, certain functionality just doesn’t run.

So after all these attempts I fell back to creating my own custom validation.  It still needed to be robust enough to handle the client adding/removing fields from the list, so I needed to use information in the DOM in order to create a code representation of the form.  When a SharePoint form renders it includes as part of every field a comment node that has the Display Name, Internal Name and Field Type of the field. Here’s a useful function I wrote up to extract this information from those comment nodes:

var getAllFieldsOnForm = function(includeAttachments) {
    if (typeof(includeAttachments) !== "boolean") {
        includeAttachments = false;
    }
    var ret = [];
    
    var commentNodes = $("td.ms-formbody", "table.ms-formtable").contents().filter(function () {
        return this.nodeType === 8;
    });
    
    $(commentNodes).each(function(idx, node) {
        var chunks = node.nodeValue.split('\n');
        var dispName = "";
        var intName = "";
        var type = "";
        var isHidden = false;
        var isRequired = false;
        for (var i = 0; i < chunks.length; i++) {
            if (chunks[i] === null) {
                continue;
            }
            var trimmed = chunks[i].trim();
            if (trimmed.indexOf("FieldName=") > -1) {
                var chunks2 = trimmed.split('=');
                if (chunks2.length > 1) {
                    dispName = chunks2[1].replace(/"/g, '');
                }
            }
            if (trimmed.indexOf("FieldInternalName=") > -1) {
                var chunks2 = trimmed.split('=');
                if (chunks2.length > 1) {
                    intName = chunks2[1].replace(/"/g, '');
                }
            }
            if (trimmed.indexOf("FieldType=") > -1) {
                var chunks2 = trimmed.split('=');
                if (chunks2.length > 1) {
                    type = chunks2[1].replace(/"/g, '');
                }
            }
        }
        if (type === "SPFieldAttachments" && !includeAttachments) {
            return;
        }
        var fieldFormRow = $(node).closest("tr");
        isHidden = !$(fieldFormRow).is(":visible");
        var labelCell = $("td.ms-formlabel", fieldFormRow);
        isRequired = $("span.ms-accentText", labelCell).length !== 0;
        ret.push({
            displayName: dispName,
            internalName: intName,
            spType: type,
            hidden: isHidden,
            required: isRequired
        });
    });
    
    return ret;
};

Note that this javascript does utilize jQuery.

This produces an array of objects that let us know useful information about every field on the form.  In our case we also had some fields that the scripts were hiding (like the Multiple Customers field) so we needed to know if a field was actually showing on the form or not so we could skip it when checking for a valid form.  Using this I was able to write functionality to a) validate the form and b) create a dynamic JSON object to submit via REST to create a list item (because we know the internal field name).  If the form is invalid we replicate the error message the OOB functionality uses so to the end user everything will look “normal”.

So through the power of Javascript I was able to create my own custom save functionality injected onto an OOB SharePoint List Form.  I definitely foresee this being used again in the future.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s