JSGrid Basics
By Anatoly Mironov
JSGrid is the javascript framework in SharePoint used in Quick Edit View (previously Datasheet View). There are a few very good blog posts on this topic (See below in “Sources”). Nevertheless the fact is that jsgrid and working with quick edit from a developer’s perspective is a huge undiscovered area. Articles I have seen are intended for advanced developers. The goal with my post today is to outline the very basics of working with JSGrid. When you know the basics you will be more comfortable to discover and try out more. The example I want to show is a jsgrid code for a “VerySimpleColumn”. The source code can be found on gist.github.com To focus on jsgrid, I assume you have knowledge and some experience of working with jslink, which is related to jsgrid.
A word of caution before we start
JSGrid is an undocumented part of SharePoint javascript “ecosystem”, neither it is a part of the official SharePoint javascript api. So actually we should not use it. On the other hand JSGrid indicates something that is more like a full-fledged javascript api because:
- It seems to be carefully prepared (all possible situations are covered)
- The api is human-readable. The events and properties are called OnBeginEdit, OnEndEdit, Focus, BindToCell and so on (opposed to the properties in the SP.Microfeed.js like $v_1, $v_2)
- It follows many best-practices for handling the UI in javascript, eventhough some constructs are clumsy, e.g. using absolute-positioned overlays on top of original table cells while editing a cell value.
So to me it seems quite okay to use it in real applications, but we have to live with the risk that the jsgrid api will be changed without any notice to us developers.
Set up a site column with a custom jslink
The first step is to set up everything so we can start discovering jsgrid. It is just a walkthrough, not a detailed explanation. If you are eager to look at jsgrid, jump directly to that section. In this example we’ll apply a jslink to a custom site column. The very same jslink will be used in jsgrid, too. First of all add a new Site Column, call it VerySimpleColumn. The type is Multiple lines of text: Let us put it in a new group: Tolle Columns (beautiful, huh?) Three lines of plain text. Nothing extravagant. After that we can verify that the column exists. Fine. Now we have to update the jslink property for the new column. Make it in an app: Now add a custom list, call it “TolleList”: In the List Settings add the new site column, then you’ll see this: Now upload an empty js file to the Style Library, call it field.jslink.verysimplecolumn.js: It goes so fast :) Now it is time to implement some jslink code
Writing jslink
I’ve been thinking a while. What example could I use for that jslink? I want it to be a very simple example, so we don’t need to concentrate on actual rendering logic. You can see sophisticated examples that I listed below in “Sources”. Here we will be using this example: We’ll append a text to the field value: “This field is fully controlled by jslink”. This text should be visible on all forms, but it is not a part of the actual field value. Well here it is: The code should appear simple for us who have written at least some jslink code. [code language=“javascript”] (function () { function verySimpleView(ctx, field) { var wrapper = ‘This field is fully controlled by jslink{0}’; var value = ctx.CurrentItem[ctx.CurrentFieldSchema.Name] || “”; return String.format(wrapper, value); } function verySimpleNewAndEdit(ctx) { var wrapper = ‘This field is fully controlled by jslink<input type=“text” id="{0}" value"{1}"/>’; var id = ’tolle-’ + new Date().getTime(); var formCtx = SPClientTemplates.Utility.GetFormContextForCurrentField(ctx); formCtx.registerGetValueCallback(formCtx.fieldName, function () { var input = document.getElementById(id); return input.value; }); var value = ctx.CurrentItem[ctx.CurrentFieldSchema.Name] || “”; var html = String.format(wrapper, id, value); return html; } var overrideContext = {}; overrideContext.Templates = overrideContext.Templates || {}; overrideContext.Templates.Fields = { ‘VerySimpleColumn’: { ‘View’: verySimpleView, ‘DisplayForm’: verySimpleView, ‘EditForm’: verySimpleNewAndEdit, ‘NewForm’: verySimpleNewAndEdit } }; SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideContext); })(); [/code] This is how it looks like in the DispForm:
JSGrid. First Step: Disable the field in Quick Edit
Now we are ready to handle the Quick Edit. What we’ll do first is to disable editing our column. Why? Let’s say we have some complicated logic for rendering and editing of our field, we don’t the quick edit override it. So before we start implementing the QuickEdit part of the field, we want to disable it. Here, perhaps, ends the implementation of Quick Edit for some business cases. To determine the Quick Edit we can use the propery of ther Render Context called inGridMode. The Quick Edit list uses the “View” template: [code language=“javascript” highlight=“2,7”] function handleGridMode(ctx, field) { field.AllowGridEditing = false; } function verySimpleView(ctx, field) { var wrapper = ‘This field is fully controlled by jslink{0}’; var value = ctx.CurrentItem[ctx.CurrentFieldSchema.Name] || “”; if (ctx.inGridMode) { handleGridMode(ctx, field); } return String.format(wrapper, value); } [/code]
Wiring up a jsgrid EditControl
When our code works in all forms and the values are not “damaged” in the Quick Edit View, then the next step is to create an Edit Control that is used by the jsgrid “engine”. The very minimal Edit Control object is as follows. It contains a few event listeners and properties: [code language=“javascript”] { SupportedWriteMode: SP.JsGrid.EditActorWriteType.LocalizedOnly, SupportedReadMode: SP.JsGrid.EditActorReadType.LocalizedOnly, BindToCell: function () { console.log(“tolle BindToCell”); }, OnCellMove: function () { console.log(“tolle OnCellMove”); }, Focus: function () { console.log(“tolle Focus”); }, OnBeginEdit: function () { console.log(“tolle OnBeginEdit”); }, OnEndEdit: function () { console.log(“tolle OnEndEdit”); }, Unbind: function () { console.log(“tolle Unbind”); }, Dispose: function () { console.log(“tolle Dispose”); } } [/code] This edit control is created and returned in a function that is called: createVerySimpleColumnGridEditControl. [code language=“javascript” highlight=“1,2”] var createVerySimpleColumnGridEditControl = function (gridContext, cellControl) { return { SupportedWriteMode: SP.JsGrid.EditActorWriteType.LocalizedOnly, SupportedReadMode: SP.JsGrid.EditActorReadType.LocalizedOnly, BindToCell: function () { console.log(“tolle BindToCell”); }, OnCellMove: function () { console.log(“tolle OnCellMove”); }, Focus: function () { console.log(“tolle Focus”); }, OnBeginEdit: function () { console.log(“tolle OnBeginEdit”); }, OnEndEdit: function () { console.log(“tolle OnEndEdit”); }, Unbind: function () { console.log(“tolle Unbind”); }, Dispose: function () { console.log(“tolle Dispose”); } } }; [/code] Now we have to register our Edit Control when jsgrid is ready. We have to write a callback function and invoke SP.GanttControl.WaitForGanttCreation. Inside the callback function we register our Edit Control for our field: SP.JsGrid.PropertyType.Utils.RegisterEditControl [code language=“javascript” highlight=“3,19”] function handleGridMode(ctx, field) { window.SP.SOD.executeOrDelayUntilScriptLoaded(function () { window.SP.GanttControl.WaitForGanttCreation(function (ganttChart) { var verySimpleColumn = null; var editId = “EDIT_TOLLE_VERYSIMPLEFIELD”; var columns = ganttChart.get_Columns(); for (var i = 0; i < columns.length; i++) { if (columns[i].columnKey == “VerySimpleColumn”) { verySimpleColumn = columns[i]; break; } } if (verySimpleColumn) { verySimpleColumn.fnGetEditControlName = function (record, fieldKey) { return editId; }; window.SP.JsGrid.PropertyType.Utils.RegisterEditControl(editId, function (gridContext, cellControl) { return createVerySimpleColumnGridEditControl(gridContext, cellControl); }, []); } }); }, “spgantt.js”); } [/code] In our first version of the Edit Control we only log the event names to the web browser console. It works.
Implementing the actual editing
When working on this I did small changes to these events and tried it out in the web browser. I’d recommend it to you, too. Trying out is the best way of learning. Here comes the functions that we need to have to implement:
- createVerySimpleColumnGridEditControl (“constructor”). Here we initialize the edit control
- bindToCell. Here we get the cellContext that we save as a “private” variable on that Edit Control object. The cell context is needed to get and set the field value.
- focus. Here we define what element should be focused. In this case we forward the focus event to the actual input
- onBeginEdit. Here we show the Edit Control and make it editable.
- onEndEdit. Here we save the field value and hide the edit control.
Some notes on the Edit Control and Events
The Edit Control has a “container” - a html element that contains the representation of the field in edit mode. The Edit Control Container is an overlay - an absolutely-positioned element that exists outside the actual listview. We must set the position and we are responsible for hiding it when the field is not edited: [code language=“javascript”] container.style.cssText = ‘visibility:hidden;position:absolute;top:0;left:0;background:#ffffff;’; [/code] We also need to set the dimensions of the edit control container. It is quite easy using the information from cellContext: [code language=“javascript” highlight=“7”] var bindToCell = function(ctx) { cellContext = ctx; //An input is put as an overlay. //We have to set the width and height so that it takes the whole cell place container.style.minWidth = cellContext.cellWidth + ‘px’; container.style.width = cellContext.cellWidth + ‘px’; container.style.height = cellContext.cellHeight + ‘px’; console.log(“tolle BindToCell”); }; [/code] In OnBeginEdit we have to show the container, and in OnEndEdit we have to hide it [code language=“javascript”] //OnBeginEdit cellContext.Show(container); //OnEndEdit cellContext.Hide(container); [/code] We have to save the value using the cellContext. [code language=“javascript” highlight=“2”] var value = input.value; cellContext.SetCurrentValue({ localized: value }); [/code]
Final code [code language=“javascript” title=“field.jslink.verysimplecolumn.js”] (function () { var editWrapper = ‘This field is fully controlled by jslink<input type=“text” id="{0}" value"{1}"/>’; var createVerySimpleColumnGridEditControl = function (gridContext, gridTextInputElement) { var cellContext, inEdit, html, container, input, id; id = “tolle-” + new Date().getTime(); html = String.format(editWrapper, id, “”); container = document.createElement(“div”); container.innerHTML = html; input = container.getElementsByTagName(“input”)[0]; container.style.cssText = ‘visibility:hidden;position:absolute;top:0;left:0;background:#ffffff;’; gridContext.parentNode.appendChild(container); var bindToCell = function(ctx) { cellContext = ctx; //An input is put as an overlay. We have to set the width and height so that it takes the whole cell place container.style.minWidth = cellContext.cellWidth + ‘px’; container.style.width = cellContext.cellWidth + ‘px’; container.style.height = cellContext.cellHeight + ‘px’; console.log(“tolle BindToCell”); }; var onCellMove = function () { console.log(“tolle OnCellMove”); }; var focus = function(eventInfo) { input.focus(); console.log(“tolle Focus”, eventInfo); }; var onBeginEdit = function (eventInfo) { inEdit = true; var currentValue = cellContext.originalValue.localized; if (currentValue) { input.value = currentValue; } cellContext.Show(container); console.log(“tolle OnBeginEdit”); this.Focus(eventInfo); }; var onEndEdit = function() { cellContext.Hide(container); inEdit = false; var value = input.value; cellContext.SetCurrentValue({ localized: value }); console.log(“tolle OnEndEdit”); }; var unbind = function() { console.log(“tolle Unbind”); }; var dispose = function () { console.log(“tolle Dispose”); } return { SupportedWriteMode: window.SP.JsGrid.EditActorWriteType.LocalizedOnly, SupportedReadMode: window.SP.JsGrid.EditActorReadType.LocalizedOnly, BindToCell: bindToCell, OnCellMove: onCellMove, Focus: focus, OnBeginEdit: onBeginEdit, OnEndEdit: onEndEdit, Unbind: unbind, Dispose: dispose } }; function handleGridMode(ctx, field) { window.SP.SOD.executeOrDelayUntilScriptLoaded(function () { window.SP.GanttControl.WaitForGanttCreation(function (ganttChart) { var verySimpleColumn = null; var editId = “EDIT_TOLLE_VERYSIMPLEFIELD”; var columns = ganttChart.get_Columns(); for (var i = 0; i < columns.length; i++) { if (columns[i].columnKey == “VerySimpleColumn”) { verySimpleColumn = columns[i]; break; } } if (verySimpleColumn) { verySimpleColumn.fnGetEditControlName = function (record, fieldKey) { return editId; }; window.SP.JsGrid.PropertyType.Utils.RegisterEditControl(editId, function (gridContext, cellControl) { return createVerySimpleColumnGridEditControl(gridContext, cellControl); }, []); } }); }, “spgantt.js”); } function verySimpleView(ctx, field) { var wrapper = ‘This field is fully controlled by jslink{0}’; var value = ctx.CurrentItem[ctx.CurrentFieldSchema.Name] || “”; if (ctx.inGridMode) { handleGridMode(ctx, field); } return String.format(wrapper, value); } function verySimpleNewAndEdit(ctx) { var id = ’tolle-’ + new Date().getTime(); var formCtx = window.SPClientTemplates.Utility.GetFormContextForCurrentField(ctx); formCtx.registerGetValueCallback(formCtx.fieldName, function () { var input = document.getElementById(id); return input.value; }); var value = ctx.CurrentItem[ctx.CurrentFieldSchema.Name] || “”; var html = String.format(editWrapper, id, value); return html; } var overrideContext = {}; overrideContext.Templates = overrideContext.Templates || {}; overrideContext.Templates.Fields = { ‘VerySimpleColumn’: { ‘View’: verySimpleView, ‘DisplayForm’: verySimpleView, ‘EditForm’: verySimpleNewAndEdit, ‘NewForm’: verySimpleNewAndEdit } }; window.SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideContext); })(); [/code]
Summary
Well, jsgrid is quite complicated. I have tried to keep it as simple and minimalistic as possible. When we can these basics, we can move on and master advanced examples where we can deliver high value to business that loves Quick Edit :)
Sources
- Source code of /_layouts/15/jsgrid.debug.js
- Anton Avishnyakov’s blog, spdevlab.com
- Custom Field Type for SharePoint 2013 – Custom “Quick Edit” mode rendering, Visa field example.
- Source code for custom fields for SharePoint 2013, including the support for Quick Edit (codeplex)
- Andrey Markeev’s blog posts
- Master-detail with JSGrid. An extremely advanced but pedagogically explained article with source code (Code Project)
- A whole series about JSGrid (in Russian, awesome content)
Interesting, Anton, Andrey and me are born in Sovyet and are interested in JSGrid. Coincidence? :)
Comments from Wordpress.com
Disabling a column in Quick Edit - Bool Tech - Dec 1, 2014
[…] Yesterday I wrote about jsgrid in my blog. Now comes more. Today I’ll share a little practical solution how one can disable editing a field in Quick Edit. […]
[…] has a better layout and uses JSGrid to render the data. Check this post for details about that ( chuvash.eu/2014/11/27/jsgrid-basics/ […]
“Now we have to update the jslink property for the new column. Make it in an app:”… Can you please elaborate on this? I am tryig this in sharepoint online. Do i need to write provided hosted app to link my custom js to this verysimplecolumn field?
Good Point. You can do it in many ways: provider hosted app, console app, you can also update this using JSOM. As the matter of fact, I am about to publish a tiny tool - a bookmarklet - for updating jslink. Stay tuned.
Hello, Any idea how we can handle the copy paste events into the JSgrid.In IE we can copy from excel and paste(like in excel). I need to validate some columns on bulk paste.However none of the handlers are being called when its pasted from clip board. Any help appreciated
great article! Was very, very helpful. Thank you!
Hi, i am fresher in my organisation, i have doubt in QuickEdit in sharepoint 2013 using CSR. 1. I have changed three single line text into three drop down choice field in New Form, Edit form using CSR (Cascading functionality). 2. i have changed even in quick edit from single line to drop down, but when i clicked in the field to edit. it shows the original value of single line text. 3.kindly let me know how to deal with it. Thanks kamalesh Paul
On enter or Tab key, how do you come out of the custom Edit Control created within cell? Please help!
Hello, I had changed three single line text into three drop down choice field in edit form using CSR (Cascading functionality). now i want do same functionality in quick edit. kindly let me know how to deal with it..