Using JSLink for custom field rendering in SharePoint 2013

Improving the image upload experience in SharePoint 2013.

One of our clients recently asked us about improving the image upload experience in SharePoint 2013. There are currently two ways (out of the box) to accomplish this; by pasting in the image source into a URL field, or by using the out of the box image upload functionality provided by SharePoint. The problem with the out of the box experience is that the upload form can be a little confusing for content authors, and contains fields that may confuse them. It is also missing features, such as drag and drop uploading.

One of the new features included in SharePoint 2013, is JSLink. JSLink is a field property that can be used to override how a field is rendered, and how its value is set. In the example below, we’re going to use JSLink to substitute the out of the box image upload functionality, with an HTML5 file input element (which allows for drag and drop).

First, we need to create our field, and add a reference to the JSLink file.

<elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <field id="{2F91A777-2D5A-4F32-8950-6DD6ED5D8A52}" name="CustomImage" displayname="Custom Image" type="URL" format="Image" group="Custom Columns" jslink="~sitecollection/Scripts/jslink/custom_image.js"></field>
</elements>

Next, we’ll need to create an empty module we can use to save the script file into the Style Library.

<elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <module name="Scripts" url="Style Library">
        <file path="Scripts\jslink\custom_image.js" url="jslink/custom_image.js"></file>
    </module>
</elements>

Finally, the code which will reside in your jslink javascript file (in this case, custom_image.js). In this example, I’ve only manipulated the new and edit views to provide a richer upload experience. As you’ll see in the code below, I have one utility function to parse the file into an array buffer before uploading it to SharePoint using the REST API, and passing the ‘imgPath’ variable back to our field.

/**
 * JSLink override to be used with a custom image field for more user friendly upload experience.
 */
(function ($, window, document) {
    'use strict';

    var customImageCtx = {};

    /**
     * Initialization
     */
    function init() {

        customImageCtx.Templates = {};

        customImageCtx.Templates.Fields = {
            // CustomImage is the name of our field.
            // When an item with the CustomImage field enters new, or edit, our associated function will be fired.
            'CustomImage': {
                'NewForm': customImageNewEdit, // Override the new form view rendering
                'EditForm': customImageNewEdit // Override the edit form view rendering
            }
        };

        // Register the custom template
        SPClientTemplates.TemplateManager.RegisterTemplateOverrides(customImageCtx);
    }

    /**
     * Rendering template for custom image new and edit forms
     */
    function customImageNewEdit(ctx) {

        var frmCtx = SPClientTemplates.Utility.GetFormContextForCurrentField(ctx);

        // Register a callback to upload the file which will be called when the form initializes
        frmCtx.registerGetValueCallback(frmCtx.fieldName, function () {

            var imgPath,
                img = document.getElementById('customImage').files[0];

            // Get array buffer to use for uploading file to SharePoint
            var arrayBuffer = getFileBuffer(img);

            // Upload file to SharePoint using REST API
            $.ajax({
                url: "/_api/web/lists/getByTitle('Site Collection Images')/RootFolder/Files/add(url='" + img.name + "', overwrite=true)",
                type: 'POST',
                async: false,
                data: arrayBuffer,
                processData: false,
                headers: {
                    "Accept": "application/json;odata=verbose",
                    "X-RequestDigest": $("#__REQUESTDIGEST").val()
                },
                success: function (data) {
                    // Set the imgPath variable.
                    imgPath = data.d.ServerRelativeUrl;
                },
                error: function (err, errMsg) {
                    alert(errMsg);
                }
            });

            // After the file was successfully uploaded, return the imgPath url to our field.
            if (imgPath) {
                return imgPath;
            }
        });

        // Render the HTML5 file input in place of the OOTB upload field. This is where we define the input
        return '<input type="file" id="customImage" />';
    }


    /**
     * Get file buffer
     */
    function getFileBuffer(file) {
        var deferred = $.Deferred(),
            reader = new FileReader();
        reader.onload = function (e) {
            deferred.resolve(e.target.result);
        }
        reader.onerror = function (e) {
            deferred.reject(e.target.error);
        }
        reader.readAsArrayBuffer(file);
        return deferred.promise();
    }

    // Run our intiialization
    init();

})($h || jQuery, window, document);

Stories say it best.

Are you ready to make your workplace awesome? We're keen to hear what you have in mind.

Interested in learning more about the work we do?

Explore our culture and transformation services.

Our commitment to reconciliation

Learn how Habanero is responding to the Truth and Reconciliation Calls to Action as a settler-owned company operating on Indigenous territories across what is now called Canada.

Read about our commitment