Today I am going to shift gears a bit from ITPro\Admin topics to a dev based topic. I would like to show you how to customize SharePoint forms with KendoUI Grid. Everything I cover in this post can be done in either SharePoint On-Premise or SharePoint Online (O365).

I recently had a need to add a field to a SharePoint form that contained multiple columns of its own data. For example a table or grid within the form itself. A couple of OOTB ideas to handle this could be a lookup field that also brings in additional fields from the lookup list:

Add Multiple Field Lookup Column - Customize SharePoint Forms With KendoUI Grid

The form doesn’t look too bad doing it this way:

Lookup Field with Distinct Values - Customize SharePoint Forms With KendoUI Grid

One obvious limitation to this is you can’t see the first name in the form while inserting it. Another is that users will need to be able to add entries to the list in order to add new speakers. However, there is an even bigger issue with multiple field look up lists. Quite often you have a need to allow non-unique entries as there could be many variations of a similar item. For example, here’s how that form looks if you had more than one surname that was the same:

Lookup Field with Non-Distinct Fields - Customize SharePoint Forms With KendoUI Grid

SharePoint is nice enough to place them in order for us, but can you tell me which Smith I am selecting?

Customize SharePoint Forms With KendoUI Grid

So obviously the OOTB form was out, but there are still a number of options. One of these options would be an InfoPath form, but I don’t want to encourage the use of that tool. Today instead I am going to show you how to use KendoUI to handle this. I have used Telerick’s tools in the past and really found them robust and fairly easy to use. The KendoUI tool set is no exception in my opinion. The tool set allows for great integration with SharePoint using REST and also has hooks for the AngularJS developer.

The Setup

Normally I would suggest the solution be setup as a SharePoint-Hosted Add-In, but I am just illustrating one feature so I chose JavaScript Injection for my solution. First thing you need to do is upload your KendoUI library to your SharePoint site. I placed it into a sub folder in my Site Assets, but as in all cases, please use whichever is best for you. You can upload just the files you need, but to be terribly honest I actually suggest you upload everything for development. It does not take up a great deal of space in your site and if you need to use different controls or styles, everything is there, you don’t have to upload the code for a different feature each time you use something new. Production is obviously a different story. Only upload the files you will be using to keep your prod environment clean. So with all that in mind, when you get your KendoUI files, upload the “js” and “styles” folders to the location you wish to store them in (in my case I put them both into a KendoUI folder). This will upload extra files that you won’t need as I stated before. If you only want the .min files, then you can remove the others. It’s your choice really.

KendoUISiteAsset

The next step is to add three new JavaScript files. These files can be used for displaying the form, editing the form and creating a new form. What I am showing in this post today you can actually do in just one form and then detect the mode, but I prefer separation. Because you may be creating other JS files for other forms in your site I suggestion you place these files into their own folder.

Next, setup the field you want to use to store the data as a multiple-line text field. If you use a single line, you will not be able to store many entries as the maximum number of characters will be reached quickly. VERY IMPORTANT: “Multiple lines of text” fields default to rich text. Change it to plain text, otherwise the code you need to write to clean-up of the data that is passed to the grid will be awful.

Data Format

Next determine your data format. I chose to use JSON formatting. The reason for this is KendoUI tools all handle JSON without any custom modifications. As long as your data is in JSON format, you can edit it directly within your code. No need massaging it before passing it to the control. In the case of my demo, the field is going to display the surname, given name and topic of the speaker. This means the format will be:

{"SpeakerInformation": [{"Surname":"Drever","FirstName":"David","Topic":"BCS"}]}

Setting up the Form

JavaScript Injection is pretty straight forward. First thing we want to do is edit the default forms to allow the inclusion of our custom javascript and css files. To do this, simply access the list you wish to customize and perform the following:

  1. Click on List
  2. Click on Form Web Parts
  3. Select the form you wish to modify (in this case I am going to modify the new form)

Customize New Form - Customize SharePoint Forms With KendoUI Grid

Next we want to add a script editor web part to the page to load our custom files

O365 Note: Before getting to #1, you first have to click on the settings “Cog” and then click Edit Page. The options below are not available to you unless the page is in edit mode and SP Online doesn’t do this automatically for you.

  1. On the edit screen select Add a Web Part.
  2. Select Media and Content
  3. Select Script Editor Web Part.
  4. Click Add

Adding Custom Scripts to Form - Customize SharePoint Forms With KendoUI Grid

Once the Script Editor Web Part has been added click on Edit Snippet and insert the following code:

<link rel="stylesheet" type="text/css" href="/kendo/SiteAssets/KendoUI/styles/kendo.common.min.css" />
<link rel="stylesheet" type="text/css" href="/kendo/SiteAssets/KendoUI/styles/kendo.office365.min.css" />

<script type="text/javascript" src="/kendo/SiteAssets/KendoUI/js/jquery.min.js"></script>
<script type="text/javascript" src="/kendo/SiteAssets/KendoUI/js/kendo.all.min.js"></script>

Script Editor Code - Customize SharePoint Forms with KendoUI Grid

Next, update the form itself by editing the form web part and clicking on the plus (+) sign beside Miscellaneous. Update the JSLink with the name of the file you are using to modify the new form of the list. The format for a JSLink field is “~site/<path>/<file.js>

~site/SiteAssets/KendoGridDemo/NewFormKendo.js

Set JSLink - Customize SharePoint Forms with KendoUI Grid

Repeat the above steps for the Display and Edit forms.

The Code

I’m going to provide the full code script at the end of this post so if that’s all you really want then scroll down. However, if you don’t mind me rambling along about why I did a certain thing or how I wrote the solution, then I beg your indulgence and please continue reading.

First off you are going to notice I am using two global variables. While it is not a absolute no-no, it is frowned upon and discouraged. The variable speakerDataArray is an array that will hold the grid data. We need this because we are not wiring the grid directly to a data source. So this source needs to be available within all scopes. The other one is the tableNextID. This is simply a holder for the next ID in the table. I could have written code that scanned the table and returned back the latest ID used, but using the variable is a lot less overhead.

So the first function is our CSR (Client Side Rendering) declaration.

function overrideContextInfo() {
	var overrideField = {};
	overrideField.Templates = {};
    overrideField.Templates.Fields = {
        'SpeakerInformation' : {'NewForm': renderNewItems}
    };
	SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideField);
}

In this case I am effectively stating that I want to change the field “SpeakerInformation”. When I modify that field I want to do it with the function “renderNewItems”.

Quick Note: if I wanted to put the code for all three forms into one file I could do it here. I would just need to modify the overrideField call as follows:

overrideField.Templates.Fields = {
        'SpeakerInformation' : {'NewForm': renderNewItems, 'EditForm': renderEditItems,
                                 'DisplayForm': renderDisplayItems}};

The client would detect the type of form the user was in and call the appropriate function\method.

The function renderNewItems builds out the structure we want the field to look like. If I wasn’t using a grid, I could change it into a dropdown or pretty much anything. In this case I change it to a table so the grid can integrate.

/********************************************************************************************
*Function Name: renderNewItems                   											*
*Parameters: $fieldCtx - Context of field being overriden 									*
*                                                                                           *
*Return Value: HTML to replace field.														*
*Purpose: Changes the SharePoint rendering of a field to display as required				*
/********************************************************************************************/
function renderNewItems(fieldCtx) {

    var speakerInfoVal = fieldCtx.CurrentItem.SpeakerInformation;
    var formctx = SPClientTemplates.Utility.GetFormContextForCurrentField(fieldCtx);

    var overrideHTML = "";
        
    overrideHTML += "<table id='speakerInfo' class='display' cellspacing='0' width='100%'>";
    overrideHTML += "</table>" 
    
    //Call the initiliaze call back for the field to override in the form
    formctx.registerInitCallback(formctx.fieldName, function () {
			makeTable(fieldCtx);
       });
 		
    //Register the call back functions within SharePoint so the value is saved back to the list
    registerSPCallBacks(formctx);

    return overrideHTML;
}

The renderNewItem function in turn calls the makeTable function. This is where the magic happens. “makeTable” builds the KendoUI Grid data source, the grid itself and applies the data source to the grid. You can download the script to see the entire method, but I am going to illustrate a couple of main parts here:

var speakerDataSource = new kendo.data.DataSource({
        transport: {
            read: function(e) {
                if(speakerDataArray.length > 0) {
                    e.success(speakerDataArray[0]);
                }
                else {
                    e.success(speakerDataArray);
                }                
            },
            create: function(e) {
                if(speakerDataArray.length > 0) {
                    e.data.SpeakerID = tableNextID++;          
                    speakerDataArray[0].push(e.data);
                    e.success(e.data);
                }
                else {
                    e.data.SpeakerID = 1;
                    speakerDataArray = buildNewJSON(e.data);          
                    e.success(e.data);
                }                
            },
            update: function(e) {
                speakerDataArray[0][getIndexByID(e.data.SpeakerID)] = e.data;
                e.success(e.data);
            },
            destroy: function(e) {
                speakerDataArray[0].splice(getIndexByID(e.data.SpeakerID),1);
                e.success(e.data);
            }
        },
        error: function(e) {
            alert("Status: " + e.status + "; Error message: " + e.errorThrown);
        },
        pagesize: 10,
        schema:{
            model:{
                id: "SpeakerID",
                fields: {
                    SpeakerID: {editable: false, nullable: true},
                    Surname: {type: "string"},
                    FirstName: {type: "string"},
                    Topic: {type: "string"}
                }   
            }            
        }
    })

In the data source we declare how the grid should handle the different types of processes against it (displaying, editing, inserting, etc). We also define the model. This is where that global variable is used. Normally the grid would connect directly to the data source via REST or a direct connection but in this case, I am converting the grid data to place it into a text box so those options aren’t available. So we are going to store the data in that global array so I can massage it for storage in the list.

This brings us to the next portion. Putting the grid on the page:

//Create the Kendo Grid with the data for the users.
$('#speakerInfo').kendoGrid({
        dataSource: speakerDataSource,
        scrollable: true,
        pageable: true,
        toolbar: ["create"],
        resizeable: true,        
        columns: [
            {field: "Surname", title: "Surname"},
            {field: "FirstName", title: "First Name"},
            {field: "Topic", title: "Topic"},
            {command: ["edit", "destroy"], title: "&nbsp;"}
        ],
        editable: "popup"
    });
}

Pretty straight forward. Remember that table I created in the renderNewItem method? I am grabbing that here and applying the grid to it. Declare where it gets its data from, and setting some other options including the columns. That’s pretty much it. I have a couple of other methods in the script file that are pretty straight forward. They simply massage the data so it can be stored in the list and in the grid array. Removing the speaker ID field from the data as we don’t need that (the SpeakerID field is just there to keep things straight in the grid), etc. If you download the scripts you can take a look at them there.

So what does this code get us?

  1. A grid that you can insert data into:

NewFormNoGridEntries - Customize SharePoint Forms with KendoUI Grid

NewFormNoGridEntries - Customize SharePoint Forms with KendoUI Grid NewFormAddEntry - Customize SharePoint Forms with KendoUI Grid

NewFormWithEntry - Customize SharePoint Forms with KendoUI Grid

  1. A grid you can edit existing data:

NewFormModifyEntry - Customize SharePoint Forms with KendoUI Grid

  1. A grid you can delete data you don’t need:

NewFormDeleteEntry - Customize SharePoint Forms with KendoUI Grid

  1. All stored in a multi-line text field in SharePoint:

DataSavedToList - Customize SharePoint Forms with KendoUI Grid

Obviously you are going to want to present the Speaker Information column a bit differently, but that is a post for another day.

Differences For Display Form

The code while very similar for the display form page is a little bit different. This is because when defining the data source, we are not providing any methods for modifying the data. Just for displaying the data. So the script is actually quite a bit smaller than the new and edit forms.

$('#speakerInfo').kendoGrid({
	dataSource: {
		data: dataArray,
		pagesize: 10,
		schema:{
			model:{
			fields: {
				Surname: {type: "string"},
				FirstName: {type: "string"},
				Topic: {type: "string"}
			}   
			}            
		}                
	},
	
	scrollable: true,
	sortable: true,
	filterable: true,
	pageable: {
		input: true,
		numeric: false
	},
	columns: [
		{field: "Surname", title: "Surname"},
		{field: "FirstName", title: "First Name"},
		{field: "Topic", title: "Topic"}
	]
});

As you can see we just tell the grid where the data source. When you download the script files (if you do) you will see the makeTable isn’t doing much with the data, just grabbing it directly from the SharePoint form and removing the div data within. This is because “multiple lines of text” fields store their data in a div.

When added to the form, it gives you a read only version of the grid.

DisplayFormWithData - Customize SharePoint Forms with KendoUI Grid

This was a long post and I thank you for sticking with me. Please fire a comment if you have any questions about the items I covered here. As promised you can download the scripts here. Use them as you will, but if you use them in your own presentations or blog I ask that you do provide credit where it is due.

Thanks for reading!