Tag Archive for Custom Field Types

Custom Field Types in SharePoint 2013 Apps

Synopsis and Key Take-Aways

Custom field types in SharePoint provide the ability to customize all aspects of SharePoint form fields– from how they are displayed, to how values are validated to how values are stored within SharePoint and a couple of other things along the way. They provide significant capability for enforcing business rules and providing a much more user friendly experience than what is available via the out-of-the-box field types.  Unfortunately, this power has never been available in a cloud-hosting scenario such as Office 365.  Things are changing for the better, however, with new capabilities available in SharePoint 2013 Preview.  We now have the ability to provide the same functionality to our end users without running custom code on the server.  With SharePoint 2013 Preview, we can modify the presentation and validation of a custom field (or technically any field) on any form in SharePoint as well as in Views simply via JavaScript.  This is an incredibly powerful capability.  It provides a nice, easy, standard (and supported) way of customizing the end user experience to be more efficient and friendly.

This article introduces the scenario, walks through the end user experience using a sample custom field and then dives into a code-level review of how to implement this new functionality.  The commented source code listing for the required JavaScript is available for download.

Note that as of the initial publication of this article (October 2012), this is based on the SharePoint 2013 Public Preview.  Once I can download RTM I’ll look to revisit this and update the article as necessary.

Introduction

Custom field types in SharePoint provide the ability to customize all aspects of SharePoint form fields– from how they are displayed, to how values are validated to how values are stored within SharePoint and a couple of other things along the way.  They provide significant capability for enforcing business rules and providing a much more user friendly experience than what is available via the out-of-the-box field types (single line of text, yes/no, date/time, user, url, etc.) Unfortunately, this power and flexibility is simply not available in a cloud scenario such as Office 365 because they require that files be deployed to the server file system which you can’t do in a cloud solution.  For the 2010 release of SharePoint, there have been various attempts at simulating custom field types in a cloud scenario, but all have suffered from varying degrees of fragility or other problems. Fast-forward to SharePoint 2013 and Microsoft has made things much easier for developers and much less fragile all around with regard to custom field types.  For the first time, it is possible to truly get most of the value of custom field types in a cloud scenario such as Office 365 or an on-premises scenario (without all of the work of doing it the old way).  Notice that I said most of the value.  There are some things that are still not possible and two major caveats of which you must be aware.  First the caveats:

  1. Even though we are building things which look and act like custom field types, we are not technically building custom field types.  We are technically just customizing the rendering and validation of the out of the box field types.  More on this later; just remember it for now
  2. All of our customizations are happening on the client side via JavaScript.  There are implications to this for your data and business rules.  What happens if a user has JavaScript turned off and therefore your customizations do not run?  What are the implications to the security and validity of your data?  We will touch briefly on custom validation later, but just keep it in mind – it’s not guaranteed to run)

Acknowledgements

Before going any further, I need to give credit where credit is due.  Andrew Connell and I hacked up the first portion of this together.  I took the rough POC AC and I had pulled together that almost did everything we wanted, finished it off and cleaned it up to produce the samples in this article.  Keep an eye on AC’s blog as he’ll be posting a follow-up article taking some of this in new directions.

Overview of the Solution

With all of that said, let’s see what it is that we’re going to get at the end of this.  Here are some screenshots and descriptions of what we’re going to build out in the rest of this article:

The New Item Form

In this example, we’ve customized the rendering of our custom field:image

Site Column 1 is our custom field.  Technically, it is just a simple text field.  Without our customizations, it would render as a textbox.  Our customizations change it’s rendering to a dropdown list. The choices available in the dropdown (One through Five) are added dynamically.  In a real world scenario, these could come from a web service call.  To keep things simple and focused here, I’m simply hardcoding an array.  You’ll see when we get to the code how simple it would be to make this truly dynamic. We’ve also added some rudimentary validation.  For the sake of this demo, FOUR is not a valid choice:image

(Michael Palin would be so proud) Notice, too, that our field is marked as required in SharePoint and regular field validation continues to function:image

One of the things I’d like to play with in a later version of this is the possibilities for additional validation via CSOM and perhaps even some event receiver work to provide additional server-side validation.

Display in Views

Once we’ve entered a legal value, our item is shown in a SharePoint View.  We also have the opportunity to customize the rendering here as well: image

For the sake of this demo, I’m not actually showing the field value in the View. Instead, I show a JavaScript link that pops up a simple alert when clicked to show the field value:image

The Display Form

Again, for the sake of this demo, I’m customizing the presentation of our field on the Display form as well.  Instead of showing the actual value, I show it’s numeric equivalent:image

The Edit Form

The Edit form looks largely identical to the New form (internally, it’s actually rendered via the same code).  The only difference is that we have to make sure that the current value of the field shows as selected in the dropdown:image

Technical Walkthrough

Now that we’ve seen what we’re building, let’s dive into how to make it all work.  Once you’ve figured it all out, it’s actually pretty easy.  (Figuring it all out, though, that was a royal PITA, especially during a beta cycle). The heart of everything we’re doing here is a new property on the out-of-the-box  SPField class (and all of it’s children) within the SharePoint server object model: JSLink (don’t  bother clicking on the link – there is NO documentation worth anything there right now – just a placeholder).  Similar to the JSLink property on SPView, SPField.JSLink gives us the opportunity to specify a custom JavaScript file that will be used to render our field.  Cool. From our SharePoint 2013 App (or, really, any SharePoint 2013 solution), we add an attribute to the Elements file that defines our custom Field to specify the JSLink value:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Field
       ID="{23d7e8ae-29c3-4f05-ac4a-08bbf2bc2b1d}"
       Name="SiteColumn1"
       DisplayName="Site Column 1"
       Type="Text"
       Required="TRUE"
       JSLink="~site/Scripts/SampleJSField.js"
       Group="Custom Site Columns">
  Field>
Elements>

The SampleJSField.js file will now be used to render our field wherever it is used.  The full SampleField.js file is available at the end of this article for your reading pleasure..I’ve added some comments to explain what is going on, but the following sections spell out the highlights

IIFE

Because we are only specifying a JavaScript filename in our JSLink attribute, and not a particular method to be called, we need some way to actually execute our code.  We do this via an IIFE (Immediately Invoked Function Expression) declared right at the top of the file:

(function () {

    initFieldTypes();
    var r = new JSFieldText("SiteColumn1", viewFunc, editFunc, editFunc, displayFunc);
    r.initField()
})();

You could do this different ways, but this is the most concise and easiest.  Basically, in one shot, I’m declaring an anonymous JavaScript function and invoking it.  No magic inside the IIFE itself, it simply calls a function to set up our custom field objects (initFieldTypes), creates an instance of my custom JSFieldText object (passing in the field name and a couple of function objects – more on these later) and then initializes the field. I’m not going to go into details on the whole initFieldTypes function here.  It’s in the full source listing and it’s a bunch of pretty funky JavaScript at this point.  I plan on cleaning this all up but there’s no sense in doing that until I’m working with RTM bits.  Eventually, I’d like to centralize all of this in a common JavaScript library that can be used by multiple fields, but that will come later.  For now, it was enough just to build out a POC to show that this all works.  A good chunk of the  code in the “Global Code” section of the full source listing is based on some code from http://www.ruzee.com/blog/2008/12/javascript-inheritance-via-prototypes-and-closures but I’m not sure whether I’ll stick with that.  It introduces some complexity and I’m not sure it gets me a lot.  In the short term, however, it filled a void and let me focus on more pressing things.

Registering Our Custom Rendering

I will, however, focus on a few key areas within the initFieldTypes function.  The first is where we actually override the default rendering and tell SharePoint to use our custom rendering.  This is done via the initField function on the JSFieldText object:

if (hasValue(this.get_fieldName())) {
      var newCtx = {};

      newCtx["Templates"] = {};
      newCtx.Templates["Fields"] = {};
      newCtx.Templates.Fields[this.get_fieldName()] = {};
      //Views are handled slightly differently because of the way they are called by SP
      newCtx.Templates.Fields[this.get_fieldName()]["View"] = hasValue(this.viewCallback) ? this.viewCallback : null;
      newCtx.Templates.Fields[this.get_fieldName()]["NewForm"] = this.renderForNew;
      newCtx.Templates.Fields[this.get_fieldName()]["EditForm"] = this.renderForEdit;
      newCtx.Templates.Fields[this.get_fieldName()]["DisplayForm"] = this.renderForDisplay;

      SPClientTemplates.TemplateManager.RegisterTemplateOverrides(newCtx);
}

This code sets up an object hierarchy in a manner prescribed by SharePoint 2013 and registers the new functions to render each of our possible views of this field:

  • Views
  • Display Form
  • Edit Form
  • New Form

The last line of the snippet is what actually registers our custom handlers with  SharePoint. Views are handled slightly differently because of how they are called internally by SharePoint.  For Views, we simply pass the callback that was supplied in the constructor of our field. Note that we do check for null and undefined via some helper functions.  If we set the View or any of the Forms to a null or undefined value, SharePoint will use it’s default rendering. The Forms all use a function on our JSFieldText object because we need to do a little processing before invoking the callback. Once the callbacks are registered, they’ll be called by SharePoint instead of the default rendering mechanisms. One of the things that you’ll notice about all of these callbacks is that we’re not dealing with HTML objects.  In each case, we need to return the actual HTML text to render our desired UI, so we’re doing a lot of string manipulation.  That’s just the way SharePoint expects things to be, so we just run with it.

Technical Details: Views

The View callback is a simple method that modifies the value written into the standard SharePoint View as we saw in the screenshots above.  Here’s the code for that:

/// Function called to render the field in a View
function viewFunc(renderCtx, field, listItem, listSchema) {
    var currentVal = '(null)';
    if (renderCtx != null && renderCtx.CurrentItem != null)
        currentVal = eval('renderCtx.CurrentItem.' + field.Name);
    return " + currentVal + "')\">Show Field Value";
}

Notice that the name of our function (viewFunc in this case) was the value passed into our JSFieldText constructor earlier.  The other callbacks operate the same way.  Note also the use of the eval statement in there.  I’m not thrilled with that and would like to find a way around it but haven’t come up with anything yet.

Technical Details: Edit Form

The Edit Form callback is used to render the field on an Edit Form.  Here’s the code for the sample I built:

/// Function called to render the NewForm & EditForm view of the field
function editFunc(renderCtx) {
    var fieldHtml = '  ';
    fieldHtml += getStandardInputHtml(this, "hidden");
    return fieldHtml;
}

This one is a little more complex than the View, but it is still just building up an HTML string to be returned to SharePoint and ultimately stuffed down the pipe to the client’s browser.  There are a couple of helper functions that you can review in the full source code listing.  In the listing above, all I’m rendering is the select box to present the choices.  The actual field used by SharePoint is handled in the getStandardInputHtmlfunction.  Remember, it renders as a hidden field and I use a simple JavaScript function (moveValue, shown below) to copy the selected value from the dropdown list to the hidden field so it is available to SharePoint. The moveValue function also handles my custom validation:

function moveValue(targetId) {
    var mySelect = document.getElementById('myNiftySelect')
    var selectedVal = mySelect.options[mySelect.selectedIndex].text
    if (customValidationPasses(selectedVal, mySelect)) {
        var targetTextBox = document.getElementById(targetId);
        targetTextBox.value = selectedVal;
    }
}

function customValidationPasses(newValue, selectControl) {
    if (newValue === "Four") {
        alert("Four is right out! Counteth thou to THREE and then lobbeth thou the holy handgrenade of Antioch at thy foe, who, being naughty in my sight, shall snuff it.");
        selectControl.selectedIndex = selectControl.selectedIndex - 1;
        return false;
    }
    return true;
}

There’s a fair amount going on in the editFunc function, but none of it is very complex.  This is another area I’d like to work on tweaking a bit. As I said before, for the sake of this demo, I have hardcoded in an array to provide the choices for the dropdown:

//keeping it simple for the sake of this demo code - not really calling a service to get values
function getOptionsFromSimulatedWebServiceCall() {
    return ["One", "Two", "Three", "Four", "Five"];
}

If you needed to make this dynamic, simply change the code in that method (and probably itsa name, too) to do whatever you need.

Technical Details: New Form

The New Form rendering is actually handled by the editFunc function.  Notice back in the IIFE when we create the JSFieldText object that we pass editFunc in twice:

var r = new JSFieldText("SiteColumn1", viewFunc, editFunc, editFunc, displayFunc);

The first one is actually the New Form callback.  If you needed to render your field differently on the New Form, simply pass in a unique callback here.

Technical Details: Display Form

Last but not least is the Display Form.  Remember this shows the numeric representation of our field’s value (kind of lame, I know, but I wanted to show something different for each type of rendering and this was the best I could come up with).  The displayFunc function handles this for us:

/// Function called to render the DisplayForm view of the field
function displayFunc(renderCtx) {

    if (renderCtx != null && renderCtx.CurrentFieldValue != null) {
        var fieldVal = renderCtx.CurrentFieldValue.toString();
        switch(fieldVal)
        {
            case "One":
                return "1";
                break;
            case "Two":
                return "2";
                break;
            case "Three":
                return "3";
                break;
            case "Four":
                return "4";
                break;
            default:
                return "5";
        }
    }
    return '';
}