SharePoint 2013 offers a fantastic client side API for crafting custom fields. Just with 50-100 lines of JavaScript code you may create unique look and feel, new/view/edit form interface and even list view presentation for your custom field. To get started, you my check my previous post “Custom Field Type for SharePoint 2013 – VISA card field sample” and check out SharePoint 2013 custom field samples project at codeplex. However, there is one more thing to be addressed. It is custom field rendering in “Quick Edit” mode. It’s quite big topic, so in this post I will cover high level approach and the next will include some cool JavaScript code.
What is the challenge?
It turns out that we are able to specify our own render templates for the custom field, however the “Quick Edit’ experience will not be affected and will stay with default behavior. That fact just spoils all the effort has been put for the custom UI creating as in “Quick Mode” you just get a plain text box to edit text-based field, standard date time picker is used for time based fields and so on. Surely, we may wish to have a full control over render and validation process even in the “Quick Edit” mode so let’s see how it works in a nutshell and how this can be customized.
Handling custom field rendering in the “Quick Edit” mode
Well, first of all it would be nice to understand how fields rendered in the “Quick Edit” mode and how we may know what we are in grid editing.
In fact, SharePoint uses field’s list view render template to render field in both list view and “Quick Edit” modes. To simplify, I will refer to “VISA Sample Field” from my previous post. So, you have a “View” render template which could be registered like that:
spdevlab.fieldSamples.VisaField.prototype.init = function (targetFieldName) { self._fieldName = targetFieldName; var fieldName = self._fieldName; var visaFieldTemplates = {}; visaFieldTemplates["Templates"] = {}; visaFieldTemplates.Templates["Fields"] = {}; visaFieldTemplates.Templates.Fields[fieldName] = {}; visaFieldTemplates.Templates.Fields[fieldName]["View"] = self.viewTemplate; visaFieldTemplates.Templates.Fields[fieldName]["NewForm"] = self.newFormTemplate; visaFieldTemplates.Templates.Fields[fieldName]["EditForm"] = self.editFormTemplate; visaFieldTemplates.Templates.Fields[fieldName]["DisplayForm"] = self.displayFormTemplate; SPClientTemplates.TemplateManager.RegisterTemplateOverrides(visaFieldTemplates); };
Next step is understanding whether or not we are inside “Quick Edit” mode. That’s quite simple and may be done via render context flag “inGridMode”:
spdevlab.fieldSamples.DateBirthField.prototype.viewTemplate = function (renderCtx, field, listItem, listSchema) { if(renderCtx.inGridMode === true) { // "QUICK EDIT" MODE } else { // LIST VIEW MODE } };
Just one line of code allows you to make a difference in the way you render your field in the list view or “Quick Edit”. Easy start, but let’s move on!
Disabling custom field editing experience in the “Quick Edit” mode
The next case has to do with disabling editing experience in “Quick Edit” mode. Due field complexity, let’s say you field represents address or visa card data types, you may just lock down an ability to edit field in the grid. That’s quite simple either – just set up “AllowGridEditing” flag in the field schema during render:
spdevlab.fieldSamples.DateBirthField.prototype.viewTemplate = function (renderCtx, field, listItem, listSchema) { if(renderCtx.inGridMode === true) { field.AllowGridEditing = false; } // DO STUFF };
Not bad, as easy as that. Let’s see what’s going on with custom UI in “Quick Edit” mode.
Creating custom field UI in the “Quick Edit” mode
Please, do that carefully or do not do that at all!
Frankly saying, this is something I would consider to implement carefully.
First of all, there is absolutely zero documentation regarding custom UI for the “Quick Edit” mode. You may find “How to: Implement Custom Validation in the JS Grid Control” as well as some information about “JS Grid Control” but it has nothing to do with the reality. Next, I do know and I have find the way for implementation custom UI in grid mode, but I have some concerns regarding how official and legal this way is. Finally, I spent almost a week just to reverse engineer, debug and trace several SharePoint’ JavaScript files to understand and implement something what can work. I suppose to spend at least one more week to refactor and make the current prototype looks good.
We need to go deeper
Well, from a JavaScript perspective, “Quick Edit” mode and all the background code and logic flow is black box. I have not found any documentation for that, but there are several files involved:
- _layouts/15/spgantt.debug.js
- _layouts/15/jsgrid.debug.js
- _layouts/15/inplview.debug.js
If you switch to “Quick Edit”, then Client Side Render (CRS) understands that via renderCtx.inGridMode property. Then, default “RenderBodyTemplate” produces different calls and, finally, ends up with “PostRenderSPGrid” call in “inplview.debug.js”.
Next, a new instance of “SP.JsGrid.JsGridControl” is created, and wrapped by “SP.GanttControl” with Init() method call. There are A LOT OF other methods behind the scene, but the general idea looks like that.
But stop… we need to go deeper!
I would say, that the most interesting stuff is going on inside SP.GanttControl.Init() call where it registers its own render template per every field type in the grid. It look like that and provides some clues to start with:
SP.JsGrid.PropertyType.RegisterNewCustomPropType(new SPG.TextMaxLengthPropertyType(BuildMaxLengthTable(_params.customFieldInfo)), SP.JsGrid.DisplayControl.Type.Text, _editBoxControlId); SP.JsGrid.PropertyType.RegisterNewCustomPropType(new SPG.DatePropertyType(), SP.JsGrid.DisplayControl.Type.Text, _editBoxControlId, [SP.JsGrid.idgetControl.Type.Date]); SP.JsGrid.PropertyType.RegisterNewCustomPropType(new SPG.EditPropertyType(), SP.JsGrid.DisplayControl.Type.Image, _editBoxControlId); SP.JsGrid.PropertyType.RegisterNewCustomPropType(new SPG.UserPropertyType(this), SP.JsGrid.DisplayControl.Type.Text, _editBoxControlId, [SP.JsGrid.WidgetControl.Type.AddressBook]); SP.JsGrid.PropertyType.RegisterNewCustomPropType(new SPG.PercentCompletePropertyType(),SP.JsGrid.DisplayControl.Type.Text, _editBoxControlId);
Okay, looks like we are able to register our own templates (will discuss later), but what about the entry point? If we had our templates, how would we register them?
Well, the next important method to be explored is SP.GanttControl.WaitForGanttCreation(). This method allows to register a callback which is called then a new instance of SP.GanttControl has been created. That’s exactly what we are looking for! The following code allows us to be notified about all the Gantt controls creates on the page:
SP.SOD.executeOrDelayUntilScriptLoaded(function() { SP.GanttControl.WaitForGanttCreation(function(ganttInstance) { // DO STUFF }); },"spgantt.js");
So, at this point we understand two facts: (1) there is another one template engine for “Quick Edit” experience and (2) there is open API to attach and being notified then a new instance of SP.GanttControl is created. Remember, we need to go deeper! What’s next? Templates!
JSGrid/Gantt control templates overview
That’s quite complicated point and I would just provide references about the potential implementation. As you may have seen, there is a helper “SP.JsGrid.PropertyType” which provides shortcuts to register some weird stuff – templates, widgets and so on. That’s correct, it really does so. We might register our own template, but we also need to understand how it works as field rendering templates are very different in SPGantt/JSGrid controls.
There are at least two ways to craft custom experience for JSGrid/Gantt controls – edit-display controls and widgets.
You may find default render templates/widgets for JSGris in “_layouts/15/jsgrid.debug.js”. They are:
- SP.JsGrid.WidgetControl.ErrorWidget
- SP.JsGrid.WidgetControl.DateWidget
- SP.JsGrid.WidgetControl.AddressBookWidget
- SP.JsGrid.WidgetControl.HyperlinkWidget
There is also an enumeration “SP.JsGrid.WidgetControl.Type” which represents “IDs” of these widgets. So, then you see custom UI for date time editing, that’s a widget.
Next, there are “Edit Controls” which are responsible for the edit experience inside the grid cell. There are several default edit control which could be found in “_layouts/15/spgantt.debug.js”:
- SPG.MultilinePlainTextEditControl
- SPG.EnhancedRichTextEditControl
- SPG.GetEditBoxEditControl
Finally, then SPGantt control initializes, it registers widget/edit control for every field based on PropertyTypeId. “PropertyTypeId” is still a missed puzzle, so let’s go deeper.
Microsoft.SharePoint.WebControls.JSGrid and the general flow
In a nutshell, client side JSGrid control utilizes data which comes from server side. There is a Microsoft.SharePoint.WebControls.JSGrid control which prepares the data and produces necessary html markup/js script including. Here are several classes/methods to be explored:
- Microsoft.SharePoint.WebControls.JSGrid
- Microsoft.SharePoint.JSGrid.SPGridSerializerGenerator
- Microsoft.SharePoint.JSGrid.SPGridSerializerGenerator. PopulateGridFields()
PopulateGridFields() method takes SharePoint lists data and makes JSON serialization which, finally, comes to the client side and consumed by client side JSGrid. This methods also produces some meta information about field such as “PropertyTypeId“. In turn, “PropertyTypeId” is used on the client side to match the appropriate edit control/widget and render field data inside the JSGrid/Gantt control.
To wrap up, let’s check the general flow:
- Microsoft.SharePoint.WebControls.JSGrid renders the data
- Microsoft.SharePoint.JSGrid.SPGridSerializerGenerator serializes all info in JSON
- Every field gets meta information such as PropertyTypeId value
- “Quick Edit” mode gets initialized
- CRS renders the data via js code
- SP.JsGrid.JsGridControl gets created
- SP.GanttControl gets created
- SP.JsGrid.JsGridControl is wrapped by SP.GanttControl
- Widgets/Edit Controls are registered
- GanttControl iterates over rows/column to render them
- Widgets/Edit Control is looked up by the field’s PropertyTypeId
- Actual render is happened!
So, how’s bad is that? Terrible, I suppose. Make a coffee, and let’s finish that epic.
How to make a custom edit experience in “Quick Mode”
As you may see, that’s not an easy task. However, it is still possible to implements.
First of all, you need to have access to all Gantt controls on the page. You may use the following code while you initialize your custom field render templates:
(function () { var visaField = new spdevlab.fieldSamples.VisaField(); visaField.init("VisaField"); SP.SOD.executeOrDelayUntilScriptLoaded(function() { SP.GanttControl.WaitForGanttCreation(function(ganttInstance) { // DO STUFF }); },"spgantt.js"); })();
Next, for every Gantt control you need to get a column definition which is responsible for your field. Then, you may register you own callbacks to provide (1) “edit control name” (this is just sort of “ID”) and (2) edit control implementation.
An edit control must provide several methods such “BindToCell“, “OnCellMove“, “Focus“, “OnBeginEdit“, ” OnEndEdit” and so on. Basically, then you double click on you field in “Quick Mode” grid, then a new instance of “Edit control” gets created and attached to the JSGrid/Gantt API. That’s why you need to implement all these methods.
To start with, I would suggest to use the following code to register your custom control for the “Quick Mode”:
(function () { var visaField = new spdevlab.fieldSamples.VisaField(); visaField.init("VisaField"); SP.SOD.executeOrDelayUntilScriptLoaded(function() { SP.GanttControl.WaitForGanttCreation(function(ganttInstance) { var columns = ganttChart.get_Columns(); var bdfColumn = getBirthDayColumn(columns, "VisaField"); if(bdfColumn) { bdfColumn.fnGetEditControlName = function(record, fieldKey) { return "EDIT_SPDLAB_VISAFIELD"; }; SP.JsGrid.PropertyType.Utils.RegisterEditControl("EDIT_SPDLAB_VISAFIELD", function(gridContext, tb) { return new VisaFieldEditControl(gridContext, tb); }, []); }}); },"spgantt.js"); })();
As you see, you can be done while you register custom render templates for you fields. That’s okay.
Proper implementation of edit control for “Quick Mode” is not an easy task, so I would suggest to start with EditBoxEditControl control implementation (SP.JsGrid.EditControl. EditBoxEditControl) in “_layouts/15/jsgrid.debug.js” to understand how it might be done. Just check the original code, mimic and change necessary methods.
Finally, you may get something like I done for my “VISA” field sample in the “Quick Edit” mode:
Looks fantastic, right?
What’s next?
To be honest I didn’t expect that custom “Quick Mode” field behavior could be THAT big and complex topic. I am planning to continue posting about this covering edit controls and widget creation. That’s s really valuable, but unknown and uncovered area in SharePoint 2013.
Also, I will be updating my playground at codeplex – SharePoint 2013 custom field samples project so you may found all the code there very soon.
Some useful links
- Custom Field Types in SharePoint 2013 Apps
- Custom Field Rendering with JSLink
- SharePoint 2013: Create a GeoLocation field that renders maps using Nokia Maps
- SP 2013: Using the JSLink property to change the way your field or views are rendered in SharePoint 2013
- Custom Field Type for SharePoint 2013 – VISA card field sample
- How to: Implement Custom Validation in the JS Grid Control
- SharePoint 2013 custom field samples at codeplex.com
Conclusion
Client side rendering is a fantastic enhancement in SharePoint 2013. It provides really easy way to create custom look and feel as well as unique behavior for your fields. However, your solution will not be completed because of the “Quick Edit” mode.
Basically, you have just two options here: either lock down your column to prevent editing or create fully customized experience for “Quick Edit” mode. The second task is not an easy thing to be implemented, but surely provides more flexibility and better user experience.