Introduction
SharePoint 2013 introduced Apps and one of the options is to
create SharePoint Hosted Apps, apps that are hosted inside SharePoint in its
own app web. With SharePoint hosted apps you can’t use server side code so you
are limited to JavaScript. However you can read and write data from SharePoint
lists in host web (SharePoint web that is hosting app).
JavaScript frameworks such as Angular, Backbone and Knockout
have been matured and became pretty strong.
So, scenario here is to have data inside SharePoint List (custom list, one column for title and one column for value), to create an app with read permissions to access that data, to use angular (http://angularjs.org) for data-binding part and
jqPlot (http://www.jqplot.com )
for chart rendering. Also, one chart in this example is using ui-chart
directive (http://angular-ui.github.io/ui-chart/),
the directive that lets you use jqPlot with Angular.
I’m going to assume that who is interested in this article
is having some knowledge about SharePoint 2013, JavaScript and html, so I’m not
going to write here about SharePoint apps, AngularJs and jqPlot. You can visit
their respective websites in order to learn more about them.
Background
The basic idea for writing this article was to create better charting for SharePoint. In Version 2010 we had chart web part which was OK, in version 2013 this web part is deprecated. Excel is desired solution for charting in SharePoint. It is a power tool but it doesn't cover every scenario especially for quick charts we want to display in our pages.
Introducing the better client scripting support (JSOM, REST) and the app model, Microsoft opened the doors to the whole new world of developing for SharePoint. Now it is possible to develop a solutions, using powerful and proved JavaScript frameworks such as AngularJs and jqPlot.
I've got an idea of creating this app, watching Jeremy Thake's session "SharePoint 2013 Apps with AngularJS", from Las Vegas on channel 9 (http://channel9.msdn.com/Events/SharePoint-Conference/2014/SPC408). Much of the code that is shown here (service that retrieves sharepoint data) is his.
Creating the app
Data
I've made simple custom list in SharePoint called
PieChartData. Besides Title column which is auto generated (and I’m going to
use it for labels), I added one more column called Value (type - number).
I've also saved this list as template (with data included)
and you can download it here - Download PieChartData.zip
You should create this list in the SharePoint web site where
your app will be installed.
You can see list with sample data on the image above.
Remark: If you are creating this list by your own, it is very important that internal names of your fields are Title and Value or the code will fail. If you are not able to name your fields like that, or you insist to name it differently, you should also change the code in App.js that retrieves the data from SharePoint. Just find these two lines and change:
var Title = currentItem.get_item("Title");
var Value = currentItem.get_item("Value");
Also if you want to change list name you should change it here (App.js):
var promise = $SharePointJSOMService.getData($scope, 'PieChartData');
Second argument is list name.
Idea: It would be nice to create app part and to have property for list name. That would empower your users and give them possibility to change data source.
Sharepoint App Creation
I was using Visual Studio 2013 for this part. I've created
new project (image below), App for SharePoint 2013.
Then I selected that it’s going to be SharePoint hosted app
(image below).
Last thing was to set permissions for our app. In the
Application.manifest file (double click it), under permissions tab just add
Read permission for Web object (image below).
And I was ready to go.
Resources
I copied angular.js into Scripts/lib/angular directory of my
project, jqplot into Scripts/lib/jqplot, bootstrap into Scripts/lib/bootstrap (but
I didn’t even use it). UI-charts chart.js file was copied directly into
Scripts/lib directory.
View template
Inside Pages/Default.aspx file which is home page of our app
web I referenced required resources and I added view part of our angular app.
This block of includes goes to head part of the Default.aspx
page:
<script type="text/javascript" src="../Scripts/lib/angular/angular.js"></script>
<script type="text/javascript" src="../Scripts/lib/jqplot/jquery.jqplot.js"></script>
<script type="text/javascript" src="../Scripts/lib/jqplot/plugins/jqplot.pieRenderer.js">
</script>
<script type="text/javascript" src="../Scripts/lib/jqplot/plugins/jqplot.barRenderer.min.js"></script>
<script type="text/javascript" src="../Scripts/lib/jqplot/plugins/jqplot.categoryAxisRenderer.min.js"></script>
<script type="text/javascript" src="../Scripts/lib/jqplot/plugins/jqplot.pointLabels.min.js"></script>
<script type="text/javascript" src="../Scripts/lib/chart.js"></script>
<script type="text/javascript" src="../Scripts/Common.js"></script>
<link rel="stylesheet" type="text/css" href="../Scripts/lib/jqplot/jquery.jqplot.css"/>
And angular view is going to the body:
<div ng-app="myChartingApp" ng-controller="DemoCtrl">
<div ui-chart="someData" chart-options="myChartOpts"
style="width:600px; height:300px"></div>
<div id="barchart" style="width:600px; height:300px;"></div>
<div id="linechart" style="width:600px; height:300px;"></div>
</div>
It’s not very practical to have ng-app directive in html
element in SharePoint master page, so I've put it inside div element in my app
Default.aspx page. I've also declared DemoCtrl controller (declared with
ng-controller directive) so everything inside this div would be part of
angularjs myChartApp and it would use DemoCtrl controller.
Inside this app div there are three more divs (they're
acting as placeholders for charts):
1.
First one is using ui-chart directive and it is
bind to someData property of our controller for chart data, and to myChartOpts
property for chart options
2.
Second div is container for our bar chart that
will be rendered directly not with ui-chart directive
3.
Third div is container for our line chart that
will be rendered directly not with ui-chart directive
That’s all markup we need for this project to work.
Remark: I've provided two scenarios here for creating the charts: one with the ui-chart directive is nice and clean if you create controller and app part for each chart type (or you group similar ones), the second one with method that uses jqplot directly is good when you create one controller which renders different types of charts and you have a property in your app part where user can choose which chart he wants to render.
Javascript
All our code will be in App.js and Common.js.
I've deleted all sample SharePoint code here and put my own.
myChartingApp module, DemoCtrl controller and angular service for retrieving
SharePointData were defined here.
Remark: all credits for angular service go to
Jeremy Thake, which wrote great article about using Sharepoint and AngularJS (http://www.jeremythake.com/2013/10/sharepoint-hosted-app-with-angularjs-for-mvc-javascript/).
Common.js
This file has only one common method, taken from MSDN
examples (you can find it in almost every SharePoint hosted app example), that
parses QueryString.
function getQueryStringParameter(paramToRetrieve) {
var params =document.URL.split("?")[1].split("&");
var strParams = "";
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split("=");
if (singleParam[0] == paramToRetrieve)return singleParam[1];
}
}
Module
Here is code which defines angular module and injects chart
options for pie chart:
var myChartingApp = angular.module('myChartingApp', ['ui.chart'])
.value('charting', {
pieChartOptions: {
seriesDefaults: {
renderer: jQuery.jqplot.PieRenderer,
rendererOptions: {
showDataLabels: true
}
},
legend: { show: true, location: 'e' }
}
});
In this code we create myChartingApp, with chart options
for creating a pie chart (the one that is using ui-chart directive). We also
tell angular that we are going to use ui-chart (['ui.chart'])).
Controller
myChartingApp.controller('DemoCtrl', function ($scope, charting,
$SharePointJSOMService) {
Properties
$scope.someData = [[]];
$scope.ticks = [];
$scope.barvalues = [];
var promise = $SharePointJSOMService.getData($scope, 'PieChartData');
promise.then(function (message) {
alert(message);
},function (reason) {
alert(reason);
}
);
$scope.myChartOpts =charting.pieChartOptions;
…
Little
bit about properties:
- someData – property that will
hold data (collection of title, value pairs from SP list)
- ticks property – array of
ticks (collection of titles from SP list in this example), used in bar chart
- barvalues – array of values
from SP list, used in bar chart for
values and line chart for y axis
- myChartOpts – pie chart
options injected into controller. Used in pie chart with ui-chart directive.
Methods
$scope.setBarChart = function () {
var plot2 = $.jqplot('barchart', [$scope.barvalues], {
seriesDefaults: {
renderer: $.jqplot.BarRenderer,
pointLabels: { show: true, location: 'e', edgeTolerance: -15 },
},
axes: {
xaxis: {
renderer:$.jqplot.CategoryAxisRenderer, ticks: $scope.ticks
}
}
});
}
Method
setbarChart is used for rendering bar chart with jqplot, without help of
ui-chart directive. It is very simple, it sets $scope.barvalues as data ,
$scope.ticks as ticks and it has an option for displaying point labels (pointLabels).
$scope.setLineChart = function () {
var plot = $.jqplot('linechart', [$scope.barvalues], {
title: 'Sales 2014',
axesDefaults: {
labelRenderer:$.jqplot.CanvasAxisLabelRenderer
},
seriesDefaults: {
rendererOptions: {
smooth: true
}
},
axes: {
yaxis: {
label: 'Sales', min: 0
},
xaxis: {
label: 'Months', min: 1, max:$scope.barvalues.length
}
},
series: [{ color: '#5FAB78' }]
});
Method
setLineChart is used for rendering line chart with jqPlot, without help of
ui-chart directive. It is very simple, it sets $scope.barvalues as y axis data,
1 as minimum for x axis, $scope.barvalues.length as maximum for x axis and it
has an option for displaying labels for title, y axis and x axis.
Service
As I said this service has been taken from Jeremy Thake’s
blog, with small modifications. Its purpose is to connect to Sharepoint host
web and to read data from PieChartData list.
I've added this code inside list enumerator:
var Title = currentItem.get_item("Title");
var Value = currentItem.get_item("Value");
var b = [Title, Value];
$scope.someData[0].push(b);
$scope.ticks.push(Title);
$scope.barvalues.push(Value);
It’s pretty simple, I’m reading Title and Value field values
and add them as collection members to my properties (someData, ticks, barvalues).
Also, outside of the enumerator I'm calling my methods for
rendering jqPlot charts:
$scope.setBarChart();$scope.setLineChart();
That’s it.
Here is the result (image below):
Conclusion
This is pretty simple app, it's been more a teaser than real app, but this way you could easily catch up with basics of charting with angular in SharePoint apps.
Next steps and ideas:
- Create app parts with single chart in it, so someone could display charts inside SharePoint pages.
- Create controllers for different types of charts
- Try more complex charts
SharePoint Store
If you plan to deploy this kind of apps to SharePoint store you should know that your app should work on IE8. I didn't test angularjs and jqplot against IE8.