Archive for November 5, 2013

MVC using Angular and Promises in SharePoint 2013

Lately I’ve been reading all kinds of cool articles about Angular JS. One of them written by Jeremy Thake: http://www.jeremythake.com/2013/10/sharepoint-hosted-app-with-angularjs-for-mvc-javascript/. This was an excellent starting point for me to see what Angular has to offer. I noticed two things:

  1. Scoping in Angular JS is pretty tricky. Jeremy’s example was written in the global namespace and I wanted to add some best practices.
  2. Combine the scoping difficulty with multiple Asynchronous calls from your JavaScript code and it’s easy to get lost. Promises to the Rescue!

Namespaces in JavaScript

A very common issue in coding JavaScript is conflicts between 2 variables with the same name in the global namespace. If you code in multiple JS files it’s possible that var i for instance is used more then once. This can easily be avoided by using Namespaces like this:

var AP = window.AP || {};

This statement loads AP from the global namespace if it already exists and otherwise creates a new namespace AP. The functions or vars created in that namespace will never conflict with same function- or varnames in a different namespace. #winning!

AP.WebPropQuery = function() { var i = 0; };
AP.ContactsQuery = function() { var i = 1; };

This makes the code maintainable and readable while the var i now is always the right value in the right context. If you want to know more this is an excellent read:

http://www.kenneth-truyers.net/2013/04/27/javascript-namespaces-and-modules/

Angular JS

My goal here was to use the namespacing practice with the Angular is written. Angular also has a way of scoping everything and I found challenging to combine it with this approach of Namespaces.

With a directive in the HTML code “ng-app” we specify the scope of this Angular App. Using this code I register it as a Module so I also can use a Service later on.

AP.Promise = angular.module('AP.Promise', []);

My goal in this example is to load a Property from the Web’s Propertybag. This property contains the name of a Contacts list. I want all the contacts to be rendered with Full Name and Email address.

Controllers

A controller in the MVC pattern handles a certain part of the View. In our case a list of Contacts. I used the directive ng-controller=”ContactController” and matching JS code

/*
* Angular Controller Registration
* Dependencies: $scope and the $ProfileService
*/
AP.Promise.controller('ContactController', ['$scope', '$ContactService', function ($scope, $ContactService) {
   $scope.contacts = [];
   $ContactService.getContacts($scope);
}]);

When adding this Controller to the Module, I pass along 2 dependencies. $scope (which is kind of mandatory otherwise the controller would be useless) and $ContactService. In this service the logic is stored for getting the data out of SharePoint. The contents of the service

/*
* Angular Service Registration to Support SharePoint interaction
*/
AP.Promise.service('$ContactService', function () {
//Service Method to Get the Contacts
  this.getContacts = function ($scope) {
     //Logic
  };
});

This is the base of our Angular code. Now we need some JS to get the data out of SharePoint. In this example I’m using the REST API.

REST calls and Promises

One library that makes our lives easier when using REST API is jQuery. You might have noticed that the $ char is very popular and is used by lots of JavaScript libraries. We have to be carefull not to call into the wrong library here.

At the start of this post I created two functions in the AP namespace which we are going to fill. One for the Call to get the Web Property and one for the Call to get the Contacts.

/*
* Query object to retrieve Property from Propertybag
* Return: Promise object that is filled with data after ASync call is returned
*/
AP.WebPropQuery = function ($) {
   var deferred = $.Deferred();
   var execute = function () {
      $.ajax({
         url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/AllProperties?$select=p42contacts",
         method: "GET",
         headers: { "accept": "application/json;odata=verbose" },
         success: function (data) {
            deferred.resolve(data);
         },
         error: function (err) {
            deferred.reject(err);
         }
      });
      return deferred;
   };
   return {
      execute: execute
   }
}(jQuery);

This is what the first function looks like. Now the important part is the Promise var deferred = $.Deferred(); This var is called a promise and is kind of like an object placeholder which you can work with untill the Async call replaces the object with the final result of the call. Using these promises you don’t have to nest the second call in the success function of the $.ajax call. This increase readability and maintainability of your code. Scott Hillier wrote a great piece on Promises and how to use them:
https://curah.microsoft.com/11711/using-promises-patterns-in-sharepoint-2013-apps-with-javascript-and-jquery

Making the function calls

The final missing piece in this is making the function calls from the logic in the Angular Service. Again the code of the service with the missing logic

/*
* Angular Service Registration to Support SharePoint interaction
*/
AP.Promise.service('$ContactService', function () {
   //Service Method to Get the Contacts
   this.getContacts = function ($scope) {
      //First Async Call
      AP.WebPropQuery.execute().promise().then(function (data) {
         //Second Async Call depending on data from 1st call
         AP.ContactsQuery.execute(data.d.p42contacts).promise().then(function (data) {
            //Data to work with
            var results = data.d.results;
            for (var i = 0; i < results.length; i++) {
               $scope.contacts.push({ fullname: results[i].FullName, email: results[i].Email });
            }
            //Apply scope to see results
            $scope.$apply();
         }, function (err) { });
      }, function (err) { });
   };
});

AP.WebPropQuery.execute() is the regular function call. With .promise().then() it now is possible to add a second function call based on the promise that is returned in the first. That second call also returns a promise and with that data we can populate our Model.

The View

The View to match all this JS code is a very plain and simpel piece of HTML.

<script type="text/javascript" src="/Style Library/Scripts/p42.contacts.js"></script>
<div ng-app="AP.Promise">
   <div ng-controller="ContactController">
      <ul>
         <li ng-repeat="contact in contacts">{{contact.fullname}} - {{contact.email}}</li>
      </ul>
   </div>
</div>

Results

The thing I really like about this combination of Promises and Angular is that your JavaScript has very clear build up. It’s very readable and maintainable and on top of that it’s very fast! This way we don’t have to wait on all the SharePoint JS files to be loaded as well since we use the REST API. Very powerfull stuff!

You can download a Visual Studio 2012 Solution here. This solution contains a Sandboxed Solution (still fine for provisioning stuff!) with a default Script Editor WebPart that holds the View HTML with the Angular directives.

Todo yourself:

– Create a Contact List and make sure the Full Name and Email are filled
– Create a Property using SP Designer called “p42contacts” in the rootweb and add the name of the Contact List as value.

Upgrading SharePoint 2010 JS

Since a lot of Customers are upgrading their Office 365 Tenant to SharePoint 2013, I noticed one specific change in how SharePoint loads it’s JavaScript that can break down some of your code. Fortunately it’s very easy to fix and the best part is that this solution was already available in SharePoint 2010. So upgrading your scripts is highly recommended even if you’re still running SharePoint 2010.

SP.js

In SharePoint 2010 the SP.SOD (Script On Demand) was introduced. This library came with a very handy function to Execute some of your custom script after a specific registered script was loaded by the SOD.

To make use of the JavaScript Object Model we have to make sure that SharePoint has loaded sp.js first. Therefor a common line of script is used by lots of developers:

SP.SOD.executeOrDelayUntilScriptLoaded(MyFunction, ‘sp.js’);

When sp.js is loaded, the SOD is notified and MyFunction is executed. In SharePoint 2010 this works in 99% of the times it is called because SharePoint depends on the sp.js file in most of its UI, so you assume the file is always loaded. I say most of the times because sometimes it can be a choice not to load it and just go with the basic JavaScript files (init.js).

SharePoint 2013

In SharePoint 2013 this has changed. Now every SharePoint JavaScript file is loaded only when it’s used, using the same SOD principle. Now most times sp.js is never loaded until it is called by SharePoint. Our call using ExecuteOrDelayUntilScriptLoaded isn’t working anymore since SOD doesn’t gets the notification that sp.js is loaded.

The solution

To overcome this issue there is a simple solution:

if(!SP.SOD.executeOrDelayUntilScriptLoaded(MyFunction, ‘sp.js’)){ 
  LoadSodByKey(‘sp.js’);
};

This will force the loading of sp.js still using the SOD principles. Also this check works in the SharePoint 2010 context, so you can upgrade your scripts now so they will continue to work when upgrading to SharePoint 2013.

Happy Scripting!