Wow.. I thought i won't be able to manage to write a blog this month.
Hey guys today i am here with a something of that i have been planning since a long time. Some time back i tried to implement Angularjs with SharePoint. To be frank Angularjs is just awesome. You can have flawless feel while working with it. I don't know how will i manage to work with Angularjs in huge projects but i found it really really good to work with single page applications.
Let's have a look to the classic definition of Angularjs that you find when you google for Angularjs.
Angularjs
"AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template language and lets you extend HTML's syntax to express your application's components clearly and succinctly. Angular's data binding and dependency injection eliminate much of the code you would otherwise have to write."
SharePoint Apps
SharePoint Apps, I personally found it more developer friendly as well as with minimum configuration to deploy compared to Provider hosted apps. Lets have a look at the definition that Microsoft provides for SharePoint.
SharePoint-hosted add-ins are one of the two major types of SharePoint Add-ins. For an overview of SharePoint Add-ins and the two different types, see SharePoint Add-ins. Here's a summary of SharePoint-hosted add-ins:
They contain SharePoint lists, Web Parts, workflows, custom pages, and other components, all of which are installed on a sub web, called the add-in web, of the SharePoint website where the add-in is installed.
The only code they have is JavaScript on custom SharePoint pages.
Let's start with a small exercise wherein will create a simple CRUD operation for a product. Product list will have 4 columns
1. ID
2. Name
3. Cost
4. Description.
Lets start with a list of steps that we need to follow. Before we start lets have a look at prerequisites.
In order to create a SharePoint Hosted apps you will need a link to Developer's Template site. Site can be either on office365 or on premise. Your visual studio will need SharePoint/Office Project templates installed.
Angularjs with SharePoint
<div>
<table style="width:100%; margin-top:50px;" border="1">
<tbody>
<tr>
<th>Name</th>
<th>Cost</th>
<th>Description</th>
<th>Action</th>
</tr>
<tr data-ng-repeat="p in product">
<td>{{p.Name}}</td>
<td>{{p.Cost}}</td>
<td>{{p.Description}}</td>
<td>
<a href="#/edit/{{p.ID}}"><img style="height:20px; width:20px;" src="/sites/spin/rnd/SiteAssets/AngularJsDemo/images/edit.png" alt=""></a>
<a href="" data-ng-click="removeProduct(p)"><img style="height:20px; width:20px;" src="/sites/spin/rnd/SiteAssets/AngularJsDemo/images/delete.png" alt=""></a>
</td>
</tr>
</tbody>
</table>
</div>
edit.html
<h2>Edit Product</h2>
<hr>
<table>
<tr>
<td>Product Name</td>
<td>
<input type="text" data-ng-model="product.productName">
</td>
</tr>
<tr>
<td>Cost</td>
<td>
<input type="text" data-ng-model="product.cost">
</td>
</tr>
<tr>
<td>Description</td>
<td>
<textarea data-ng-model="product.description"></textarea>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="button" value="Save" data-ng-click="updateProduct(product)">
</td>
</tr>
</table>
add.html
<h2>Add New Product</h2>
<hr>
<table>
<tr>
<td>Product Name</td>
<td>
<input type="text" data-ng-model="product.name">
</td>
</tr>
<tr>
<td>Cost</td>
<td>
<input type="text" data-ng-model="product.cost">
</td>
</tr>
<tr>
<td>Description</td>
<td>
<textarea data-ng-model="product.description"></textarea>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="button" value="Save" data-ng-click="addProduct(product)">
</td>
</tr>
</table>
all.js
"use strict";
(function () {
angular.module("productApp")
.controller("addProductCtrl", ["$scope", "productService","$location", function ($scope, productService,$location) {
$scope.addProduct = function (product) {
productService.addNew(product)
.then(function(response){
console.log(response);
$location.path("/");
});
};
}]);
})();
edit.js
"use strict";
(function () {
angular.module("productApp")
.controller("editProductCtrl", ["$scope", "productService", "$routeParams","$location",
function ($scope, productService, $routeParams,$location) {
productService.getById($routeParams.productId).then(function (response) {
$scope.product = {
productId : response.d.ID,
name : response.d.Name,
cost : response.d.Cost,
description : response.d.Description
};
$scope.editProduct = function(product){
productService.update(product)
.then(function(response){
$location.path("/");
});
};
});
}]);
})();
add.js
"use strict";
(function () {
angular.module("productApp")
.controller("allProductsCtrl", ["$scope", "productService",
function ($scope, productService) {
productService.getAll()
.then(function (response) {
$scope.product = response.d.results;
});
$scope.removeProduct = function(p){
productService.remove(p.ID)
.then(function(response){
var productIndex = $scope.product.indexOf(p);
$scope.product.splice(productIndex,1);
});
};
}]);
})();
Now to add the solution to your page you just need to get the url of product-main.txt and place it into Content Editor webpart properties and that's it.
Angularjs in SharePoint hosted Apps
Below are steps that you need to follow in order to create SharePoint hosted apps.
Hey guys today i am here with a something of that i have been planning since a long time. Some time back i tried to implement Angularjs with SharePoint. To be frank Angularjs is just awesome. You can have flawless feel while working with it. I don't know how will i manage to work with Angularjs in huge projects but i found it really really good to work with single page applications.
Let's have a look to the classic definition of Angularjs that you find when you google for Angularjs.
Angularjs
"AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template language and lets you extend HTML's syntax to express your application's components clearly and succinctly. Angular's data binding and dependency injection eliminate much of the code you would otherwise have to write."
SharePoint Apps
SharePoint Apps, I personally found it more developer friendly as well as with minimum configuration to deploy compared to Provider hosted apps. Lets have a look at the definition that Microsoft provides for SharePoint.
SharePoint-hosted add-ins are one of the two major types of SharePoint Add-ins. For an overview of SharePoint Add-ins and the two different types, see SharePoint Add-ins. Here's a summary of SharePoint-hosted add-ins:
They contain SharePoint lists, Web Parts, workflows, custom pages, and other components, all of which are installed on a sub web, called the add-in web, of the SharePoint website where the add-in is installed.
The only code they have is JavaScript on custom SharePoint pages.
Let's start with a small exercise wherein will create a simple CRUD operation for a product. Product list will have 4 columns
1. ID
2. Name
3. Cost
4. Description.
Lets start with a list of steps that we need to follow. Before we start lets have a look at prerequisites.
In order to create a SharePoint Hosted apps you will need a link to Developer's Template site. Site can be either on office365 or on premise. Your visual studio will need SharePoint/Office Project templates installed.
Angularjs with SharePoint
- Create a SharePoint List with above fields.
- Create a folder in SiteAssets library of SharePoint site and create a folder named AngularJsDemo.
- Add a text file to it named product-main
- What we do over here is we place all the Angularjs references in this particular file.
<link href="SitecollectionSiteAssets/AngularJsDemo/css/style.css" rel="stylesheet" type="text/css">
<script src="SitecollectionSiteAssets/AngularJsDemo/Library/angular.min.js" type="text/javascript"></script>
<script src="SitecollectionSiteAssets/AngularJsDemo/Library/angular-route.min.js" type="text/javascript"></script>
<script src="SitecollectionSiteAssets/AngularJsDemo/js/app.js" type="text/javascript"></script>
<script src="SitecollectionSiteAssets/AngularJsDemo/js/Services/baseSvc.js" type="text/javascript"></script>
<script src="SitecollectionSiteAssets/AngularJsDemo/js/Services/Products/product.js" type="text/javascript"></script>
<script src="SitecollectionSiteAssets/AngularJsDemo/js/Controllers/Product/all.js" type="text/javascript"></script>
<script src="SitecollectionSiteAssets/AngularJsDemo/js/Controllers/Product/add.js" type="text/javascript"></script>
<script src="SitecollectionSiteAssets/AngularJsDemo/js/Controllers/Product/edit.js" type="text/javascript"></script>
<div data-ng-app="productApp">
<div data-ng-view class="product-app"></div>
</div>
Also it includes required css files and references to the files where in we will right actual code.
Css contains all the css files.
HTML Templates contains all the template files that will be rendered on screen.
Images contains all required images.
js contains Controller and Services folder wherein Controller contains all the controllers (Controllers are the actual back end of the that communicates with HTML Templates).
Services contains a file that contains a file that prepares data that we will send/receive data from/to server and will pass it to controller. Basesvc.js is the actual file that will communicate with SharePoint.
app.js acts as an route, Based on the url it renders HTML template in the div.
Below is the code that we need to write in each page.
all.html
<a href="#/add" class="add-new-button">Add New Product</a><div>
<table style="width:100%; margin-top:50px;" border="1">
<tbody>
<tr>
<th>Name</th>
<th>Cost</th>
<th>Description</th>
<th>Action</th>
</tr>
<tr data-ng-repeat="p in product">
<td>{{p.Name}}</td>
<td>{{p.Cost}}</td>
<td>{{p.Description}}</td>
<td>
<a href="#/edit/{{p.ID}}"><img style="height:20px; width:20px;" src="/sites/spin/rnd/SiteAssets/AngularJsDemo/images/edit.png" alt=""></a>
<a href="" data-ng-click="removeProduct(p)"><img style="height:20px; width:20px;" src="/sites/spin/rnd/SiteAssets/AngularJsDemo/images/delete.png" alt=""></a>
</td>
</tr>
</tbody>
</table>
</div>
edit.html
<h2>Edit Product</h2>
<hr>
<table>
<tr>
<td>Product Name</td>
<td>
<input type="text" data-ng-model="product.productName">
</td>
</tr>
<tr>
<td>Cost</td>
<td>
<input type="text" data-ng-model="product.cost">
</td>
</tr>
<tr>
<td>Description</td>
<td>
<textarea data-ng-model="product.description"></textarea>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="button" value="Save" data-ng-click="updateProduct(product)">
</td>
</tr>
</table>
add.html
<h2>Add New Product</h2>
<hr>
<table>
<tr>
<td>Product Name</td>
<td>
<input type="text" data-ng-model="product.name">
</td>
</tr>
<tr>
<td>Cost</td>
<td>
<input type="text" data-ng-model="product.cost">
</td>
</tr>
<tr>
<td>Description</td>
<td>
<textarea data-ng-model="product.description"></textarea>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="button" value="Save" data-ng-click="addProduct(product)">
</td>
</tr>
</table>
all.js
"use strict";
(function () {
angular.module("productApp")
.controller("addProductCtrl", ["$scope", "productService","$location", function ($scope, productService,$location) {
$scope.addProduct = function (product) {
productService.addNew(product)
.then(function(response){
console.log(response);
$location.path("/");
});
};
}]);
})();
edit.js
"use strict";
(function () {
angular.module("productApp")
.controller("editProductCtrl", ["$scope", "productService", "$routeParams","$location",
function ($scope, productService, $routeParams,$location) {
productService.getById($routeParams.productId).then(function (response) {
$scope.product = {
productId : response.d.ID,
name : response.d.Name,
cost : response.d.Cost,
description : response.d.Description
};
$scope.editProduct = function(product){
productService.update(product)
.then(function(response){
$location.path("/");
});
};
});
}]);
})();
add.js
"use strict";
(function () {
angular.module("productApp")
.controller("allProductsCtrl", ["$scope", "productService",
function ($scope, productService) {
productService.getAll()
.then(function (response) {
$scope.product = response.d.results;
});
$scope.removeProduct = function(p){
productService.remove(p.ID)
.then(function(response){
var productIndex = $scope.product.indexOf(p);
$scope.product.splice(productIndex,1);
});
};
}]);
})();
app.js
"use strict";
(function () {
angular.module("productApp", ["ngRoute"])
.config(["$routeProvider", function ($routeProvider) {
$routeProvider.when("/", {
templateUrl: "/sites/spin/rnd/SiteAssets/AngularJsDemo/HtmlTemplates/all.html",
controller: "allProductsCtrl"
}).when("/add", {
templateUrl: "/sites/spin/rnd/SiteAssets/AngularJsDemo/HtmlTemplates/add.html",
controller: "addProductCtrl"
}).when("/edit/:productId", {
templateUrl: "/sites/spin/rnd/SiteAssets/AngularJsDemo/HtmlTemplates/edit.html",
controller: "editProductCtrl"
});
}]);
})();
You need to register your different parts of code under a single module name. And also need to tell angularjs compiler about the the different controllers and views. So that the reason we place all the code in angular.module("productApp"... where productApp is the name of the div that we have added in produce-App.txt file. Also it acts as an name of module. You also need to register all the modules that you use in your project like ui-grid, any kind of animation etc. When ever you try to include any such thing into your project you need to register that particular module so that angularjs can easily identify it.
basesvc.js
"use strict";
(function () {
angular.module("productApp")
.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
};
}]);
})();
product.js
"use strict";
(function(){
angular.module("productApp")
.factory("productService",["baseSvc",function(baseService){
var listEndPoint = '/_api/web/lists';
var getAll = function(){
var query = listEndPoint + "/GetByTitle('Products')/Items?$select=ID,Name,Cost,Description";
return baseService.getRequest(query);
};
var addNew = function(product){
var data = {
__metadata: { 'type': 'SP.Data.ProductsListItem' },
Name : product.name,
Cost : product.cost,
Description: product.description
};
var url = listEndPoint + "/GetByTitle('Products')/Items";
return baseService.postRequest(data,url);
};
var getById = function(productId){
var query = listEndPoint + "/GetByTitle('Products')/GetItemById("+productId+")?$select=ID,Name,Cost,Description";
return baseService.getRequest(query);
};
var update = function (product){
var data = {
__metadata: { 'type': 'SP.Data.ProductsListItem' },
Name : product.name,
Cost : product.cost,
Description : product.description
};
var url = listEndPoint + "/GetByTitle('Products')/GetItemById("+product.productId+")";
return baseService.updateRequest(data,url);
};
var remove = function(productId){
var url = listEndPoint + "/GetByTitle('Products')/GetItemById("+productId+")";
return baseService.deleteRequest(url);
};
return{
getAll:getAll,
addNew:addNew,
getById:getById,
update:update,
remove:remove
};
}]);
})();
Now to add the solution to your page you just need to get the url of product-main.txt and place it into Content Editor webpart properties and that's it.
Angularjs in SharePoint hosted Apps
Below are steps that you need to follow in order to create SharePoint hosted apps.
- Add new project to visual studio with below project template.
Now all all the HTML Template files that we created above to the Templates module. To do that right click on templates module and select Add Existing item. Select all the files that you want to add.
Now open Element.xml file and check whether it has entries for all the uploaded files of not.
- Upload all images to images folder.
- css to Content folder.
- Add all the js files to Scripts folder, try to keep the folder structure same.
Now its time to replace paths of each file that we have created so lets start with that.
Copy entire code of product-main.txt and paste it in Default.aspx.
Delete all the References that we added and drag and drop them from respective folders that you see in solution explorer.
You will also need to modify path's in app.js. Below is the code for it.
"use strict";
(function () {
angular.module("productApp", ["ngRoute"])
.config(["$routeProvider", function ($routeProvider) {
$routeProvider.when("/", {
templateUrl: "../templates/all.html",
controller: "allProductsCtrl"
}).when("/add", {
templateUrl: "../templates/add.html",
controller: "addProductCtrl"
}).when("/edit/:productId", {
templateUrl: "../templates/edit.html",
controller: "editProductCtrl"
});
}]);
})();
Basically we are just replacing path to html pages.
Another important change that we need to do is in basesvc.js file. We just need to get the SPAppWebUrl instead of _spPageContextInfo.webAbsoluteUrl. You can find SPAppWebUrl in the query string as parameter. Make sure you trim url after #/ because that is appended by angularjs.
Below is the code of basesvc.js file now.
"use strict";
(function () {
angular.module("productApp")
.factory("baseSvc", ["$http", "$q", function ($http, $q) {
var temp = decodeURIComponent(manageQueryStringParameter('SPAppWebUrl'));
var baseUrl = temp.substr(0, temp.indexOf('#'));
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
};
}]);
})();
function manageQueryStringParameter(paramToRetrieve) {
var params =
document.URL.split("?")[1].split("&");
var strParams = "";
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split("=");
if (singleParam[0] == paramToRetrieve) {
return singleParam[1];
}
}
}
Now you will need to add list to SharePoint app. Right click on project select Add new Item > SharePoint List and add required fields to the same. This will re create list each time SharePoint app is deployed.
That's it now just right click project and hit deploy. You will find the App deployed to the dev site.
Your app will be in App in Testing library of the dev site. You can click on the app from site contents.
Make sure when ever you need to re-deploy your app you change the version of app from AppManifest.xml. This will minimize chances of error in deployment.
Regards,
Keyur Pandya
Can you provide a complete code package zipped up for AngularJS with SharePoint? or at least files like images, style.css etc. you are referring therein?
ReplyDeleteCan you, please, clarify the following step?
ReplyDelete"Copy entire code of product-main.txt and paste it in Default.aspx"
Replace all content of default.aspx?
This is the only simplest article I found to learn how to use AngularJS 1 with SharePoint online. Please provide the style.css
ReplyDelete@Emt Thanks
DeleteQuesto tipo di aspetto sembra assolutamente migliore. Queste minuscole informazioni vengono prodotte in aggiunta alla massiccia comprensione del passato storico. Mi piace molto questo genere. app per bloccare le chiamate
ReplyDelete@Kevin Horgan Thanks for your comments.
Delete