Using jQuery and jqplot in your display template to show a piechart refiner


With the display templates in SharePoint 2013 being built in html and javascript you can easily re-use exisiting javascript frameworks like jQuery.

The jquery plugin called jqplot is one of many charting plugins available. It is more or less randomly chosen and the technique shown in this post can be re-used with many other plugins available.

Using the piechart available in jqplot can make your refiner look like below:

jqplot_refiner

 

The display templates used for refiners are located in the master page gallery, in folder “Filters” in the “Display Templates” folder. For more information about display templates, have a look a Steve Peschka’s article Using Query Rules, Result Types and Display Templates for a Custom Search Sales Report in SharePoint 2013

On your enterprise search site, go to Site Settings –> Master pages and page layouts. Go to the “Display Templates” folder, then “Filters”. The easiest way to edit files is to click on the “Library” tab, then “Open with Explorer”

image

To create a new display template make a copy of “Filter_Default.html”, and renamed it to “Filter_PieChart.html”.

Prior to editing the file, you can uploaded a couple of dependencies. I’ve put jquery-1.7.2.js as well a my own script “Filter_PieChart_script.js” (attached) into the folder. You also need copy the entire jqplot folder (as a sub folder) into the Filters folder. Download jquery and jqplot from http://www.jquery.com and http://www.jqplot.com.

Now, on to editing the file.

First thing to change is the title tag. The title tag is used in the refiner configuration when chosing the refiner display template you want to use. 

<title>Refinement Item PieChart</title>

I’m leaving all the other properties of the file. Now, on to the interesting part – how to include other scripts in a display template.

There is a number of techniques:

  • Use the $includeScript function in a <script>-tag
  • Use SharePoint Script On Demand (SP.SOD) in a <script>-tag
  • Add the scripts to the master page

I’m using a combination of the two first options. The difference between the two functions is that $includeScript loads the script asynchronously as fast a possible, but there is no guarrantee that the script is loaded. It’s suitable for small scripts like my Filter_PieChart_Script.js which is only used  to set up jqplot.

For larger scripts like jquery and jqplot the SharePoint Script On Demand functions are more suitable. With SP.SOD it is possible ensure that a script is loaded before you try to start using it. It is also possible register dependencies between the scripts to make sure scripts are loaded in the correct order.

The code snippet below is from the modified Filter_PieChart.html where a <script>-tag has been added.

 
<body>
    <script>
 
         $includeScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Filters/Filter_PieChart_Script.js");
         SP.SOD.registerSod('jquery-1.7.2.js', this.L_Menu_BaseUrl + "/_catalogs/masterpage/Display Templates/Search/jquery-1.7.2.js"); 
         SP.SOD.registerSod('jquery.jqplot.js', this.L_Menu_BaseUrl + "/_catalogs/masterpage/Display Templates/Filters/jqplot/jquery.jqplot.js"); 
         SP.SOD.registerSod('jqplot.pierenderer.js', this.L_Menu_BaseUrl + "/_catalogs/masterpage/Display Templates/Filters/jqplot/plugins/jqplot.pieRenderer.js"); 
         SP.SOD.registerSodDep('jquery.jqplot.js', 'jquery-1.7.2.js');
         SP.SOD.registerSodDep('jqplot.pieRenderer.js','jquery.jqplot.js');
         $includeCSS(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Filters/jqplot/jquery.jqplot.css");
</script>

The custom script Filter_PieChart_Script.js is loaded with $includeScript, in addition to jquery, jqplot and the pierenderer which are loaded in the correct order with SP.SOD.

The last function used in $includeCSS which can be used to load additional CSS.

Now, go far down in the file and find

<div id='Container'>

This is the actual part that displays the normal refiner action links. There is lots of other code in this template. Mostly of it is used to do result site merging of common file types. PPT, PPX, PPS etc are combined into the PowerPoint type.  The same for other file types. This part is nice to, so just jump down to the Container-div tag.

Delete everything inside this tag. I’ve modified it look like this:

 var pieChartId = ctx.RefinementControl.containerId + "_pieChart";
 var pieHeight = 240 + ((listData.length-1)*20);
 
_#-->
 
        <div id='Container'>
            _#= Srch.U.collapsibleRefinerTitle(ctx.RefinementControl.propertyName, ctx.ClientControl.get_id(), refinerCatTitle, iconClass) =#_
 
            <div id="_#= pieChartId =#_" style="height: _#= pieHeight =#_px; width: 180px;"></div>
 
        </div>
        <!--#_
 
 $addRenderContextCallback(ctx, "OnPostRender", function(){
 SP.SOD.executeFunc("jqplot.pierenderer.js", null, function() {
 SetupPieChartFilter(pieChartId, listData) 
 });
 }); 
 }
 }
_#-->
 

I’ve added a couple of variables, pieChartId and pieHeight. The pieChartId will later be used to create a div tag, and also handed to jqplot to tell it where to draw the pieChart. The pieHeight is calculated dynamicly since the legend is under the pie chart the size will vary depending on the number if items in the refiner.

The only html added is the div-tag with the pieChartId. After the html comes the intresting part. The function $addRenderContextCallback is run after the filter is rendered. We’ll run our code inside here, since we then now that everything is rendered. The code uses the SP.SOD.executeFunc to make sure jqplot is loaded. The it calls my custom function SetupPieChartFilter which uses jqplot functions to draw a pie chart based on the listData in the refiner (script attached).

Once the html is saved SP2013 will generate a Filter_PieChart.js which will be used by the browser. Now the only thing you need to do is to configure the refiner web part to use the new display template:

  1. Edit the page
  2. Edit the Refinement webpart
  3. In the Refinement settings, click “Choose Refiners…”
  4. Select the FileType chose your new Display Template “Refinement Item PieChart”¨

image

Click OK, Apply etc, and you should be all set!

Download the display template (you still need to download jquery/jqplot)

Download the full Visual Studio solution which will deploy a SharePoint feature with the display template (jquery and jqplot are downloaded from cdn.jsdelivr.net)

WSP file

 

Comments (20)

  1. Bhanu Prakash says:

    Nice Post….I am able to generate pie chart but my problem is here when i am searching the word again through search box pie chart is not loading its taking so much time and coming some problem occurred. one more thing identified i have modified date slider on search results page when i am sliding the graph the chart is not working.

    i think the script is not loading can you help me the asynchronous loading of pie chart.

  2. Mohammad Nizamuddin says:

    very nice info…thanks for sharing

  3. RK says:

    Thanks a lot :-) ..i feel it was beginning for learning of template customization :-) ,though we have few issues on submitting request as mentioned by Bhanu parkash  

  4. Calle says:

    Hi great post! Question, do you have some links/suggestions on how to create a hostname refiner? I.e. refine by web application in the farm (webapp1.company.com, webapp2.company.com, webapp3.company.com).

  5. KK says:

    Hi, great post!! Is there any way to make the titles below the pie chart clickable as well?

  6. Murad says:

    Yes you can make it clickable by binding to the jqplotDataClick-event in jqplot and triggering the $getClientControl(this).addRefinementFiltersJSON method (look at Filter_Default.html)

  7. Abot says:

    Hi – thanks for an excellent article.  I wonder if anyone has got this working on SharePoint online?  The charts don't appear to render even if I script link to a CDN.  Any thoughts would be really appreciated.

  8. murads says:

    Shouldn't be any stopping this from working on SPO.. I haven't tested it in a while though.  Is all the scripts being loaded if you debug this with F12 developer tools?

  9. KK says:

    Hi Murad, thanks for your reply on how to make the titles clickable. When you say look at Filter_default.html, where is this file? or did you mean filter_piechart.html? Thanks again

  10. KK says:

    Ignore the last comment, I just remembered from your article that you asked us to rename the filter_default.html file. I hope I will get it working today as this is really important for the project I'm working on. Thanks again for the excellent article.

  11. Murad says:

    Filter_default.html is the standard refiner which already located in the Display Templates/Filters folder in your masterpage gallery.

  12. Murad says:

    You shouldn't rename Filter_default.html, you should make a copy…

  13. KK says:

    Thanks Murad, yeah I didn't rename the original one and I copied and then renamed. I'm still struggling with this, can you please help? How do you make those titles clickable?

  14. PT says:

    Hi – Awesome post!! Was wondering if you could help me with making the legends filterable as well? As someone has already asked that question and you said it is possible. However, I have already tried by binding the legend to the jqplotdataclick event as you have suggested and triggering the event with addrefinementfiltersJSON method but no luck. Is there anything that you would like to add that I may be missing?

  15. Murad says:

    Change your $addRenderContextCallback to something like this (some old sample I had lying around, no guarranties):

    $addRenderContextCallback(ctx, "OnPostRender", function(){

    SP.SOD.executeFunc("jqplot.pierenderer.js", null, function() {

    SetupPieChartFilter(pieChartId, listData);

    if(listData.length > 1) {

    $('#'+pieChartId).bind('jqplotDataClick', function (ev, seriesIndex, pointIndex, data) {

    filter = listData[pointIndex];

    var refiners = new Object();

    refiners[filter.RefinerName] = filter.RefinementTokens;

    eval('$getClientControl(this).'+addMethod+'(Sys.Serialization.JavaScriptSerializer.serialize(refiners));');

    });

    } else { // show all

    $('#'+pieChartId).bind('jqplotDataClick', function (ev, seriesIndex, pointIndex, data) {

    var refinersAll = new Object();

    refinersAll[ctx.RefinementControl.propertyName] = null;

    if (ctx.RefinementControl.propertyName == "FileType") // FileType is special, this is copied from Filter_Default.html

    {

    refinersAll["contentclass"] = null;

    refinersAll["ContentTypeId"] = null;

    refinersAll["WebTemplate"] = null;

    }

    $getClientControl(this).updateRefinersJSON(Sys.Serialization.JavaScriptSerializer.serialize(refinersAll));

    });

    }

    });

    });

    // Put this code somewhere above

    var addMethod = "addRefinementFiltersJSON";

    if (ctx.RefinementControl.propertyName == "FileType")

    {

    addMethod = "addRefinementFiltersJSONWithOr";

    }

  16. PT says:

    Thank you very much for this but this looks quite similar to the code that I have already got for the actual pie chart itself and it works for the pie chart. Would this work with the legends table (File Types)  when user clicks on PowerPoint not the actual slice and it will filter as well? I'll try this tonight. Thanks again :)

  17. Murad says:

    Ok. I don't really remeber but you might be right. Anyway it's a generic jqplot issue I would say, so I would seek advise somewhere else :)

  18. PT says:

    Thanks Murad

  19. Priyanka says:

    Hi Murad,

    Can you provide an html which has the click event working. I have tried above code sample, but its not working.

  20. Howie says:

    Hi – this works great for me, and thanks for the article.  I have one problem however, when I click on an element in the chart (or remove the refiners) I need to refresh the page in order for the chart to display.  Any ideas on how I can refresh the chart would be appreciated.  Thanks.