Custom list view by using the “JS Link” property.

6 minute read

Customizing a list view could be done by using XSLT in previous version of SharePoint. In SharePoint 2013 this van be done by using the “JS Link” functionality. The “JS Link” functionality is included in list view web parts of SharePoint.

You can use this property for adding specific JavaScript files. You can add 1 or multiple when you want to add multiple you will have to separate them using the “|”  symbol. On the internet you can find a lot of articles on how to use the “JS Link” functionality. Normally the “JS Links”  functionality is used to adjust the rendering of a specific field type in my scenario I had to change the complete list view.

In this article I will show you how to define a custom list view using the “JS Link” functionality according to a specific scenario: On project sites we want to follow the status of site migrations (for example SharePoint sites). The status of these migrations can be saved within SharePoint lists:

clip_image002

In this list you will need to have two fields:

  • Title: The title of the site that needs to be migrated.
  • Status: The status of the migration. In this example we will have three statuses: “Not started”, “In progress” en “Done”.

Using this list we can manage the status of the migrations. At this moment we only have to add functionality to see the migration status of all sites in one view without the use of a farm solution.

clip_image004

In the above image you can see the same list but a altered view with the use of “JS LInk”. This view shows a doughnut view with three different colours. So how can we achieve this without creating a full trust solution.

First off all we searched for a JavaScript library to be able to create a doughnut chart. We choose the following library:

Now that we have a library we can start writing a JavaScript file that we need to load within the “JS Link” property. Within the JavaScript file we first off all register a namespace to not get in the way of other JavaScript code besides that we also define a view variables that we can  use within the namespace.

var migrationStatus = migrationStatus || {};

migrationStatus.InProgressString = 'In progress';
migrationStatus.DoneString = 'Done';
migrationStatus.NotStartedString = 'Not started'; 
migrationStatus.ListName = 'Migration';
migrationStatus.ItemsDone = 0;
migrationStatus.ItemsInProgress = 0;
migrationStatus.ItemsNotStarted = 0;

To be able to define template you will need to define specific overrides. These overrides can be defined by using the “RegisterTemplateOverrides” method on the “SPClientTemplates.TemplateManager” object. This method needs one object that defines what needs to be overridden. We will define this within the method: “CustomizeViewRendering”.

// Create a function for customizing the view rendering of the list
migrationStatus.CustomizeViewRendering = function () {
    var migrationStatusContext = {};
    migrationStatusContext.Templates = {};
    migrationStatusContext.Templates.View = migrationStatus.RenderMigrationViewBodyTemplate;
    migrationStatusContext.OnPostRender = migrationStatus.OnMigrationViewPostRender;
    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(migrationStatusContext);
};

In the “migrationStatusContext” object needs to be defined which rendering methods needs to be overridden. As you can see in the above snippet the “OnPostRender” and the “Templates.View” will be overridden. These methods we will discuss in the upcoming paragraphs.

Note:When you override the rendering methods, this will be for every list view on a page. If you want to override the view rendering of one specific list you will have to build in a check if you are performing the action on the correct list.

migrationStatus.RenderMigrationViewBodyTemplate = function (ctx) {
    if (ctx.Templates.Body == '') {
        return RenderViewTemplate(ctx);
    }

    if (ctx.ListTitle == migrationStatus.ListName) {
        var listData = ctx.ListData;

        for (var idx in listData.Row) {
            var listItem = listData.Row[idx];

            if (listItem.Status == migrationStatus.InProgressString) {
                migrationStatus.ItemsInProgress++;
            } else if (listItem.Status == migrationStatus.NotStartedString) {
                migrationStatus.ItemsNotStarted++;
            } else if (listItem.Status == migrationStatus.DoneString) {
                migrationStatus.ItemsDone++;
            }
        }

        var htmlOutput = [];
        htmlOutput.push('<div id="listData"><a href="' + ctx.listUrlDir + '"> View this list</a></div>');
        htmlOutput.push('<div id="migrationstatusView" style="height:400px;width:400px;margin-top:10px;float:left;margin-bottom:10px;display:block;">');
        htmlOutput.push('<canvas id="myChart" width="250" height="250"></canvas>');
        htmlOutput.push('<div id="chartLegend" style="width:150px;float:right;" />');
        htmlOutput.push('</div>');
        

        var retVal = htmlOutput.join('');
        return retVal;
    }
    else {
        return RenderViewTemplate(ctx);
    }
}

The “migrationStatus.RenderMigrationViewBodyTemplate” function needs to be used to adjust the view. This method also receives the context (ctx) object in which all information regarding the context (in this example list) is saved. From this context we can retrieve all of the list data.

But first we will have to check if the body of the template isn’t empty. When it is empty we will have to call the default renderer. After that we can check if the list we are performing the actions for is our migration list: “ctx.ListTitle == migrationStatus.ListName” if this isn’t the case we also call the default rendering function:

return RenderViewTemplate(ctx);

When this list is our list we count all of the statuses and place them in the specific variables. When all this is done we write out the HTML we need for the rendering of the graph and legend and we return this.

De “PostRender” function will be called as last. In this function the graph will be loaded with the correct data and besides that we will also load the legend of the graph.

migrationStatus.OnMigrationViewPostRender = function (ctx) {
    if (ctx.ListTitle == migrationStatus.ListName) {

        var pieData = [
        {
            value: migrationStatus.ItemsNotStarted,
            color: "#F7464A",
            highlight: "#FF5A5E",
            label: migrationStatus.NotStartedString
        },
        {
            value: migrationStatus.ItemsDone,
            color: "#46BFBD",
            highlight: "#5AD3D1",
            label: migrationStatus.DoneString
        },
        {
            value: migrationStatus.ItemsInProgress,
            color: "#FDB45C",
            highlight: "#FFC870",
            label: migrationStatus.InProgressString
        }];
        
        var options = {
            //Boolean - Whether we animate the rotation of the Doughnut
            animateRotate: true,
            //String - A legend template
            legendTemplate: ''
        };

        //get the chart element and setup the doughnut chart
        var ctxChart = document.getElementById("myChart").getContext("2d");
        var myPie = new Chart(ctxChart).Doughnut(pieData, options);

        //generate the legend html
        var legend = document.getElementById("chartLegend");
        legend.innerHTML = myPie.generateLegend();
    }
};

In the “PostRender” function we also need to check if we are performing the action for the correct list.

The final thing we need to adjust to the JavaScript file is adding the following line:

migrationStatus.CustomizeViewRendering();

This line of code will be called on the moment the file is loaded and by calling this function is will make sure that the overrides for the list views will be registered.

Know that the files are ready we only need to specify the files in the “JS Link” property of the list view.

clip_image006

In the “JS Link” we have specified the following value:

/siteassets/Migration/Chart.js|/siteassets/Migration/MigrationStatus.js