Questo è un esempio di come può essere usato AngularJS 1.0 per costruire una app all'interno di una pagina SharePoint. L'esempio visualizza all'interno di alcuni box, una serie di link presi da una lista SharePoint:app linksla lista da cui vengono presi i links è questa:lista linksPer realizzare questa app serve:
una lista SharePoint chiamata SgartItLinks con i seguenti campi: Title (testo), ImageSrc (testo), OpenNewWindow (yes/no), Link (testo) e Category (choice)
una document library chiamata HtmlJs un cui mettere il codice JavaScript e html
//vedi anche http://www.sgart.it/IT/informatica/angularjs-come-funziona/post
/*
Usa la lista SharePoint .../Lists/SgartItLinks
con i seguenti campi:
- Title (string) required
- Link (string) required
- Category (chioce) required
- ImageSrc (string)
- OpenNewWindow (bool)
*/
(function () { // richiudo tutto in una enclosure per evitare conflitti di nomi
"use strict";
//costanti globali
var PARAMETERS = {
siteUrl: '', // es. '/sites/s1'
listName: 'SgartItLinks', //nome dalla lista
fields: {
title: 'Title',
link: 'Link',
category: 'CategoryValue', // suffisso Value aggiunto da SharePoint
imageSrc: 'ImageSrc',
openNewWindow: 'OpenNewWindow'
}
};
//dichiarazione app / inizializzo angular
var app = angular.module('sgartItLinks', []);
//*********************************************
// controller usato dalla view
// creo un CONTROLLER
// la variabile $scope viene "iniettata"" (inject) da angular
// la variabile linkFactory viene "iniettata"" (inject) da angular
app.controller('LinksCtrl', function($scope, $rootScope, linkFactory) {
var self = this;
// dichiaro le variabili e le aggancio allo scope da usare nella view
// conterrà tutti i link letti
self.items = [{ title: '', link: '', imageSrc: '', openNew: true, category: '' }];
// conterrà l'elenco delle categorie letto dal campo choice
self.categoryAll = [];
// la categoria selezionata
self.category = "";
//dichiaro le funzioni usate nella view
self.doSearch = function (id) { // esegue il seach sui link in base alla categoria
self.items = [];
if (typeof id === "undefined")
id = self.category;
else
self.category = id;
linkFactory.getLinks(id).then(function (result) {
self.items = result;
});
};
self.isEmpty = function () {
if($rootScope.isUpdating()) return false;
return self.items === null || self.items.length === 0;
};
self.getimageSrc = function (item) {
return item.imageSrc;
if (self.isVisibleImage())
return item.imageSrc;
else
return null;
};
self.isVisibleImage = function (item) {
if (typeof item === "undefined") return false;
if (typeof item.imageSrc === "undefined") return false;
return !(item.imageSrc === null || item.imageSrc === "");
};
self.isCurrentCategory = function (item) {
return item.i === self.category;
};
self.getTarget = function (item) {
if (item.openNew === true)
return "_blank";
else
return "";
};
// inizializzo il controller
function init() {
self.categoryAll = [];
self.category = "";
self.items = [];
linkFactory.getCategories().then(function (result) {
self.categoryAll = result;
self.doSearch();
});
};
init();
});
//*********************************************
// factory per l'accesso ai dati tramite il client object model di SharePoint
// inietto il factory $http per le chiamate ajax
// inietto stateFactory per gestire lo stato di update/loading
app.factory("linkFactory", function($http, stateFactory) {
//variabili globali
var dbApiBase = PARAMETERS.siteUrl;
// le funzioni esposte dal factory
return {
'getLinks': _getLinks,
'getCategories': _getCategories
}
function _getCategories() {
stateFactory.updating();
return $http.get(dbApiBase + "/_vti_bin/listdata.svc/" + PARAMETERS.listName + "()?$orderby=" + PARAMETERS.fields.category, {
cache: false,
params: {
t: new Date().getTime() //per evitare il caching su IE
}
}).then(successCallbackCategory, errorCallback);
};
function successCallbackCategory(response) {
var result = [{ i: '', d: 'All' }];
var items = response.data.d.results;
var prev = {};
//leggo le category associate ai link facendo un distinct
for (var i = 0; i < items.length; i++) {
var cat = items[i][PARAMETERS.fields.category];
if (typeof prev[cat] === 'undefined') {
result.push({ i: cat, d: cat });
prev[cat] = 1;
}
}
stateFactory.updated();
return result;
};
function _getLinks(category) {
stateFactory.updating();
var filter = "";
if (!(typeof category === 'undefined' || category === null || category === ''))
filter = "&$filter=" + PARAMETERS.fields.category + " eq '" + category + "'";
return $http.get(dbApiBase + "/_vti_bin/listdata.svc/" + PARAMETERS.listName + "()?$orderby=" + PARAMETERS.fields.title + filter, {
cache: false,
params: {
t: new Date().getTime() //per evitare il caching su IE
}
}).then(successCallbackLinks, errorCallback);
};
//{title:'', link:'', imageSrc:'', openNew:true, category:''}
function successCallbackLinks(response) {
var result = [];
var items = response.data.d.results;
for (var i = 0; i < items.length; i++) {
var item = items[i];
result.push({
title: item[PARAMETERS.fields.title],
link: item[PARAMETERS.fields.link],
imageSrc: item[PARAMETERS.fields.imageSrc],
openNew: item[PARAMETERS.fields.openNewWindow],
category: item[PARAMETERS.fields.category]
});
}
stateFactory.updated();
return result;
};
//gestione errori comune
function errorCallback(response) {
stateFactory.updated();
alert(response);
};
});
//*********************
// loading service per la gestione del feedback visuale all'utente durante le chiamate ajax
app.factory("stateFactory", function($rootScope) {
var updatingCount = 0;
//aggancio la funzione al root scope
//per averla sempre disponibile
$rootScope.isUpdating=function(){
return updatingCount !=0;
};
return {
'updating': _updating,
'updated': _updated,
'reset': _reset,
'isUpdating': $rootScope.isUpdating
};
function _updating(){
updatingCount++;
}
function _updated(){
if(updatingCount>0)
updatingCount--;
else if(updatingCount<0)
_reset();
}
function _reset(){
updatingCount=0;
}
});
})();
Possiamo individuare 3 blocchi principali con cui è costruita questa app AngularJS:
dichiarazione della app e inizializzazione di AngularJS ( angular.module()
definizione del controller (app.controller - LinksCtrl)
definizione del factory per l'accesso ai dati ( app.factory - linkFactory)
Attenzione se deve essere usato all'interno di SharePoint 2010 è necessario modificare il meta tag X-UA-Compatible da IE=8 a IE=9 o meglio ancora IE=edge
L'app può facilmente essere riadattata per girare in un abiente diverso da SharePoint, ad esempio un sito ASP.NETMVC che usa un DB per persistere l'elenco dei links. In questo caso bisognerà solo modificare il factory per gestire le nuove API Json esposte da MVC in modo che i metodi del factory ritornino sempre gli stessi oggetti.