Now-a-days Angular JS has become popular and certainly there is reason for that. If you search in Internet you can find really good examples of Angular JS.
Since SharePoint is my bread and butter for some time, I thought of using it on SharePoint, I have found few examples in the internet but none of them was working correctly for me. I had to make few modifications and it worked on SharePoint. The main reason it was incorrect angular tags. So I’ll explain this as a first thing.
Relation between Angular JS and HTML page
I’ll not give you details of how Angular JS works; I assume you must have some sort of idea about it. But I found below relation was working in all of my SharePoint pages. Note the DIV tags ng-app=”myApp”, ng-controller=”ConactController” in the HTML side and their relation with the JavaScript in the below diagram
Preparation
Before you start development you need to prepare the environment, i.e. getting the necessary JS files I have used Angular JS, JQuery and Bootstrap CSS for the examples, but bootstrap is not essential.
I have downloaded most of the files and placed them inside “SiteAssets” library; you may want to put it in a more orderly folder structure.
Sometimes I have used CDN also e.g.: http://getbootstrap.com/2.3.2/assets/css/bootstrap.css . Similarly you can use Google CDN for jQuery and Angular JS. You can download Angular JS from https://angularjs.org/ and bootstrap from here: http://getbootstrap.com/
Below is a screenshot of the Site Assets library in my SharePoint.
Page1-Page6 JS files are specific for the SharePoint examples shown in Page1 to 6 (see them in thleft navigation). All other files are common.
For some reason the JS was not working until I activated SharePoint Publishing feature on the site.I’m not sure if that’s really needed though. But for a better looking site you might want to use publishing pages rather than wiki pages.
I have added all HTML script in a script editor on each page.
Create a page named Page1„ Edit the page„³Add a script editor in the page
Once you add the script editor, go to Edit Snippet and paste the HTML there. A sample screenshot is given below.
I have created a custom list name Employee which has been used in many examples below. Notice the EmpImage column. The Images are stored in the Images library, this library should be already created when you activate SharePoint publishing feature. You can use some other library too.
I have created a custom list name Employee which has been used in many examples below. Notice the EmpImage column. The Images are stored in the Images library, this library should be already created when you activate SharePoint publishing feature. You can use some other library too.
Page1 Example
Now to the actual examples, this is a really simple example, just to give you an idea. You enter some data and a popup shows your answer.
I have created a page name Page1.aspx, added a script editor and pasted below HTML into the script editor. Same technique used for all the examples.
HTML
This few lines
of HTML render the questions and popup answers too.
<script
src="http://code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/angular.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/Page1.js"></script>
<link
rel="stylesheet" type="text/css"
href="http://getbootstrap.com/2.3.2/assets/css/bootstrap.css">
<style
type="text/css"> .auto-style1 { color: #339933;}</style>
<div
ng-app="myApp">
<div
ng-controller="ContactController">
<div
id="PutYourContentInside">
<ol>
<li
ng-repeat="question in questions">
<span>{{question.text}}</span>
<br
/>
<input
type="text" ng-model="question.answer"/>
</li>
</ol>
<input
type="submit" value="Submit"
ng-click="addAnswers($event)"/>
</div>
</div></div>
JavaScript:
The JavaScript
used here is saved as Page1.js in the site asserts library. So this script just
passes the questions to the 'ContactController'module and gets back the answers. This
JavaScript was saved as Page1.js in the Site Assets library.
var
myApp = angular.module('myApp', []);
var
uid = (function () {
var
id = 1;
return
function () {
if
(arguments[0] === 0)
id
= 1;
return
id++; }})();
var
siteUrl = GetSiteUrl();
myApp.controller('ContactController',
function ($scope) {
$scope.questions
= [
{
text: 'How would you like your eggs done?', answer: "" },
{
text: 'What kind of bread would you like?', answer: "" },
{
text: 'What kind of drink would you like?', answer: "" } ];
$scope.addAnswers
= function ($event) {
$event.preventDefault();
var
ans = "";
for
(i in $scope.questions) {
ans
= ans + $scope.questions[i].answer + ", ";
$scope.questions[i].answer
= "";
}
$scope.$apply();
alert("Thank
you for your answers : " + ans);
}});
function
GetSiteUrl() {
var
urlParts = document.location.href.split("/");
return
urlParts[0] + "//" + urlParts[2] + "/" + urlParts[3] +
"/" + urlParts[4];
}
Page2 Example
This is a
simple data grid operation.
HTML
<script
src="http://code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/angular.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/Page2.js"></script>
<link
rel="stylesheet" type="text/css"
href="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/bootstrap.css">
<style
type="text/css">
.auto-style1
{ color: #339933; }
</style>
<div
ng-app="myApp">
<div
ng-controller="ContactController">
<label><span
class="auto-style1"><strong>Enter
data.</strong></span><br />
<br
/> Name</label>
<input
type="text" name="name"
ng-model="newcontact.name"/>
<label>Email</label>
<input
type="text" name="email"
ng-model="newcontact.email"/>
<label>Phone</label>
<input
type="text" name="phone"
ng-model="newcontact.phone"/>
<br/>
<input
type="hidden" ng-model="newcontact.id"/>
<br>
<input
type="button" value="Save It"
ng-click="saveContact()" class="btn btn-primary"
/><br />
<table
class="table table-striped table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="contact in
contacts">
<td>{{
contact.name }}</td>
<td>{{
contact.email }}</td>
<td>{{
contact.phone }}</td>
<td>
<a
href="javascript:void(0)"
ng-click="edit(contact.id)">edit</a> |
<a
href="javascript:void(0)"
ng-click="delete(contact.id)">delete</a>
</td>
</tr>
</tbody>
</table></div></div>
JavaScript:
var
myApp = angular.module('myApp', []);
var
uid = (function () {
var
id = 1;
return
function () {
if
(arguments[0] === 0)
id
= 1;
return
id++;
}
})();
myApp.controller('ContactController',
function ($scope) {
$scope.contacts
= [{ id: 0, 'name': 'Arindam', 'email': 'Arindam@myemail.com', 'phone':
'123-2343-44' }];
$scope.saveContact
= function () {
if
($scope.newcontact.id == null) {
$scope.newcontact.id
= uid();
$scope.contacts.push($scope.newcontact);
}
else
{
for
(i in $scope.contacts) {
if
($scope.contacts[i].id == $scope.newcontact.id) {
$scope.contacts[i]
= $scope.newcontact;
}
}
}
$scope.newcontact
= {};
}
$scope.delete
= function (id) {
for
(i in $scope.contacts) {
if
($scope.contacts[i].id == id) {
$scope.contacts.splice(i,
1); $scope.newcontact = {};
}
}
}
$scope.edit =
function (id) {
for (i in
$scope.contacts) {
if
($scope.contacts[i].id == id) {
$scope.newcontact
= angular.copy($scope.contacts[i]);
}
}
}
});
Page3 Example
In this
example I have done some real SharePoint operations, I have created a custom
list named Employee and then read the list items in a tabular mode. Now
there is more than one way of doing list operation in client side coding. Here
I have used the list service but this is not just normal list service call, I
have used Angula JS’s capability of calling a http method and parse data.
Latter I have another example with async client calling.
Check how
small amount of coding was required for this. You must have noticed that
bootstrap CSS was used for the table formatting.
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/jquery-2.1.3.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/angular.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/bootstrap.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/Page3.js"></script>
<link
rel="stylesheet" type="text/css"
href="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/bootstrap.min.css">
<style
type="text/css">
.auto-style1
{
color:
#339933;
}
</style>
<div
ng-app="SharePointAngApp" class="row">
<div
ng-controller="spCustomerController" class="span10">
<table
class="table table-striped table-bordered">
<tr>
<th>Employee
ID</th>
<th>Employee
Name</th>
<th>City</th>
</tr>
<tr
ng-repeat="customer in customers">
<td>{{customer.Id}}</td>
<td>{{customer.EmpName}}</td>
<td>{{customer.EmpCity}}</td>
</tr>
</table>
</div>
</div>
JavaScript:
var
webSiteURL = GetSiteUrl();
//alert(webSiteURL);
var
myAngApp = angular.module('SharePointAngApp', []);
myAngApp.controller('spCustomerController',
function ($scope, $http) {
$http({
method:
'GET',
url:
webSiteURL +
"/_api/web/lists/getByTitle('Employee')/items?$select=Id,EmpName,EmpCity",
headers:
{ "Accept": "application/json;odata=verbose" }
}).success(function
(d, s, h, c) {
$scope.customers
= d.d.results;
});
});
function
GetSiteUrl() {
var
urlParts = document.location.href.split("/");
return
urlParts[0] + "//" + urlParts[2] + "/" + urlParts[3] +
"/" + urlParts[4] + "/" + urlParts[5];
}
Page4 Example
In this
example I’m adding a new item to the Employee list, client site async call have
been used for this.
HTML
<script
src="http://code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/angular.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/Page4.js"></script>
<link
rel="stylesheet" type="text/css"
href="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/bootstrap.css">
<style
type="text/css">
.auto-style1
{
color:
#339933;
}
</style>
<div
ng-app="myApp">
<div
ng-controller="ContactController">
<div
id="PutYourContentInside">
<ol>
<li
ng-repeat="question in questions">
<span>{{question.text}}</span>
<br
/>
<input
type="text" ng-model="question.answer"/>
</li>
</ol>
<input
type="submit" value="Submit"
ng-click="addAnswers($event)"/>
</div>
</div>
</div>
JavaScript:
var
myApp = angular.module('myApp', []);
var
uid = (function () {
var
id = 1;
return
function () {
if
(arguments[0] === 0)
id
= 1;
return
id++;
}
})();
var
siteUrl = GetSiteUrl();
//alert(siteUrl);
myApp.controller('ContactController',
function ($scope) {
$scope.questions
= [
{
text: 'What is the new Employee name?', answer: "" },
{
text: 'Where does he live?', answer: "" }
];
$scope.addAnswers
= function ($event) {
$event.preventDefault();
var
clientContext = new SP.ClientContext(siteUrl);
var
web = clientContext.get_web();
var
list = web.get_lists().getByTitle('Employee');
//
create the ListItemInformational object
var
listItemInfo = new SP.ListItemCreationInformation();
//
add the item to the list
var
listItem = list.addItem(listItemInfo);
//
Assign Values for fields
listItem.set_item('EmpName',
$scope.questions[0].answer);
listItem.set_item('EmpCity',
$scope.questions[1].answer);
listItem.update();
clientContext.executeQueryAsync(
Function.createDelegate(this,
onQuerySucceeded),
Function.createDelegate(this,
onQueryFailed)
);
};
onQuerySucceeded
= function () {
alert('Thank
you, one record added to Employee list.');
}
onQueryFailed =
function (sender, args) {
alert('Request
failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
});
function
GetSiteUrl() {
var urlParts =
document.location.href.split("/");
return
urlParts[0] + "//" + urlParts[2] + "/" + urlParts[3] +
"/" + urlParts[4] + "/" + urlParts[5];
}
Page5 Example
Here I’m
reading data again from Employee list; I have used JQury’s capability of
calling a service and get the response in JSON format, for Image parsing we
need to do some special coding pls note that in the JS file.
HTML
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/jquery-2.1.3.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/angular.min.js"></script>
<script
src="http://SERVERNAME/sites/arindamc/Subsite1/SiteAssets/Page5.js"></script>
<div
ng-app="myApp">
<div
ng-controller="ContactController">
<div
ng-repeat="p in Products">
<table
style="background-color:#f07432">
<tr><td
align = "center"><b>Employee Name: {{p.EmpName}}</b>
</td></tr>
<tr><td
align = "center"><img ng-src={{p.EmpImage}} />
</td></tr>
<tr><td
align = "center"><b> Employee
City:{{p.EmpCity}}.</b></td></tr>
</table>
<hr
/>
</div></div></div>
JavaScript:
var
myApp = angular.module('myApp', []);
var
webSiteURL = GetSiteUrl();
myApp.controller('ContactController',
function ($scope) {
$scope.loadREST
= function () {
jQuery.ajax({
url:
webSiteURL +
"/_vti_bin/listdata.svc/Employee?$select=EmpName,EmpCity,EmpImage",
type:
"GET",
dataType:
"json",
async:
"true",
headers:
{ "Accept": "application/json;odata=verbose" },
success:
function (data) {
var
newData = [];
jQuery.each(data.d.results,
function (index, value) {
//alert(value.EmpImage);
var
pImage = value.EmpImage;
if
(pImage != null) {
prImage
= pImage.substring(0, pImage.indexOf(','));
newData.push({
EmpName: value.EmpName, EmpCity: value.EmpCity, EmpImage: prImage });
}
});
$scope.$apply(function
() {
$scope.Products
= newData;
});
},
error:
function () {
alert("error");
}
});
};
$scope.loadREST();
});
function
GetSiteUrl() {
var urlParts =
document.location.href.split("/");
return
urlParts[0] + "//" + urlParts[2] + "/" + urlParts[3] +
"/" + urlParts[4] + "/" + urlParts[5];
}
Page6
Example
This example
is adopted from this awesome article: Chart SharePoint Hosted App with
AngularJs and JqPlot
Please go ahead and check the details there. The
scenario here is to have data inside SharePoint List (custom list, one column
for title and one column for value), 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
Note: The original article was written as an App on SharePoint
2013, I have converted it to fully client side coding, no Visual studio was
used. For that I have provided the HTML below, also I had to make some changes
the App.JS which is the main JS file for this (like Page1.Js for Page1.aspx in
previous articles).
I have also
copied the whole script folder as SiteAsset\ChartApp in the SharePoint
portal. The content was copied from the Your Download path \ChartApp\Scripts
folder. This has duplicate angular.js/JQuey-min.js etc. which you need to
take care in actual scenario.
I also copied App.Css from ChartApp\Content
folder to SiteAsset\ChartApp in the SharePoint portal. The site assets
now look like below
HTML
<script
type="text/javascript" src="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/jquery-1.9.1.min.js"></script>
<script
type="text/javascript"
src="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/lib/angular/angular.js"></script>
<script
type="text/javascript"
src="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/lib/jqplot/jquery.jqplot.js"></script>
<script
type="text/javascript"
src="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/lib/jqplot/plugins/jqplot.pieRenderer.js"></script>
<script
type="text/javascript"
src="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/lib/jqplot/plugins/jqplot.barRenderer.min.js"></script>
<script
type="text/javascript"
src="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/lib/jqplot/plugins/jqplot.categoryAxisRenderer.min.js"></script>
<script
type="text/javascript"
src="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/lib/jqplot/plugins/jqplot.pointLabels.min.js"></script>
<script
type="text/javascript"
src="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/lib/chart.js"></script>
<script
type="text/javascript"
src="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/Common.js"></script>
<link
rel="stylesheet" type="text/css"
href="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/lib/jqplot/jquery.jqplot.css"/>
<!--
Add your CSS styles to the following file -->
<link
rel="Stylesheet" type="text/css"
href="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/App.css"
/>
<!--
Add your JavaScript to the following file -->
<script
type="text/javascript"
src="http://inbaghpc00156/sites/arindamc/Subsite1/SiteAssets/ChartApp/App.js"></script>
<div>
<p
id="message">
</p>
</div>
<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>
JavaScript
The below
script is the App.js, I did not have to modify any other JS file. I also have
commented the original code so that you can understand my changes.
'use
strict';
//
The allAnnouncements variable is used by more than one
//
function to retrieve and process the results.
var
allData;
var
hostweburl;
var
appweburl;
//alert('inside');
var
myChartingApp = angular.module('myChartingApp', ['ui.chart'])
.value('charting',
{
pieChartOptions:
{
seriesDefaults:
{
//
Make this a pie chart.
renderer: jQuery.jqplot.PieRenderer,
rendererOptions: {
//
Put data labels on the pie slices.
//
By default, labels show the percentage of the slice.
showDataLabels:
true
}
},
legend:
{ show: true, location: 'e' }
}
});
myChartingApp.service('$SharePointJSOMService',
function ($q, $http) {
this.getData
= function ($scope, listTitle) {
var
deferred = $q.defer();
hostweburl
= 'http://inbaghpc00156/sites/arindamc/Subsite1';
//
decodeURIComponent(
//
getQueryStringParameter("SPHostUrl")
//
);
appweburl
= 'http://inbaghpc00156/sites/arindamc/Subsite1';
//
decodeURIComponent(
//
getQueryStringParameter("SPAppWebUrl")
//
);
//alert(hostweburl);
//alert(appweburl);
//
resources are in URLs in the form:
//
web_url/_layouts/15/resource
var
scriptbase = hostweburl + "/_layouts/15/";
//
Load the js files and continue to the successHandler
$.getScript(scriptbase
+ "SP.Runtime.js",
function
() {
$.getScript(scriptbase
+ "SP.js",
function
() {
$.getScript(scriptbase
+ "SP.RequestExecutor.js",
function
() {
//
var clientContext = new SP.ClientContext(siteUrl);
//
var web = clientContext.get_web();
// var list = web.get_lists().getByTitle('Employee');
var context = new
SP.ClientContext(appweburl);
//var
factory = new SP.ProxyWebRequestExecutorFactory(appweburl);
//
context.set_webRequestExecutorFactory(factory);
//
var appContextSite = new SP.AppContextSite(context, hostweburl);
//var
web = appContextSite.get_web();
var
web = context.get_web();
context.load(web);
var
list = web.get_lists().getByTitle(listTitle);
var
camlQuery = SP.CamlQuery.createAllItemsQuery();
this.listItems
= list.getItems(camlQuery);
context.load(this.listItems);
context.executeQueryAsync(
Function.createDelegate(this,
function () {
var
ListEnumerator = this.listItems.getEnumerator();
while
(ListEnumerator.moveNext()) {
var
currentItem = ListEnumerator.get_current();
console.info(currentItem.get_item('Title')
+ currentItem.get_item('ID'));
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);
//$scope
is not updating so force with this command
$scope.$apply();
}
$scope.setBarChart();
$scope.setLineChart();
}),
Function.createDelegate(this,
function (sender, args) {
deferred.reject('Request
failed. ' + args.get_message() + '\n' + args.get_stackTrace());
})
);
}
);
}
);
}
);
return
deferred.promise;
};
});
myChartingApp.controller('DemoCtrl',
function ($scope, charting, $SharePointJSOMService) {
//alert('controller');
$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;
$scope.setBarChart
= function () {
//alert('setBarChart');
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
}
}
});
}
$scope.setLineChart
= function () {
//alert('setLineChart');
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' }]
});
}
});
Output
POINTS TO BE NOTED
I have masked the actual server name with SERVERNAME is all
JS references, you need to use correct URL.
If you are using CDN, make sure internet is available in
your server
function
GetSiteUrl()- is a very
basic method, you need to modify it based on the depth of the SharePoint portal
URL you are using, for me the URL is http://SERVRNAME/arindamc/Subsite1, so I used until urlParts[4] in the return statement.
I Hope its help for learning.