First of all I would like to say that quick edit JavaScript validation is not documented at all, so the methods to achieve the validation are debatable and not necessary correct. But hey – it’s still a way how to do it :) The only reads you can find on MSDN are here and here. Not very helpful, eh? There are a couple of blog posts (despite the lack of consistent examples) on this topic, but 95% of them involve custom fields and the rest 5% is far from what i needed. A good place for a head start if you’re overriding a template (creating a custom rendering) for a field is here (this covers basic stuff and has some examples which at least are consistent). If you’re creating a new custom field and want custom rendering for it – this is a good place to start for a series or articles (the examples are inconsistent, though, and might be misleading sometimes, but still a good read). And last but not least – this guy here. I was on my own on this one, but first things first…

The requirements were to override Task list field validation. The fields had to be SP-Built-in-ones, unmodified in any way (e.g. Status, Start Date, Due Date and etc.). This is relatively easily done in the forms (we ended up using custom actions, though we could use rendering template overrides as well). But what about list quick edit? The easiest way would have been to create a bunch of calculated fields but that would render them all as read only in quick edit mode and the users wouldn’t get any error messages. So we had to go with something more interesting and challenging – field template overrides. The first thing you do is “throw the hook” – you either specify the JS Link property of a field, view, or in my case – list view web part.

You can do it through the UI: Edit Page -> Edit Web Part -> Miscellaneous -> JS Link and there you specify a site collection relative path of your freshly-created JavaScript file:

web part properties
Or you can do this using Server-Side Object Model:

using (SPLimitedWebPartManager webPartManager = defaultPage.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
    var tasksListViewWebPart= webPartManager.WebParts["taskWP"];
    tasksListViewWebPart.JSLink = "/_layouts/15/QEValidation.js";
    webPartManager.SaveChanges(tasksListViewWebPart);
}

or PowerShell:

$web = Get-SPWeb http://sp2013
$webPartPage = "/home.aspx"
$webPartManager = $web.GetLimitedWebPartManager($webPartPage, [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
$webpart = $webPartManager.WebParts["taskWP"]
$webpart.JSLink = "/_layouts/15/QEValidation.js";
$webPartManager.SaveChanges($webpart)

Not all web parts have a JS Link property. You won’t be able to set it to an XsltListViewWebPart or any other web part that is not inherited from IListWebPart. The best approach, in my opinion, is setting the JS Link property of an SPView – this would work everywhere the view is being used.

Now what we need is an entry point – a JavaScript file that is loaded when a list web part is being rendered for the user. All the rendering will depend on the contents of template override methods. Since we are specifying a JS file and not a particular method, we need to execute our code somehow. This is done via an IIFE. Basically we are declaring an anonymous JavaScript function at the top of the file which will be invoked automatically. So first thing we do is handle the “load” event, add field template overrides and execute a function called “onInit” in sp.js context (the latter is needed to load some variables from SharePoint which will be used in field rendering):

(function () {
    if (typeof SPClientTemplates === 'undefined')
        return;

    fieldContext = {};
    fieldContext.Templates = {};

    fieldContext.Templates.Fields = {
        "LinkTitle": {
            "View": TitleFieldReadOnlyViewTemplate },

    fieldContext.Templates.Fields = {
        "LinkTitle": {
            "View": TitleFieldReadOnlyViewTemplate },

    [....]

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(fieldContext);

    $(document).ready(function () {
        SP.SOD.executeFunc('sp.js', 'SP.ClientContext', onInit);
    });
})();

We need to register template overrides before onInit, because latter will be executed asynchronously. Doing this the opposite way (registering template overrides at the end of onInit) would cause problems as all the fields would have already been rendered using default templates. Since field rendering (read-only field, different errors) is dependent on some SharePoint variables (like current user) we will need to include checks in the rendering methods. You can add New, Edit and Display form rendering overrides as well by doing this:

fieldContext.Templates.Fields = {
    "LinkTitle": {
        "View": TitleFieldReadOnlyViewTemplate,
        "DisplayForm": TitleFieldDisplayFormTemplate,
        "NewForm": TitleFieldNewFormTemplate,
        "EditForm": TitleFieldEditFormTemplate }
     };

What you specify on the right-hand side of the colon (e.g. TitleFieldReadOnlyViewTemplate) is a function which will be responsible for field rendering and which should return field contents as HTML. I will not be covering other than View templates in this article. Let’s continue with an example of the rendering method:

function TitleFieldReadOnlyViewTemplate(ctx) {
    var columnName = "Title";
    var quickEditId = "QEDIT_TITLE";

    ReadOnlyViewTemplateInstance(ctx, columnName, quickEditId);

    return RenderField(ctx, columnName);
}
function ReadOnlyViewTemplateInstance(ctx, columnName, quickEditId) {
    cellContext = ctx;

        SP.SOD.executeOrDelayUntilScriptLoaded(function () {
            SP.GanttControl.WaitForGanttCreation(function (ganttInstance) {

                ChangeColumnGetEditControlName(ganttInstance, columnName, quickEditId);

                    SP.JsGrid.PropertyType.Utils.RegisterEditControl(quickEditId, function (ctx) {
                        var editorInstance = new SP.JsGrid.EditControl.EditBoxEditControl(ctx, null);

                        editorInstance.SetValue = function (value) {
                            if (cellContext.inGridMode === true) {
                                _cellContext = editorInstance.GetCellContext();
                                editorInstance._cellContext = _cellContext;

                                if (IsUserOperationManager == true) {
                                    _cellContext.SetCurrentValue({ localized: value });
                                } else if (value != _cellContext.originalValue.localized) {
                                    ctx.jsGridObj.SetCellError(_cellContext.record.recordKey, columnName, "You cannot change the value of this field");
                                }
                            }
                        };

                        editorInstance.Unbind = function () {
                            if (cellContext.inGridMode === true) {
                                _cellContext = editorInstance._cellContext;

                                if (_cellContext != null) {
                                    ctx.jsGridObj.ClearAllErrorsOnCell(_cellContext.record.recordKey, columnName);
                                }
                            }
                        };

                        return editorInstance;
                    }, []);

            });
        }, "spgantt.js");
}

TitleFieldReadOnlyViewTemplate does two things:

  • Registers a new edit control and overrides it’s methods (ReadOnlyViewTemplateInstance);
  • and

  • Renders the field depending on it’s type and returns field contents as HTML (RenderField).

Let’s get to the most interesting part of this slop – ReadOnlyViewTemplateInstance method. As I mentioned before – there is absolutely no documentation about this, but the files involved in the background logic are: spgantt.js and jsgrid.js. Basically, every time a cell is rendered, an SP.GanttControl is created. Then it’s Init() method is called which therefore creates an instance of SP.JsGrid.JsGridControl. Let’s take a closer look at the spaghetti code from a bit earlier:

SP.SOD.executeOrDelayUntilScriptLoaded(function () {
    SP.GanttControl.WaitForGanttCreation(function (ganttInstance) {
    });
}, "spgantt.js");

This part gets called every time a Gantt control is being created. Great, this is exactly what we want! What we do not want is the Gantt control creating a default SP.JsGrid.JsGridControl – we want it customized. So the next thing we do is change column function fnGetEditControlName so it returns our unique control id (ChangeColumnGetEditControlName) and then we override the creation of the SP.JsGrid.JsGridControl itself:

SP.JsGrid.PropertyType.Utils.RegisterEditControl(quickEditId, function (ctx) {
    var editorInstance = new SP.JsGrid.EditControl.EditBoxEditControl(ctx, null);

    return editorInstance;
}, []);

Great! Now we have a default edit control. We need to override a couple of methods and we’ll be good to go:

editorInstance.SetValue = function (value) {
    if (cellContext.inGridMode === true) {
        _cellContext = editorInstance.GetCellContext();
        editorInstance._cellContext = _cellContext;

        if (IsUserOperationManager == true) {
            _cellContext.SetCurrentValue({ localized: value });
        } else if (value != _cellContext.originalValue.localized) {
            ctx.jsGridObj.SetCellError(_cellContext.record.recordKey, columnName, "You cannot change the value of this field");
        }
    }
};

We want to interact only when users are changing data, so first we check if the cell is in edit mode (cellContext.inGridMode). When a user enters a value in the cell, SP.JsGrid.EditControl.EditBoxEditControl.SetValue is being called. By default this method sets a new value without doing any checks. So first we need to check if the user has permissions to edit this field and then either set a new value or display a cell error.

editorInstance.Unbind = function () {
    if (cellContext.inGridMode === true) {
        _cellContext = editorInstance._cellContext;

        if (_cellContext != null) {
            ctx.jsGridObj.ClearAllErrorsOnCell(_cellContext.record.recordKey, columnName);
        }
    }
};

This method is being called when cell is loosing focus. If a user doesn’t have permissions and a new value is not set, we clear any existing errors and unbind the cell. We could have registered a DisplayControl instead of EditControl, but we need to override templates earlier than get user permissions – so the only way to go is register an EditControl and make it read-only when needed.

That’s about it if you want to change a simple text box and add some custom validation to it. If you want to go deeper, take a look at JsGrid.debug.js. You’ll find that an EditBoxEditControl is a small drop in the ocean. If you want to get a good grasp of how this stuff works get down to bedrock of SP.JsGrid.EditControl.EditBoxEditControl class.

Hope this helps!

Paul.