Building SPA Using AngularJS

I have created a single page application (SPA) using Angular.js because I had a requirement where the users can update their monthly activities corresponding to their respective villages. I named the application as Continuous Quality Improvement Tracker (CQI).

In this application, I am going to explain how we can use Angular.js with SharePoint. I am also going to explain many AngularJS directives, like -

  • ng-app
  • $scope
  • ngRoute
  • $routeProvider
  • ng-model
  • ng-options
  • ng-change
  • ng-init
  • data-ng-repeat

We need to create five lists in the application -

  1. AreaDirectory (Internal name) – To store areas

Structure of Area Directory

Columns 

    • Title (Areas stored in Title)

      Angular
  1. VillageDirectory – to store villages

Structure of VillageDirectory list

It has two columns

    • Title – for storing village names
    • ILUCExpArea (internal name)Lookup – lookup to AreaDirectory Title column

Area and Village columns shown below are cascading lookups.

Angular

  1. Activities – for storing activities

 Below is the screenshot of the SharePoint list.

Angular

  1. Village Manager Activities – to store the activities according to the month

Columns

    • Month – It is a choice column
    • Activities – it is lookup to the Title column of Activities list

      Angular
  1. Village Activity Tracker (Internal name of list) – where Village Managers update the status of their monthly tasks

Screenshot of Village Activity Tracker list.

Any update or addition recorded in the CQI tracker will be updated in the below list.

Columns

    • Title – it stores Activity
    • VillageName – it is a lookup to the Tile column of VillageDirectory list
    • VillageActivities – it is lookup to Title column of Activities List
    • Month – it is choice column
    • Year – it is a Single line of text
    • IsActive – it is checkbox Yes/No columns
Angular

In the below screenshot, we can see the CQI Tracker,

The user has to select an Area and Village before marking a check for an activity for the particular month. Any addition or update will be stored in VillageActivitiesTracker List.

Angular

I have performed Create, Read, and Update operations using the below code on SharePoint List.

Add the below code to Content Editor or create a text file and provide the path of the text file in the Content Editor Web Part.

Note

Please change the <site URL> to your respective URL of the site, I am attaching a source code document, download it and upload it in your site.

  1. <script src="/_layouts/MicrosoftAjax.js" type="text/javascript"></script>  
  2. <script src="<site URL>/Style%20Library/CQI Tracker/lib/jquery-1.12.4.min.js" type="text/javascript"></script>  
  3. <script src="<site URL>/Style%20Library/CQI Tracker/lib/bowser.js" type="text/javascript"></script>  
  4. <link href="<site URL>/Style%20Library/CQI Tracker/css/CQIstyle.css" rel="stylesheet" type="text/css"/>  
  5. <script src="<site URL>/Style%20Library/CQI Tracker/lib/angular.min.js" type="text/javascript"></script>  
  6. <script src="<site URL>/Style%20Library/CQI Tracker/lib/angular-route.min.js" type="text/javascript"></script>  
  7. <script src="<site URL>/Style%20Library/CQI Tracker/js/Villageapp.js" type="text/javascript"></script>  
  8. <script src="<site URL>/Style%20Library/CQI Tracker/js/services/baseTrackerSvc.js" type="text/javascript"></script>  
  9. <script src="<site URL>/Style%20Library/CQI Tracker/js/services /Activity.js" type="text/javascript"></script>  
  10. <script src="<site URL>/Style%20Library/CQI Tracker/js/controllers/CQITracker.js" type="text/javascript"></script>  
  11.   
  12. <div data-ng-app="CQITrackerApp">  
  13.     <div data-ng-view ></div>  
  14. </div>  

Data-ng-app element in the above code will call “CQITrackerApp” `from “Villageapp.js” which will in turn call corresponding HTML page.

CQITrackerApp is the main module of this application; ngRoute is used for redirecting the user to the required page without page reloading. NgRoute provides $routeProvider to configure different routes to your application.

Code of “Villageapp.js”

  1. "use strict";  
  2. (function () {  
  3.     angular.module("CQITrackerApp", ["ngRoute"])  
  4.         .config(["$routeProvider"function ($routeProvider) {  
  5.         $routeProvider.when("/", {  
  6.             templateUrl: "<siteURL>/SiteAssets/Content Editor/templates/people/CQITracker.html",  
  7.             controller: "CQITrackerCtrl"  
  8.         });  
  9.     }]);  
  10. })();  

Once “CQITracker.html” is loaded, it will call “CQITracker.js”. In the below code, year is getting populated, Area, Village dropdown is getting populated, and Activities are fetched from VillageActivities List.

In the below code, we are using ng-model for binding values to Village and Area drop-down. We can only have one ng-model on one page; if we have two, then the first one will work by default. We used ng-options="area.ID as area.Title for the area in Areas" which will put the value of drop-down as area.ID and Text as area.Title.

ng-change will invoke the onchange event of DOM when there is any change in Area, Village, or Year drop-down.

ng-init is also used in Village drop down because Village is cascading dropdown with respect to Area; which means that Village drop down will get populated according to area and if we want –select— option in Village dropdown the we have to use ng-init directive and specify <option value="0">--Select--</option>  inside select element of Village dropdown.

data-ng-repeat directive is used for iterating a collection, In our case it the collection of activities.

Below is the code for CQITracker.html.

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">  
  2. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">  
  3.   
  4. <head>  
  5.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
  6.     <meta http-equiv="X-UA-Compatible" content="IE=edge,9,10,chrome=1" />  
  7.     <title>Activity Tracker</title>  
  8.     <!--[if gte mso 9]><xml>  
  9. <mso:CustomDocumentProperties>  
  10. <mso:_dlc_DocId msdt:dt="string">ILUC-5-160</mso:_dlc_DocId>  
  11. <mso:_dlc_DocIdItemGuid msdt:dt="string">8c4b5239-ca70-4487-b267-ed97223e3ef1</mso:_dlc_DocIdItemGuid>  
  12. <mso:_dlc_DocIdUrl msdt:dt="string">http://team.unitingcare.local/sites/ILUC/_layouts/15/DocIdRedir.aspx?ID=ILUC-5-160, ILUC-5-160</mso:_dlc_DocIdUrl>  
  13. </mso:CustomDocumentProperties>  
  14. </xml><![endif]-->  
  15. </head>  
  16.   
  17. <body>  
  18.     <div style="padding:20px"> Area : <select ng-model="ddlArea" ng-options="area.ID as area.Title for area in Areas" ng-change="getVillage()">  
  19.  <option value="">--Select--</option>  
  20. </select> </div>  
  21.     <div style="padding:20px"> Village : <select id="ddlVillage" ng-change="getactivitiesForVillage()" ng-model="ddVillage" ng-init="ddVillage=0">  
  22. <option value="0">--Select--</option>  
  23. <option ng-repeat="village in Villages" value="{{village.ID}}">{{village.Title}}</option>  
  24. </select> </div>  
  25.     <div style="padding:20px;padding-bottom:20px;"> Year : <select name="startYear" ng-model="dates.startYear" id="startYear" ng-options="year.value as year.value for year in Years" ng-change="getactivitiesForVillageFromYear()">  
  26. </select> </div>  
  27.     <table style="width: 100%" class="borderTable" border="1">  
  28.         <tbody>  
  29.             <tr>  
  30.                 <th> </th>  
  31.                 <th>Jan</th>  
  32.                 <th>Feb</th>  
  33.                 <th>March</th>  
  34.                 <th>April</th>  
  35.                 <th>May</th>  
  36.                 <th>June</th>  
  37.                 <th>July</th>  
  38.                 <th>August</th>  
  39.                 <th>September</th>  
  40.                 <th>October</th>  
  41.                 <th>November</th>  
  42.                 <th>December</th>  
  43.                 <th> </th>  
  44.             </tr>  
  45.             <tr data-ng-repeat="activity in Activities">  
  46.                 <td>{{activity.Title}}</td>  
  47.                 <td data-ng-repeat="MonthlyActivity in MonthlyActivities" class="tdAlign">  
  48.                     <div class="ie-article" data-ng-repeat="item in MonthlyActivity.Activities.results" ng-if="item.Title.indexOf(activity.Title)!==-1" id="checkboxes">  
  49.                         <!--<a href="#/FileUpload"></a>--><input type="checkbox" id="{{MonthlyActivity.Month}}#{{activity.Title}}#{{activity.ID}}" ng-click="checkActivity(this)"></input>  
  50.                     </div>  
  51.                 </td>  
  52.                 <td></td>  
  53.             </tr>  
  54.         </tbody>  
  55.     </table>  
  56.     <div id="loading-div-background">  
  57.         <div id="loading-div" class="ui-corner-all"> <img style="height:80px;margin:30px;" src="/_layouts/images/gears_anv4.gif" alt="Loading.." />  
  58.             <h2 style="color:gray;font-weight:normal;">Please wait....</h2>  
  59.         </div>  
  60.     </div>  
  61. </body>  
  62.   
  63. </html>  

Once CQITracker.html is loaded, CQITrackerCtrl, which is our main function for our application, gets invoked by $scope object which, in turn, controls all the variables and functions in the application.

We are using Factory in our application. Factory in angular.js is the function or method that is used by our application only. Ours is CQITrackerService service which is calling all the functions in Village.js, CQITrackerService is passed as an argument to the controller.

  1. "use strict";  
  2. (function() {  
  3.     angular.module("CQITrackerApp")  
  4.         .controller("CQITrackerCtrl", ["$scope""CQITrackerService",  
  5.             function($scope, CQITrackerService) {  
  6.                 $("#loading-div-background").css({  
  7.                     opacity: 0.8  
  8.                 });  
  9.                 $("#loading-div-background").show();  
  10.                 CQITrackerService.getAll()  
  11.                     .then(function(response) {  
  12.                         $scope.Activities = response.d.results;  
  13.   
  14.                         //select default year  
  15.                         $scope.dates = {};  
  16.                         $scope.Years = [{  
  17.                             value: 'Year'  
  18.                         }];  
  19.                         for (var i = 2015; i <= 2025; i++) {  
  20.                             $scope.Years.push({  
  21.                                 value: i  
  22.                             });  
  23.                         }  
  24.                         var d = new Date();  
  25.                         var n = d.getFullYear();  
  26.                         $scope.dates.startYear = n;  
  27.                         //END  
  28.   
  29.                     });  
  30.                 CQITrackerService.getActivityforMonth().then(function(data) {  
  31.                     $scope.MonthlyActivities = data.d.results;  
  32.                 });  
  33.                 CQITrackerService.getArea().then(function(data) {  
  34.   
  35.                     $scope.Areas = data.d.results;  
  36.                     $("#loading-div-background").hide();  
  37.                 });  
  38.                 // onchange event on Area dropdown  
  39.                 $scope.getVillage = function(area) {  
  40.                 // uncheck all check boxes  
  41.                     clearCheckboxes();  
  42.                     var areaId = $scope.ddlArea;  
  43.   
  44.                     CQITrackerService.getVillageList(areaId).then(function(data) {  
  45.                         $scope.Villages = data.d.results;  
  46.                         $scope.ddVillage = 0;  
  47.   
  48.                     });  
  49.   
  50.                 };  
  51.   
  52.                 // Get activities from Village  
  53.                 $scope.getactivitiesForVillage = function() {  
  54.                     // uncheck all check boxes  
  55.                     clearCheckboxes();  
  56.                     var villageId = $("#ddlVillage").find("option:selected").val();  
  57.                     var year = $("#startYear").find("option:selected").text();  
  58.                     CQITrackerService.getVillageActivities(villageId, year).then(function(data) {  
  59.                         for (var i = 0; i < data.d.results.length; i++) {  
  60.   
  61.                             $(document.getElementById(data.d.results[i].Month + '#' + data.d.results[i].VillageActivities.Title + '#' + data.d.results[i].VillageActivities.ID)).prop('checked'true);  
  62.   
  63.                         }  
  64.   
  65.                     });  
  66.                 };  
  67.                   
  68.                  $scope.getactivitiesForVillageFromYear = function() {  
  69.                     // uncheck all check boxes  
  70.                     clearCheckboxes();  
  71.                     var villageId = $("#ddlVillage").find("option:selected").val();  
  72.                     var year = $("#startYear").find("option:selected").text();  
  73.                     CQITrackerService.getVillageActivities(villageId, year).then(function(data) {  
  74.                         for (var i = 0; i < data.d.results.length; i++) {  
  75.   
  76.                             $(document.getElementById(data.d.results[i].Month + '#' + data.d.results[i].VillageActivities.Title + '#' + data.d.results[i].VillageActivities.ID)).prop('checked'true);  
  77.   
  78.                         }  
  79.   
  80.                     });  
  81.                 };  
  82.   
  83.   
  84.                 $scope.checkActivity = function(chkId) {  
  85.   
  86.                     var villageId = parseInt(document.getElementById('ddlVillage').value);  
  87.                     var yearVal = $("#startYear").find("option:selected").text();  
  88.                     var activityId = chkId.$$watchers[0];  
  89.                     var activityTitle = chkId.$$watchers[1];  
  90.                     var activityMonth = chkId.$$watchers[2];  
  91.                     var arrActivity = {  
  92.                         villageId : parseInt(document.getElementById('ddlVillage').value),  
  93.                         activityId : chkId.$$watchers[0],  
  94.                         activityTitle : chkId.$$watchers[1],  
  95.                         yearVal : $("#startYear").find("option:selected").text(),  
  96.                         activityMonth : chkId.$$watchers[2]  
  97.                     };  
  98.                     if ($(document.getElementById(activityMonth.last + '#' + activityTitle.last + '#' + activityId.last)).is(":checked")) {  
  99.                         if ($("#ddlVillage").find("option:selected").text() == "--Select--") {  
  100.                             alert("Select Village");  
  101.                             $(document.getElementById(activityMonth.last + '#' + activityTitle.last + '#' + activityId.last)).prop('checked'false);  
  102.                             return false;  
  103.                         } else {  
  104.                         CQITrackerService.getActivityTrackerId(arrActivity)  
  105.                                 .then(function(data) {  
  106.                                 if(data.d.results.length > 0)  
  107.                                 {  
  108.                                    var itemId=data.d.results[0].ID;  
  109.                                    var flag=true;  
  110.                                    CQITrackerService.update(itemId,flag).then(function(data) {  
  111.                                     alert("Activity Tracker updated");  
  112.                                    });  
  113.                                    }  
  114.                                    else  
  115.                                    {  
  116.                                      CQITrackerService.addActivity(arrActivity)  
  117.                                     .then(function(response) {  
  118.                                     //alert("Activity Tracker added");  
  119.                                     });  
  120.                                    }  
  121.                                      
  122.                                 });  
  123.   
  124.                              
  125.                         }  
  126.                     } else {  
  127.                       
  128.                      CQITrackerService.getActivityTrackerId(arrActivity)  
  129.                                 .then(function(data) {  
  130.                                 if(data.d.results.length > 0)  
  131.                                 {  
  132.                                    var itemId=data.d.results[0].ID;  
  133.                                    var flag=false;  
  134.                                    CQITrackerService.update(itemId,flag).then(function(data) {  
  135.                                     //alert("Activity Tracker updated");  
  136.                                    });  
  137.                                    }  
  138.                                 });  
  139.   
  140.                     }  
  141.                 };  
  142.             }  
  143.         ]);  
  144. })();  
  145.   
  146. function clearCheckboxes() {  
  147.     $('div#checkboxes input[type=checkbox]').each(function() {  
  148.         if ($(this).is(":checked")) {  
  149.             $(this).prop('checked'false);  
  150.         }  
  151.     });  
  152. }  

 “Activity.js” JavaScript file is used for writing Rest queries and calling functions in baseTrackerSvc.js file where all the logic is written.

Code for Activity.js is below; below code has function for uploading files which I am not using in present application

  1. "use strict";  
  2. (function(){  
  3.     angular.module("CQITrackerApp")  
  4.     .factory("CQITrackerService",["CQITrackerSvc",function(baseService){  
  5.         var listEndPoint = '/_api/web/lists';  
  6.         var getAll = function(){  
  7.             var query = listEndPoint + "/GetByTitle('Activities')/Items?$select=ID,Title&$orderby=Title asc";  
  8.             return baseService.getRequest(query);  
  9.         };  
  10.           
  11.         var getActivityforMonth = function(){  
  12.             var query = listEndPoint + "/GetByTitle('Village Manager Activities')/Items?$select=ID,Activities/ID,Activities/Title,Month&$expand=Activities/ID,Activities/Title";  
  13.             return baseService.getRequest(query);  
  14.         };  
  15.           
  16.         var getArea = function(){  
  17.             var query = listEndPoint + "/GetByTitle('AreaDirectory')/Items?$select=ID,Title"  
  18.             return baseService.getRequest(query);  
  19.         };  
  20.           
  21.         var getVillageList = function(AreaId){  
  22.             var query = listEndPoint + "/GetByTitle('Village Directory')/Items?$select=ID,Title,ILUCExpArea/ID,ILUCExpArea/Title&$expand=ILUCExpArea/ID,ILUCExpArea/Title&$filter=ILUCExpArea/ID eq '"+AreaId+"'";  
  23.             return baseService.getRequest(query);  
  24.         };  
  25.           
  26.          var getVillageActivities = function(villageId,year){  
  27.             var query = listEndPoint + "/GetByTitle('VillageActivitiesTracker')/Items?$select=Year,Month,VillageName/ID,VillageName/Title,VillageActivities/Title,VillageActivities/ID,IsActive&$"+  
  28.                                        "expand=VillageName/ID,VillageName/Title,VillageActivities/Title,VillageActivities/ID&$filter=(VillageName/ID eq '"+villageId+"') and (Year eq '"+ year +"' ) and (IsActive ne false)";  
  29.             return baseService.getRequest(query);  
  30.         };  
  31.   
  32.         var addActivity = function(activity){             
  33.             var data = {  
  34.                 __metadata: { 'type''SP.Data.VillageActivitiesTrackerListItem' },           
  35.                 VillageActivitiesId :parseInt(activity.activityId.last),  
  36.                 Month : activity.activityMonth.last,  
  37.                 Year  : activity.yearVal,  
  38.                 Title : activity.activityTitle.last,  
  39.                 VillageNameId : activity.villageId,   
  40.                 IsActive : true           
  41.             };  
  42.             var url = listEndPoint + "/GetByTitle('VillageActivitiesTracker')/Items";  
  43.             return baseService.postRequest(data,url);  
  44.         };  
  45.         var getActivityTrackerId = function(activity){  
  46.             var query = listEndPoint + "/GetByTitle('VillageActivitiesTracker')/Items?$select=ID,VillageName/ID,VillageName/Title,VillageActivities/Title,VillageActivities/ID,Year,Month&$"+  
  47.                                        "expand=VillageName/ID,VillageName/Title,VillageActivities/Title,VillageActivities/ID&$filter=(VillageName/ID eq '"+activity.villageId+"') and (VillageActivities/ID eq '"+parseInt(activity.activityId.last)+"') and (Year eq '"+ activity.yearVal+"' ) and (Month eq '"+activity.activityMonth.last+"')";;  
  48.             return baseService.getRequest(query);  
  49.         };  
  50.         var update = function (itemId,flag){  
  51.             var data = {  
  52.                 __metadata: { 'type''SP.Data.VillageActivitiesTrackerListItem' },               
  53.                 IsActive : flag   
  54.                             };  
  55.             var url = listEndPoint + "/GetByTitle('VillageActivitiesTracker')/GetItemById("+itemId+")";  
  56.             return baseService.updateRequest(data,url);  
  57.         };  
  58.         var remove = function(personId){  
  59.             var url = listEndPoint + "/GetByTitle('People')/GetItemById("+personId+")";  
  60.             return baseService.deleteRequest(url);  
  61.         };  
  62.           
  63.         //get context info  
  64.           
  65.         // uploads multiple files  
  66.           
  67.         var getformDigest = function(proposalSiteUrl)  
  68.         {  
  69.          return baseService.getContextInfo(proposalSiteUrl);  
  70.         };  
  71.   
  72.         var uploadFiles = function(file, proposalSiteUrl,currentDlg)  
  73.         {  
  74.          return baseService.uploadFileCrossSite(file, proposalSiteUrl,currentDlg);  
  75.         };  
  76.         var remove = function(path){  
  77.             var url = listEndPoint + "/GetByTitle('People')/GetItemById("+personId+")";  
  78.             return baseService.deleteRequest(url);  
  79.         };  
  80.   
  81.         return{  
  82.             getAll:getAll,  
  83.             addActivity :addActivity,  
  84.             getActivityTrackerId :getActivityTrackerId,  
  85.             update:update,  
  86.             remove:remove,  
  87.             getActivityforMonth:getActivityforMonth,  
  88.             getArea:getArea,  
  89.             getVillageList :getVillageList,  
  90.             getVillageActivities:getVillageActivities,  
  91.             getformDigest:getformDigest,  
  92.             uploadFiles:uploadFiles  
  93.               
  94.         };  
  95.     }]);  
  96. })();  

Code for baseTrackerSvc.js is below

  1. "use strict";  
  2. (function() {  
  3.     angular.module("CQITrackerApp").factory("CQITrackerSvc", ["$http""$q"function($http, $q) {  
  4.         var baseUrl = _spPageContextInfo.webAbsoluteUrl;  
  5.         var getRequest = function(query) {  
  6.             var deferred = $q.defer();  
  7.             $http({  
  8.                 url: baseUrl + query,  
  9.                 method: "GET",  
  10.                 headers: {  
  11.                     "accept""application/json;odata=verbose",  
  12.                     "content-Type""application/json;odata=verbose"  
  13.                 }  
  14.             }).success(function(result) {  
  15.                 deferred.resolve(result);  
  16.             }).error(function(result, status) {  
  17.                 deferred.reject(status);  
  18.             });  
  19.             return deferred.promise;  
  20.         };  
  21.         var postRequest = function(data, url) {  
  22.             var deferred = $q.defer();  
  23.             $http({  
  24.                 url: baseUrl + url,  
  25.                 method: "POST",  
  26.                 headers: {  
  27.                     "accept""application/json;odata=verbose",  
  28.                     "X-RequestDigest": document.getElementById("__REQUESTDIGEST").value,  
  29.                     "content-Type""application/json;odata=verbose"  
  30.                 },  
  31.                 data: JSON.stringify(data)  
  32.             }).success(function(result) {  
  33.                 deferred.resolve(result);  
  34.             }).error(function(result, status) {  
  35.                 deferred.reject(status);  
  36.             });  
  37.             return deferred.promise;  
  38.         };  
  39.         var updateRequest = function(data, url) {  
  40.             var deferred = $q.defer();  
  41.             $http({  
  42.                 url: baseUrl + url,  
  43.                 method: "PATCH",  
  44.                 headers: {  
  45.                     "accept""application/json;odata=verbose",  
  46.                     "X-RequestDigest": document.getElementById("__REQUESTDIGEST").value,  
  47.                     "content-Type""application/json;odata=verbose",  
  48.                     "X-Http-Method""PATCH",  
  49.                     "If-Match""*"  
  50.                 },  
  51.                 data: JSON.stringify(data)  
  52.             }).success(function(result) {  
  53.                 deferred.resolve(result);  
  54.             }).error(function(result, status) {  
  55.                 deferred.reject(status);  
  56.             });  
  57.             return deferred.promise;  
  58.         };  
  59.         var deleteRequest = function(url) {  
  60.             var deferred = $q.defer();  
  61.             $http({  
  62.                 url: baseUrl + url,  
  63.                 method: "DELETE",  
  64.                 headers: {  
  65.                     "accept""application/json;odata=verbose",  
  66.                     "X-RequestDigest": document.getElementById("__REQUESTDIGEST").value,  
  67.                     "IF-MATCH""*"  
  68.                 }  
  69.             }).success(function(result) {  
  70.                 deferred.resolve(result);  
  71.             }).error(function(result, status) {  
  72.                 deferred.reject(status);  
  73.             });  
  74.             return deferred.promise;  
  75.         };  
  76.         //get context info  
  77.         var getContextInfo = function(webUrl) {  
  78.             var deferred = $q.defer();  
  79.             var url = webUrl + "/_api/contextinfo";  
  80.             $.ajax({  
  81.                 url: url,  
  82.                 method: "POST",  
  83.                 headers: {  
  84.                     "accept""application/json;odata=verbose",  
  85.                     "Content-Type""application/json;odata=verbose"  
  86.                 }  
  87.             }).success(function(result) {  
  88.                 deferred.resolve(result);  
  89.             }).error(function(status) {  
  90.                 deferred.reject(status);  
  91.             });  
  92.             return deferred.promise;  
  93.         };  
  94.         var uploadFileCrossSite = function(file, webUrl, currentDlg) {  
  95.             var deferred = $q.defer();  
  96.             var url = webUrl + "/_api/contextinfo";  
  97.             $.ajax({  
  98.                 url: url,  
  99.                 type: "POST",  
  100.                 headers: {  
  101.                     "Accept""application/json; odata=verbose"  
  102.                 },  
  103.                 contentType: "application/json;odata=verbose",  
  104.                 success: function(data) {  
  105.                     var digest = data.d.GetContextWebInformation.FormDigestValue;  
  106.                     var libraryName = "Documents";  
  107.                     var reader = new FileReader();  
  108.                     var arrayBuffer;  
  109.                     reader.onload = function(e) {  
  110.                         arrayBuffer = reader.result;  
  111.                         url = webUrl + "/_api/web/lists/getByTitle(@TargetLibrary)/RootFolder/files/add(url=@TargetFileName,overwrite='true')?" + "@TargetLibrary='" + libraryName + "'" + "&@TargetFileName='" + file.name + "'";  
  112.                         $.ajax({  
  113.                             url: url,  
  114.                             type: "POST",  
  115.                             data: arrayBuffer,  
  116.                             headers: {  
  117.                                 "Accept""application/json; odata=verbose",  
  118.                                 "X-RequestDigest": digest  
  119.                             },  
  120.                             contentType: "application/json;odata=verbose",  
  121.                             processData: false,  
  122.                             success: function(status) {  
  123.                                 deferred.resolve(status);  
  124.                             },  
  125.                             error: function() {  
  126.                                 $('#lblResult').text("Error uploading file to different site.");  
  127.                                 if (currentDlg != null) {  
  128.                                     currentDlg.close();  
  129.                                 }  
  130.                                 deferred.reject(status);  
  131.                             }  
  132.                         });  
  133.                     };  
  134.                     reader.readAsArrayBuffer(file);  
  135.                 },  
  136.                 error: function() {  
  137.                     $('#lblResult').text("Error accessing other site.");  
  138.                     if (currentDlg != null) {  
  139.                         currentDlg.close();  
  140.                     }  
  141.                 }  
  142.             });  
  143.             return deferred.promise;  
  144.         };  
  145.         return {  
  146.             getRequest: getRequest,  
  147.             postRequest: postRequest,  
  148.             updateRequest: updateRequest,  
  149.             deleteRequest: deleteRequest,  
  150.             getContextInfo: getContextInfo,  
  151.             uploadFileCrossSite: uploadFileCrossSite  
  152.         };  
  153.     }]);  
  154. })();   
Note

This application is tested on IE 8,9,10 and Chrome.