Click here to Skip to main content
13,193,048 members (68,191 online)
Click here to Skip to main content
Add your own
alternative version

Stats

93.6K views
2.6K downloads
18 bookmarked
Posted 12 Aug 2015

SharePoint 2013 and Angularjs

, 2 Jan 2016
Rate this:
Please Sign up or sign in to vote.
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.

<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

"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

"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.

"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.

<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:

"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:

<%-- 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.

"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.

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 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)

Share

About the Author

Atish Dipongkor
Software Developer BrainStation-23
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.

You may also be interested in...

Comments and Discussions

 
GeneralGood Work Pin
maheshkatakam14-Mar-17 19:07
membermaheshkatakam14-Mar-17 19:07 
QuestionI am facing issues with routing and some permission issues Pin
Member 108849799-Mar-17 5:38
memberMember 108849799-Mar-17 5:38 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun2-Jan-17 17:50
memberHumayun Kabir Mamun2-Jan-17 17:50 
QuestionRequired App Operating Multiple Lists Pin
Ramakrishna Gowda29-Nov-16 15:19
memberRamakrishna 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 
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
memberranasnehal18-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
memberMember 1221084820-Jun-16 23:50 
GeneralRe: Save Button does not work Pin
xcmsxxx8-Nov-16 2:24
memberxcmsxxx8-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
memberxcmsxxx8-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
membermalgoul6-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
membernaveen121516-Jun-16 9:52 
QuestionAsking for credentials again and again Pin
patswapnil8-Mar-16 19:41
memberpatswapnil8-Mar-16 19:41 
AnswerRe: Asking for credentials again and again Pin
Member 869193824-Apr-16 11:03
memberMember 869193824-Apr-16 11:03 
GeneralRe: Asking for credentials again and again Pin
lseltzer13-Jan-17 10:09
memberlseltzer13-Jan-17 10:09 
QuestionCannot read property 'value' of null Pin
avipawar7-Mar-16 11:03
memberavipawar7-Mar-16 11:03 
QuestionThanks a lot for the awesome article, small problem with address column data Pin
Member 114762995-Mar-16 23:37
memberMember 114762995-Mar-16 23:37 
AnswerRe: Thanks a lot for the awesome article, small problem with address column data Pin
Atish Dipongkor6-Mar-16 17:23
professionalAtish Dipongkor6-Mar-16 17:23 
GeneralRe: Thanks a lot for the awesome article, small problem with address column data Pin
Rishabh127-Mar-16 10:46
memberRishabh127-Mar-16 10:46 
PraiseGreat stuff Atish Pin
KannansCodeProject1-Mar-16 12:05
memberKannansCodeProject1-Mar-16 12:05 
QuestionAngularJs: How to set radio button checked based on model Pin
Fabio Barcelos26-Feb-16 5:04
professionalFabio Barcelos26-Feb-16 5:04 
AnswerRe: AngularJs: How to set radio button checked based on model Pin
Atish Dipongkor26-Feb-16 6:32
professionalAtish Dipongkor26-Feb-16 6:32 
QuestionError - Microsoft.SharePoint.Client.InvalidClientQueryException Pin
Member 122814244-Feb-16 2:48
memberMember 122814244-Feb-16 2:48 
AnswerRe: Error - Microsoft.SharePoint.Client.InvalidClientQueryException Pin
Atish Dipongkor7-Feb-16 4:01
professionalAtish Dipongkor7-Feb-16 4:01 
GeneralMy vote of 5 Pin
M Rayhan31-Jan-16 19:54
memberM Rayhan31-Jan-16 19:54 
GeneralRe: My vote of 5 Pin
Atish Dipongkor3-Feb-16 1:37
professionalAtish Dipongkor3-Feb-16 1:37 
QuestiongetCurrentUser function Pin
Member 1228142422-Jan-16 7:42
memberMember 1228142422-Jan-16 7:42 
AnswerRe: getCurrentUser function Pin
Atish Dipongkor28-Jan-16 6:20
professionalAtish Dipongkor28-Jan-16 6:20 
QuestionHow to add/edit SharePoint User (People/Group) field using AngularJS? Pin
Member 1214745723-Nov-15 5:24
memberMember 1214745723-Nov-15 5:24 
GeneralThank you for sharing Pin
Van_Lai12-Aug-15 16:32
memberVan_Lai12-Aug-15 16:32 
GeneralRe: Thank you for sharing Pin
Atish Dipongkor12-Aug-15 20:07
professionalAtish Dipongkor12-Aug-15 20:07 
QuestionOnly first 100 items Pin
Ofer Gal6-Jul-15 23:30
memberOfer Gal6-Jul-15 23:30 
AnswerRe: Only first 100 items Pin
Atish Dipongkor7-Jul-15 1:45
professionalAtish Dipongkor7-Jul-15 1:45 
QuestionWonder if you can help with angular-ui/ui-select Pin
Ofer Gal5-Jul-15 6:29
memberOfer Gal5-Jul-15 6:29 
AnswerRe: Wonder if you can help with angular-ui/ui-select Pin
Atish Dipongkor5-Jul-15 7:49
professionalAtish Dipongkor5-Jul-15 7:49 
GeneralRe: Wonder if you can help with angular-ui/ui-select Pin
Ofer Gal5-Jul-15 9:56
memberOfer Gal5-Jul-15 9:56 
GeneralRe: Wonder if you can help with angular-ui/ui-select Pin
Atish Dipongkor5-Jul-15 20:20
professionalAtish Dipongkor5-Jul-15 20:20 
GeneralSome images are missing Pin
Atish Dipongkor26-Jun-15 19:10
professionalAtish Dipongkor26-Jun-15 19:10 
QuestionRe: Some images are missing Pin
Nelek28-Jun-15 2:50
protectorNelek28-Jun-15 2:50 
AnswerRe: Some images are missing Pin
Atish Dipongkor28-Jun-15 7:20
professionalAtish Dipongkor28-Jun-15 7:20 

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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.171017.2 | Last Updated 2 Jan 2016
Article Copyright 2015 by Atish Dipongkor
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid