Click here to Skip to main content
15,868,014 members
Articles / Web Development / HTML

SharePoint 2013 and Angularjs

Rate me:
Please Sign up or sign in to vote.
4.60/5 (22 votes)
2 Jan 2016CPOL5 min read 171.5K   3.3K   23   53
Taking the great advantage of two way data binding of AngularJS in SharePoint 2013. AngularJS can be used in Content Editor Web Part or SharePoint Apps.

Introduction

We all know that AngularJS is currently most famous for both way data binding framework among all. This article aims to show how this advantage (both way data binding) can be taken in SharePoint 2013. Before reading this article, you must have knowledge of AngularJS and SharePoint 2013 REST API.

AngularJS can help us in developing Content Editor Web Parts and apps. In most of the cases, I found that people choose Sandbox solution but the REST API of SharePoint 2013 is so powerful that Sandbox solutions can be replaced by the Content Editor Web Parts or apps easily. In this article, we will see how AngularJS can be used in Content Editor Web Parts and SharePoint hosted apps.

Initial Setup

For keeping everything simple, I will demonstrate a CRUD operation on a list because we know that CRUD is a very common part in any kinds of business requirement. I named the list as People. Apart from that, you will need SharePoint Designer 2013 and Visual Studio 2013 or 2012. Finally, make sure that your server is prepared for developing SharePoint hosted app.

people-list

The following features will be implemented such View All Items and Delete Item.

all

Add New Item

add

Edit Item

edit

AngularJs in Content Editor Web Part

If you are not familiar with Content Editor Web Part please have a look here. Now from Designer, create a folder in any asset library and then a text file. In my case, I have created a folder named People and text file named people-main.txt inside Site Assets library. Finally, add this file URL in Content Link. My URL becomes /SiteAssets/People/people-main.txt as I am using my root site collection. If you prefer to work from any sub-site, then add its path to the URL properly.

Now text file is ready for adding HTML code.

HTML
<link href="/SiteAssets/People/css/style.css" rel="stylesheet" type="text/css">
<script src="/SiteAssets/People/lib/angular.min.js" type="text/javascript"></script>
<script src="/SiteAssets/People/lib/angular-route.min.js" type="text/javascript"></script>
<script src="/SiteAssets/People/js/app.js" type="text/javascript"></script>
<script src="/SiteAssets/People/js/services/baseSvc.js" type="text/javascript"></script>
<script src="/SiteAssets/People/js/services/people/people.js" type="text/javascript"></script>
<script src="/SiteAssets/People/js/controllers/people/all.js" type="text/javascript"></script>
<script src="/SiteAssets/People/js/controllers/people/add.js" type="text/javascript"></script>
<script src="/SiteAssets/People/js/controllers/people/edit.js" type="text/javascript"></script>

<div data-ng-app="peopleApp">
    <div data-ng-view class="people-app"></div>
</div>

Actually, in this file, I have added the necessary script sources and initialised ng-app and ng-view. Rather than that, I have linked css file here also. 

folder-structure

Now let's have look at script files. Everything is very much similar as typical AngularJS apps. Now let me explain what are the differences.

app.js

JavaScript
"use strict";
(function() {
    angular.module("peopleApp", ["ngRoute"])
        .config(["$routeProvider", function($routeProvider) {
            $routeProvider.when("/", {
                templateUrl: "/SiteAssets/People/templates/people/all.html",
                controller: "allPeopleCtrl"
            }).when("/addPerson", {
                templateUrl: "/SiteAssets/People/templates/people/add.html",
                controller: "addPersonCtrl"
            }).when("/editPerson/:personId", {
                templateUrl: "/SiteAssets/People/templates/people/edit.html",
                controller: "editPersonCtrl"
            });
        }]);
})();

peopleApp is created here and necessary routes are defined.

baseSvc.js

JavaScript
"use strict";
(function() {
    angular.module("peopleApp")
        .factory("baseSvc", ["$http", "$q", function($http, $q) {
            var baseUrl = _spPageContextInfo.webAbsoluteUrl;
            var getRequest = function(query) {
                var deferred = $q.defer();
                $http({
                        url: baseUrl + query,
                        method: "GET",
                        headers: {
                            "accept": "application/json;odata=verbose",
                            "content-Type": "application/json;odata=verbose"
                        }
                    })
                    .success(function(result) {
                        deferred.resolve(result);
                    })
                    .error(function(result, status) {
                        deferred.reject(status);
                    });
                return deferred.promise;
            };
            var postRequest = function(data, url) {
                var deferred = $q.defer();
                $http({
                        url: baseUrl + url,
                        method: "POST",
                        headers: {
                            "accept": "application/json;odata=verbose",
                            "X-RequestDigest": document.getElementById("__REQUESTDIGEST").value,
                            "content-Type": "application/json;odata=verbose"
                        },
                        data: JSON.stringify(data)
                    })
                    .success(function(result) {
                        deferred.resolve(result);
                    })
                    .error(function(result, status) {
                        deferred.reject(status);
                    });
                return deferred.promise;
            };
            var updateRequest = function(data, url) {
                var deferred = $q.defer();
                $http({
                        url: baseUrl + url,
                        method: "PATCH",
                        headers: {
                            "accept": "application/json;odata=verbose",
                            "X-RequestDigest": document.getElementById("__REQUESTDIGEST").value,
                            "content-Type": "application/json;odata=verbose",
                            "X-Http-Method": "PATCH",
                            "If-Match": "*"
                        },
                        data: JSON.stringify(data)
                    })
                    .success(function(result) {
                        deferred.resolve(result);
                    })
                    .error(function(result, status) {
                        deferred.reject(status);
                    });
                return deferred.promise;
            };
            var deleteRequest = function(url) {
                var deferred = $q.defer();
                $http({
                        url: baseUrl + url,
                        method: "DELETE",
                        headers: {
                            "accept": "application/json;odata=verbose",
                            "X-RequestDigest": document.getElementById("__REQUESTDIGEST").value,
                            "IF-MATCH": "*"
                        }
                    })
                    .success(function(result) {
                        deferred.resolve(result);
                    })
                    .error(function(result, status) {
                        deferred.reject(status);
                    });
                return deferred.promise;
            };
            return {
                getRequest: getRequest,
                postRequest: postRequest,
                updateRequest: updateRequest,
                deleteRequest: deleteRequest
            };
        }]);
})();

I make a generic service for all kinds of requests like GET, POST, UPDATE and DELETE so that we can inject it from anywhere. I am not describing here how these methods work because I have an another article about it. In that article, I demonstrated it using jQuery and in this article I have converted it into AngularJS. This baseSvc is injected in peopleService what benefitted us from repeating codes while getting, adding, updating and deleting items. Before sending any request, you have to pass required URL and parameters to the baseSvc methods.

JavaScript
"use strict";
(function() {
    angular.module("peopleApp")
        .factory("peopleService", ["baseSvc", function(baseService) {
            var listEndPoint = '/_api/web/lists';
            var getAll = function() {
                var query = listEndPoint + "/GetByTitle('People')/Items?$select=ID,
                         FirstName,LastName,Address";
                return baseService.getRequest(query);
            };
            var addNew = function(person) {
                var data = {
                    __metadata: {
                        'type': 'SP.Data.PeopleListItem'
                    },
                    FirstName: person.firstName,
                    LastName: person.lastName,
                    Address: person.address
                };
                var url = listEndPoint + "/GetByTitle('People')/Items";
                return baseService.postRequest(data, url);
            };
            var getById = function(personId) {
                var query = listEndPoint + "/GetByTitle('People')/GetItemById
                (" + personId + ")?$select=ID,FirstName,LastName,Address";
                return baseService.getRequest(query);
            };
            var update = function(person) {
                var data = {
                    __metadata: {
                        'type': 'SP.Data.PeopleListItem'
                    },
                    FirstName: person.firstName,
                    LastName: person.lastName,
                    Address: person.address
                };
                var url = listEndPoint + "/GetByTitle('People')/
                GetItemById(" + person.personId + ")";
                return baseService.updateRequest(data, url);
            };
            var remove = function(personId) {
                var url = listEndPoint + "/GetByTitle('People')/
                GetItemById(" + personId + ")";
                return baseService.deleteRequest(url);
            };
            return {
                getAll: getAll,
                addNew: addNew,
                getById: getById,
                update: update,
                remove: remove
            };
        }]);
})();

You may have a question that how did I construct the URLs and parameters for each request. Again, you have to check this article.

Now have look on templates and controllers.

All Items

This template is intended to show items and remove items.

HTML
<a href="#/addPerson" class="add-new-button">Add New Person</a>
<div class="all-people">
    <table>
        <tbody>
            <tr>
                <th>Fist Name</th>
                <th>Last Name</th>
                <th>Address</th>
                <th>Action</th>
            </tr>
            <tr data-ng-repeat="person in people">
                <td>{{person.FirstName}}</td>
                <td>{{person.LastName}}</td>
                <td>{{person.Address}}</td>
                <td>
                    <a href="#/editPerson/{{person.ID}}">
                       <img src="/SiteAssets/People/images/edit.png" alt=""></a>
                    <a href="" data-ng-click="removePerson(person)">
                       <img src="/SiteAssets/People/images/delete.png" alt=""></a>
                </td>
            </tr>
        </tbody>
    </table>
</div>

Respective controller for this view looks like the following:

JavaScript
"use strict";
(function () {
    angular.module("peopleApp")
        .controller("allPeopleCtrl", ["$scope", "peopleService",
        function ($scope, peopleService) {
            peopleService.getAll()
                .then(function (response) {
                $scope.people = response.d.results;
            });
            $scope.removePerson = function(person){
                peopleService.remove(person.ID)
                .then(function(response){
                    var personIndex = $scope.people.indexOf(person);
                    $scope.people.splice(personIndex,1);
                });
            };
        }]);
})();

You must notice that I have requested ID, FirstName, LastName and Address in peopleService using getAll(). In this controller, items returned by peopleService are binded to the view using ng-repeat. Removing item is also implemented here. I am not describing other views and controllers here because it will elaborate this article unnecessarily. Hope you will understand everything after downloading my source code.

NB: Don't use any form tag inside the template.

AngularJS in Hosted App

Before developing hosted app for on-premises, environment must be configured properly. In that case, office-365 is much more easier. Environment configuration is not needed at all. Now, create a developer site collection by choosing Developer Site template. We cannot use any other sites except Developer Site for app development. Now add the same People list in this site.

Create a new project in Visual Studio and choose App for SharePoint 2013 from Office/SharePoint template.

New Project

Click ok, Enter your site URL which you want to use for debugging, choose SharePoint-Hosted and finally click on finish.

Url selection

When we create a SharePoint Hosted app, it gives us a standard folder structure for our scripts and design files. We cannot follow what we did in Content Editor. I have added my files in the following structure.

app-folder

Basically, I have added my all scripts in Scripts folder, styles in Content folder and images to the Images folder. For template files, I have added a new folder named Templates. Now, open the Default.aspx page and modify it like following:

ASP.NET
<%-- The following 4 lines are ASP.NET directives needed when using SharePoint components --%>
<%@ Page Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, 
Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" 
MasterPageFile="~masterurl/default.master" Language="C#" %>
<%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" 
Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" 
Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" 
Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%-- The markup and script in the following Content element 
will be placed in the <head> of the page --%>
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
    <script type="text/javascript" src="/_layouts/15/sp.runtime.js"></script>
    <script type="text/javascript" src="/_layouts/15/sp.js"></script>
    <meta name="WebPartPageExpansion" content="full" />
    <link rel="Stylesheet" type="text/css" href="../Content/style.css" />
    <script src="../Scripts/angular.min.js" type="text/javascript"></script>
    <script src="../Scripts/angular-route.min.js" type="text/javascript"></script>
    <script src="../Scripts/peopleApp/app.js" type="text/javascript"></script>
    <script src="../Scripts/peopleApp/services/baseSvc.js" type="text/javascript"></script>
    <script src="../Scripts/peopleApp/services/people/people.js" type="text/javascript"></script>
    <script src="../Scripts/peopleApp/controllers/people/all.js" type="text/javascript"></script>
    <script src="../Scripts/peopleApp/controllers/people/add.js" type="text/javascript"></script>
    <script src="../Scripts/peopleApp/controllers/people/edit.js" type="text/javascript"></script>
</asp:Content>
<asp:Content ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">
    SharePoint 2013 Hosted App and AngularJS Demo
</asp:Content>
<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <div data-ng-app="peopleApp">
        <div data-ng-view class="people-app"></div>
    </div>
</asp:Content>

In this page, I have added all script and style references and also initialized peopleApp here. Now there is a little change in app.js and baseSvc.js file. In app.js, we have to change the template URLs as following.

JavaScript
"use strict";
(function () {
    angular.module("peopleApp", ["ngRoute"])
        .config(["$routeProvider", function ($routeProvider) {
            $routeProvider.when("/", {
                templateUrl: "../Templates/people/all.html",
                controller: "allPeopleCtrl"
            }).when("/addPerson", {
                templateUrl: "../Templates/people/add.html",
                controller: "addPersonCtrl"
            }).when("/editPerson/:personId", {
                templateUrl: "../Templates/people/edit.html",
                controller: "editPersonCtrl"
            });
        }]);
})();

In the baseSvc.js, we have to use _spPageContextInfo.siteAbsoluteUrl instead of _spPageContextInfo.webAbsoluteUrl for baseUrl.

JavaScript
var baseUrl = _spPageContextInfo.siteAbsoluteUrl;

We are all done in developing SharePoint Hosted app using AngularJS. Another thing, I must mention that whenever you add any file into your project, please check the respective Elements.xml file. Scripts, Content and Images have their own Elements.xml file. For example, I have added template files inside Templates folder, So my main Elements.xml file should look like the following:

XML
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="RootModule">
    <File Path="Templates\people\add.html" 
    Url="Templates/people/add.html" ReplaceContent="TRUE" />
    <File Path="Templates\people\all.html" 
    Url="Templates/people/all.html" ReplaceContent="TRUE" />
    <File Path="Templates\people\edit.html" 
    Url="Templates/people/edit.html" ReplaceContent="TRUE" />
  </Module>
</Elements>

Now right click on your project and click on deploy. I hope you will find following view in browser.

app home

Conclusion

Hope you enjoyed this article. Now download my source code and start digging into it in your own way. Use comment section for issues and suggestions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Instructor / Trainer Jashore University of Science and Technology
Bangladesh Bangladesh
2016 Microsoft MVP

Currently, I am devoted to provide technical and development support to the SharePoint clients and also I am working on angularjs. I am experienced with C#, ASP.NET, SharePoint, SignalR, angularjs, MS SQL, Oracle 11g R2, Windows Phone, Firefox OS and so on. I have fallen in love with many technologies but never got married to any of them. I am evolving myself as full stack developer. I always like to share knowledge as much as to gather from you.

Comments and Discussions

 
QuestionAngularJS v1.7.8 Pin
RalphZero25-Sep-19 12:10
RalphZero25-Sep-19 12:10 
QuestionAdding file to Sharepoint library folder using AngularJS $http Pin
Member 139370933-Aug-18 5:45
Member 139370933-Aug-18 5:45 
QuestionWorks great, but how about large data? Pin
Jaitsujin26-Jul-18 19:22
Jaitsujin26-Jul-18 19:22 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun5-Feb-18 19:11
Humayun Kabir Mamun5-Feb-18 19:11 
PraiseExcellent Pin
Member 1357727614-Dec-17 20:16
Member 1357727614-Dec-17 20:16 
GeneralGood Work Pin
maheshkatakam14-Mar-17 19:07
maheshkatakam14-Mar-17 19:07 
QuestionI am facing issues with routing and some permission issues Pin
raghupola9-Mar-17 5:38
raghupola9-Mar-17 5:38 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun2-Jan-17 17:50
Humayun Kabir Mamun2-Jan-17 17:50 
QuestionRequired App Operating Multiple Lists Pin
Ramakrishna Gowda29-Nov-16 15:19
Ramakrishna Gowda29-Nov-16 15:19 
AnswerRe: Required App Operating Multiple Lists Pin
Atish Dipongkor29-Nov-16 20:03
professionalAtish Dipongkor29-Nov-16 20:03 
QuestionI am facing an error when inserting record in to multiple lists using common method Pin
BHANU PRAKASH BYSANI2-Jun-16 1:16
professionalBHANU PRAKASH BYSANI2-Jun-16 1:16 
Hi Atish,

I am inserting record in to list A calling function of common service addNew method from controller, then method after getting response I am calling same add new method to insert a record in to list b, finally list c.


In most of the cases record has been inserting to list a, but failing in wither list b or c. Some times even inserting a record in to list b also it's not going to success method failure method with status zero and error object null.

Could you please guide me.
AnswerRe: I am facing an error when inserting record in to multiple lists using common method Pin
Atish Dipongkor2-Jun-16 3:09
professionalAtish Dipongkor2-Jun-16 3:09 
QuestionSave Button does not work Pin
ranasnehal18-May-16 9:29
ranasnehal18-May-16 9:29 
AnswerRe: Save Button does not work Pin
Atish Dipongkor18-May-16 19:54
professionalAtish Dipongkor18-May-16 19:54 
GeneralRe: Save Button does not work Pin
Member 1221084820-Jun-16 23:50
Member 1221084820-Jun-16 23:50 
GeneralRe: Save Button does not work Pin
xcmsxxx8-Nov-16 2:24
xcmsxxx8-Nov-16 2:24 
GeneralRe: Save Button does not work Pin
Atish Dipongkor8-Nov-16 4:36
professionalAtish Dipongkor8-Nov-16 4:36 
GeneralRe: Save Button does not work Pin
xcmsxxx8-Nov-16 4:52
xcmsxxx8-Nov-16 4:52 
GeneralRe: Save Button does not work Pin
Atish Dipongkor8-Nov-16 18:55
professionalAtish Dipongkor8-Nov-16 18:55 
QuestionUnable to view Pin
malgoul6-Apr-16 19:13
malgoul6-Apr-16 19:13 
AnswerRe: Unable to view Pin
Atish Dipongkor9-May-16 0:04
professionalAtish Dipongkor9-May-16 0:04 
GeneralRe: Unable to view Pin
naveen121516-Jun-16 9:52
naveen121516-Jun-16 9:52 
QuestionAsking for credentials again and again Pin
patswapnil8-Mar-16 19:41
patswapnil8-Mar-16 19:41 
AnswerRe: Asking for credentials again and again Pin
Member 869193824-Apr-16 11:03
Member 869193824-Apr-16 11:03 
GeneralRe: Asking for credentials again and again Pin
lseltzer13-Jan-17 10:09
lseltzer13-Jan-17 10:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.