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);