10.29.2015

Developing an Angular Web App inside of SharePoint: Part 1 - Hello World in SharePoint

Angular is currently my favorite JavaScript Library. It feels like a marriage of the best of Desktop Development with the best of Web Development. It paves the way to understanding mobile and responsive design. Data-binding makes managing data on your page much more simple than the event-driven style of jQuery. Routing makes lazy-loading the pages of your app quicker and it makes spinning up new pages for your app quicker and easier as well.

(I tend to be verbose in my writing. Look for "tl;dr;" for "too long, didn't read" summaries of sections.

The Sales Pitch on using Angular in SharePoint

If you are not already sold on the idea of drinking the Angular Kool-Aid, let me go into some reasons why you should. I have 5 years of server-side SharePoint development in 2007 and 2010 (less in 2013). When we had the ability to write farm solutions on the server, doing anything in JavaScript felt like peasant-work. We avoided it at all costs. When the idea of "single-page-apps" came along, we scoffed at it as unnecessary. When we heard that SP2013 would switch to a greater emphasis on client-side development (JavaScript) we cringed. JavaScript is terrible ... why would we want more of it?

Later, I switched to a contract where deploying anything to the server was more trouble than it was worth because of environmental and contracting issues. Out of necessity, I went down the JavaScript path and haven't turned back. When I came across Angular, I realized my decision was the correct one. SharePoint is slow and it is often deployed in environments with an equally slow intranet and on computers that should were already obsolete 2 years before they were purchased as employee workstations. Ever time you click a link in SharePoint, you load about 1-2 dozen JavaScript files, some aspx files, web parts, a few css files, etc. Each of these has to be downloaded (or checked against the cache) and loaded into memory and processed. Most of these files were the same ones you just had on the last page you were on!

Angular Fixes a lot of this problem with routing. When we go into routing, we will be trading out the default Angular-routing library with angular-ui-router, which is far superior. I'll go more into that later. Routing allows you to load up your entire Angular Web App, after which, you can go to different pages within your App (called views) WITHOUT reloading all those pesky JS files. This lazy-loading means that you're only loading a very small amount of data when you switch pages. You will also learn to use promises and asynchronous web service calls to load your pages before you have the data you need on the page, so the page will load quickly. The data will then follow onto the page a moment later, and using some subtle animation, the user will have no idea that he waited any time at all.

My last sales pitch will revolve around data-binding, which does not provide a specific advantage over SharePoint, but is just a really awesome and structured way of writing your code. Data-binding will make managing complex amounts of data far-easier than you are probably use to. If you've already used other data-binding frameworks like knockout then you will know what I'm talking about. Knockout is great, and I've used it, but the syntax in Angular is far friendlier. However, the friendliness of Angular's syntax comes at a cost: it may take a little longer for you to figure out how Angular works under the hood because so much is done for you.

Getting Started with a Hello World Example

At this point, I will assume that you are already sold on the idea of bringing the power of Angular to your SharePoint site. I'll bring up the advantages of doing so, less as an attempt to convince you of those advantages, and more to help you recognize when you are adding value for your customers when you write a site. I'll also assume that you have some experience with SharePoint, though I'll assume a very low amount of experience with Server-side development and Client Side Object Model (herein referred to as either CSOM or JSOM. You will here JSOM more often when there is a danger of confusing JavaScript CSOM with C# CSOM, and the two need to be distinguished). Lastly, Bootstrap will be referenced a lot at a basic level. This really just means that we will be using pre-defined bootstrap classes that work well with Angular.

To get started, we need to create a main page that contains the minimum that is required to hook into SharePoint and allow HTML5 on the page. Start by adding a folder in Site Assets, or really any library or location on your site. Keep everything you need for your application contained in a single folder that you can copy easily. This helps with branching, so you can have a dev branch you're working on, previous versions you've saved, and a production branch that users will ultimately use.

Next, add an aspx file (or any file type and rename it to .aspx). It is necessary for your home page to be aspx and not html because this allows you to add a key asp control that is used to interact with SharePoint. This allows SharePoint to know your username and provide a SharePoint Context Object. If you've done server side development then the bit about a SharePoint Context Object will make sense to you. If not, don't worry about it.

The code snippet below includes the minimum that is required to have a SharePoint-hosted HTML5 page that can access SharePoint resources as the current user. Credit goes to all that js, and is modified to include support for Angular and Bootstrap.

 
<!DOCTYPE html>
<%@ Page language="C#" %>
<%@ Register Tagprefix="SharePoint" 
     Namespace="Microsoft.SharePoint.WebControls" 
     Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<!--Example from: http://allthatjs.com/2012/04/03/using-sharepoint-csom-in-html5-apps/-->
<html>
<head>
<meta name="WebPartPageExpansion" content="full" />
 <!--I don't remember what each of these does exactly, but they were all necessary for one reason or another-->
 <meta http-equiv="X-UA-Compatible" content="IE=edge;chrome=1" />
 <meta name=GENERATOR content="Microsoft SharePoint">
 <meta http-equiv=Content-Type content="text/html; charset=utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1"> <!--Required for viewport commands in css. Responsive Design-->
 
 
  <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->   

    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">

    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.1/angular.min.js"></script>
</head>
<body ng-app="main-app" ng-controller="master-model">
    <SharePoint:FormDigest ID="FormDigest1" runat="server"></SharePoint:FormDigest>
    
    
    <!-- Begin Custom Section. Everything outside of this section should be included in any SP/Angular page that you start, minus any custom js/css I may have added -->
  <h3>{{data.message}}</h3>
 <p>{{data.site.title || 'Loading Site Details...'}}</p>
    <input id="description" class="form-control" type="text" ng-model="data.site.description" placeholder="Description">
    <p>{{data.user.username}} <a href="mailto:{{data.user.email}}">{{data.user.email}}</a></p>
   <button type="button" class="btn btn-default" ng-click="data.toggle = !data.toggle">Toggle Me!</button>
    <!-- Really good example of how you should be debugging -->
    <pre>{{data | json}}</pre>   
    <!-- End Custom Section -->
   
    
    <!-- Put all your js at the bottom so the page loads faster. Put angular at the top so it can start sinking its hooks earlier-->    

 <!-- the following 5 js files are required to use CSOM -->
    <script src="/_layouts/1033/init.js"></script>
    <script src="/_layouts/MicrosoftAjax.js"></script>
    <script src="/_layouts/sp.core.js"></script>
    <script src="/_layouts/sp.runtime.js"></script>
    <script src="/_layouts/sp.js"></script>
    
    <!--Using CDNs for demo purposes -->
    <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.1/angular-animate.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.14.3/ui-bootstrap-tpls.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.14.3/ui-bootstrap.min.js"></script>
    <!-- include your app code --> 
    <script src="app.js"></script>
</body>
</html>

Next, set your app.js to the following:

 
(function(){
angular.module('main-app',['ngAnimate']).controller('master-model', ['$scope', '$q', function($scope, $q){
 function init(){
  $scope.data = { //Initialize data
   message: "Welcome to Angular!"
  }; 
  
  getSiteInformationUsingPromise().then(function(results){
   $scope.data.site = results.site;
   $scope.data.user = results.user;
  });


 } 
 
 function getSiteInformationUsingPromise(){
  return $q(function(resolve, reject){
   //Note that if you aren't using a master page, you can't just use $().SPServices.SPGetCurrentSite(). You have to use a relative path, not an absolute path which this function returns
   var ctx = new SP.ClientContext("/"); //This needs to include the entire relative path, included any site collections and subsites. This example uses the root site collection.
   var web = ctx.get_web();
   var user = web.get_currentUser();
   ctx.load(web);  
   ctx.load(user);
   ctx.executeQueryAsync(Function.createDelegate(this,
    function() {
     resolve({ //Angular knows to refresh the page after resolve is executed because we used an Angular Promise
      site: {
       title: web.get_title(),
       description: web.get_description()
      },
      user: {
       id: user.get_id(),
       username: user.get_title(),
       email: user.get_email()
      }
     })     
    }), Function.createDelegate(this, function(sender, args) {
     reject({
      title: 'Error'
     })
    }));
  });
 }

 
 init();
}]);
})();

There you go! This should be a basic Angular page with a minimal tie-in to SharePoint. In my next post I intend to expand further and eventually break the data-access layer out into a separate module as an Angular Factory. I'll also do some simple CRUD operations. In the mean time, look over the code and take note of many of the comments which drill down into a little more detail about why I'm doing things certain ways.

6.24.2015

Self-Expiring Comment Pattern

As a developer, having good style is important to me. I can’t tell the number of times I’ve inherited code, and wondered what the heck was going on. I can’t tell the number of times I’ve looked at code I’ve written six months ago and wondered what the heck was going on.

In school, I was taught to comment EVERYTHING. I don’t think this was the right lesson, as we would often comment functions with self-commenting names. There is no reason to add comments to a function called "public Item GetItemById(int ID)" in my opinion.

In any case, I have made it a point to comment any code that I didn’t think had an obvious function. I also, like many people, tend to comment obsolete code instead of deleting it. This can often result in a source file in which sometimes half of the file is commented out functions. This is even harder to read than a file with no comments.

So what I’ve come up with is the Self-Expiring Comment Pattern. I think it is simple enough, and I have used it long enough, that I’m ready to see what people think of it. I’ll use JavaScript for my example:

 
//Todo: Delete Commented Code after 9/24/2015 or after Sprint 5
/*function oldFunction(){
}*/

//Todo: Delete comment after 9/24/2015 or after Sprint 5
//This seems to work better than oldFunction because of x, y, and z
function newFunction(){
}

The concept here is super simple. If I am looking at this code that I just inherited on week 2 of my new job, and I see these comments, I don’t even have to read them. I know they are safe to delete, and I don’t have to worry that I’m breaking anything or deleting important reference code by doing it. This is sort of a pass-the-buck approach that we are probably already aware of as developers. You would do this when you have a comment that just reflects what you are thinking about right now, but you know will not be useful once you're finished with this section of code, or when the current build is deployed. You don't have to be too accurate with the date you pick. Just pick anything that ensures that your comment will not exist until the end of life of your code.

This works even better in Visual Studio or Eclipse, which interprets Todo comments as tasks which are gathered into a Tasks pane for easy access.

Since I’ve started doing this, I have found it easier to maintain enough comments in my code to be useful, without having so much that it becomes cumbersome. I’m going to start trying to push this concept more and more in the future. It seems like a small change, but having code that’s easy-to-read is worth it I think.

I welcome thoughts and feedback on this idea.

Update: After a few conversations, I think the consensus is that this idea is a solution to a problem that has already been solved a better way. Stick with proper version control, and this idea is not necessary. Only if proper version control is not an option should this idea be considered.

Update 2: Looks like someone has previously come up with a slightly better version of my idea called Tombstones