Moving Everyone and Everything to AngularJS

We are late on the boat.  But I'm describing the journey for us, and we are truly on the boat.

 

So in mid 2014, SharePoint Gurus decided that we'll move everyone to JavaScript.  Traditionally, we are very much an InfoPath, Workflows and Configuration-over-Customization type of Consultancy.

My background is hardcore ASP.NET so I get all the fun stuff of writing Full Trust and Sandbox Solutions.  Custom Web Parts, Feature Receivers and Workflow Activities.  Later, as more work moves to client-side scripting, I would write Full Trust REST service end-points that provide lots of functionality to our InfoPath and Front-End Developers.

As InfoPath goes on a slowly (but surely) spiral of death, we look forward to where we should be investing our company's time and skills.  I was asked to consider and plan out the migration path for the company.

What we looked at:

JQuery

JQuery itself was a good starting point, and possibly the simplest JavaScript library one should start with.  Traditionally, it's used for DOM manipulation, but it's usefulness was extended with jQuery Ajax and jQuery Deferred/Promise.  We use JQuery and the various plugins for JQuery for a lot of things.  Additionally, JQuery works very well with SPServices on SP2007 customizations.

KnockoutJS

KnockoutJS is a DataBinding library.  JQuery is great for creating Read Only, One-Way DOM creation (say, you query an REST end point and want to create a bunch of elements in a table or list).  But JQuery is terrible at keeping track of the UI and the Original JavaScript object for you to post that data back.

KnockoutJS fixes that problem.  The good side is that it's intuitive.  The downside is that because JavaScript properties doesn't have onChanged() method, KnockoutJS creates observable property wrappers instead, and the syntax is not as pretty.

DurandalJS

Since KnockoutJS is only a databinding library, when you want to start looking at creating Single Page Applications, you need more stuff.  You need a Module Loader.  You need a Routing System.  And you need View Composition.

Durandal uses KnockoutJS for data binding, and adds RequireJS for module loading and JQuery for AJAX calls.

Although I loved DurandalJS, ultimately I could not recommend it to the whole company because the creator Rob Eisenberg (@eisenbergeffect) moved to AngularJS (and later to Aurelia.io).  I am keen personally on Aurelia, but it is not ready to introduce to the whole company in 2014.

ASP.NET MVC

I personally LOVE ASP.NET MVC.  But here's the problem - we have developers who are used to ASP.NET WebForms, so for them there are two jumps: first they have to learn ASP.NET MVC and then they still have to learn JavaScript.

JavaScript is required learning.

And ASP.NET MVC needs SharePoint Provider-Hosted apps - we can't, for example, use it with SharePoint 2010 or SharePoint 2007.

We have not turned our backs to ASP.NET MVC forever, it's just for the immediate focus, we are fully committed to client-side (Front-End) development technologies.  A few of our advanced guys will double back and pick up ASP.NET MVC later (or NodeJS).

AngularJS

All choices aside, we decided on pushing forward with AngularJS.  For many in the team, they have experience with JQuery but not DurandalJS nor KnockoutJS - so there's not a lot of wasted learning.

For me, I always compare AngularJS to DurandalJS and the biggest issue I have personally is that AngularJS is...  too magical.  UN-intuitively magical.  There are a lot of very custom syntax that you just have to know.  If you didn't know, you wouldn't know it's available.  But if you are looking at someone else's work, you can generally work backwards and understand Ah that's how it fits.  I wish it was more intuitive.

The biggest advantage for AngularJS is just how much community momentum there is for it.  There are a lot of people that understand it well, and lots of people building applications with it, and discussing it on the Internet - anywhere you look.

Additionally, lots of AngularJS and SharePoint / Office 365 training is available online.  Which means that we don't have to re-invent every wheel.

As a company and for our teams, it is a very safe bet.

There are a few related technology in the stack that we are using:

Bootstrap and Font-Awesome both give the front-end developer two things:  Rapid layout and font-glyphs without mucking around outside of the HTML templates and JavaScript code.

AngularJS + TypeScript

Since AngularJS announced that it will be built in TypeScript, there has been a resurgence of activity on the TypeScript front.

If your team has a lot of C# developers, TypeScript is worth your time to invest into.  It will make your team more productive, and as a team, you will write much better code.

 

What I did NOT look at:

All that said, there's also a lot that's unsaid.  I did not look into Backbone, EmberJS or Telerik KendoUI.  The main reason really is that I was personally very happy with DurandalJS, and if it was up to me personally, I would have chose Aurelia to go next.  But the evaluation we went through is to decide which framework to pursue for the entire company.

I always consider that we should never stop learning.  In the JavaScript space this is even more true.  Just like back in 2008 we decided (in my previous company) that we will pursue JQuery, in 2014 - we made a similar decision to pursue AngularJS.

 

An Army of Front-End Developers

So as a whole, our team is turning from InfoPath designers into Front-End Developers.  We have mastered JQuery and now diving into the fun that is AngularJS Directives.

 

Aim to have An Army of Full-Stack Developers in the nearby future

Moving towards a proper MEAN stack.  There are other components to tackle next:

  • M - MongoDB (a NoSQL document database suitable for storing XML and JSON)
  • E - Express (a lightweight web server, aka IIS)
  • A - AngularJS (front-end)
  • N - NodeJS (run JavaScript on the server, aka C# or .NET Framework)
  • Everything will have to run Provider-Hosted, most likely on Azure

For now - we are Front-End developers and happy with just the UI and storage of the form contents.

  • A - AngularJS
  • S - SharePoint Library (storage of XML and JSON - can be easily loaded via client-side scripting)

Summary

https://twitter.com/alwaysbetonjs

https://twitter.com/alwaysbetonjs


Presenting "PhantomJS" at SPBizConf free online conference on June 18.

I will be presenting "Introducing PhantomJS: Headless Browser for your SharePoint/Online" in the SPBizConf on 18 June.

http://www.spbizconf.com/events/headless-in-sharepoint-phantomjs-will-blow-your-mind/

Despite the -JS in the name of the title, PhantomJS is not another Javascript library.  Instead, it is a headless browser that lets you automate many tasks involving a browser, and there are some very nifty tricks you can use this to build site directories or create PDF snapshot of documents and forms.

Timing

Technically, because of Timezone Differences, it will actually be the 19th of June at 7AM in the morning.  The session is pre-recorded and I will basically be watching it along with the attendees and replying to questions in the chat room.

More Online.  More content.

Online Conferences such as SPBizConf and Unity Online are a new thing for me, and has prompted me to start looking into setting up recording room at home for more, higher quality, recorded content to be made available from my local presentations.  I'm very excited to be able to participate in global conferences like this without the expense of having to do crazy travel - travelling from Australia to "anywhere" tend to get expensive very quickly.

Will it blow my mind?

The curious will see the old name of the session.  I've since calm down and renamed the title, but the old URL has to stick for link reasons.  You need to let me know if this session gave you great ideas about what sort of shenanigans you get up to afterwards.

Office 365 Saturday Perth #O365PER summary

On 22nd of May I had the pleasure of presenting O365 Saturday Perth.  In the past years, we have called this SharePoint Saturday.  We re-branded the name and this year had a mix of content from SharePoint On-Premises, SharePoint Online as well as Azure and Office 365.

People from Perth are always early risers - the room was quite packed early in the day.

Here's James Milne presenting the keynote.

I presented Introducing PhantomJS: Headless browser for SharePoint/Online.

Had lots of feedback about what people found it interesting, many people said it has potential for scenarios that they didn't think were previously possible.  I'm very keen to hear more feedback especially for different business cases.

Some of the feedback and demos will be rolled into an updated content over the year, so stay tuned.

Here are the PowerPoint and ZIP files for the presentation.

 

Nintex Work Inspired Breakfast Seminar - Sydney

I'm now a Nintex vTE (Virtual Technical Evangelist).  Continuing my company SharePoint Gurus' strong partnership with Nintex.

I had the pleasure of presenting Nintex Tips at the Work Inspired Breakfast Seminars - Sydney event on May 15.

http://www.nintex.com/company/events-webinars/2015/5-15-2015-work-inspired-breakfast-seminars-sydney

As I wasn't sure of the audience, I decided to cover the simple Nintex workflow scenarios and diving deeper into the complex - developer minded (heavy API stuff) at the end.

  • Site Workflows
  • Scheduled Workflow
  • Iterating through Query SQL
  • Recursive CAML Query
  • Regex Options in the Regular Expression Action
  • XSLT to clean up complex XML namespaces
  • And finally, pre-caching json queries in Javascript based applications (great for javascript charts that needs lots of data)

It was quite interesting that the Nintex Breakfast is targeted at existing Nintex customers and really just show casing what they can do with tools they have already got.  So there was no hard sell - just "you can already do this".  There was a bit of discussion from the tables regarding various techniques and tricks.

Timing wise, some were able to stay to end (developers) for the last few demos while many had to go after 9:30am.  Still a great experience and will look forward to more of these events in the future.

I've previously worked with Dan Stoll and he's always a great show.

SharePoint Add-in: Accessing Webcam with Only Javascript

This blog post details how to access your Webcam via Javascript through the browser, and upload that content to a SharePoint library.  Then, with an added bonus, set it to be your User Profile Picture.

I have build this User Profile Webcam solution as an Add-in for SharePoint (was App for SharePoint).  This is a SharePoint Hosted app.  All the code runs in the browser and access SharePoint via the SharePoint Online API.

Step 1.  Access your webcam.

Modern Browsers:  Immersive Internet Explorer, Firefox, Chrome and Safari all has ways to access your webcam via

getUserMedia

 

But this doesn't work on IE (Desktop).  So for that we'll add a Polyfill (Flash). 

 

There's a project on github that does this nicely, and I use large chunks of the code from their demo.

https://github.com/addyosmani/getUserMedia.js

<script type="text/javascript" src="../Scripts/html5.js"></script>
<script type="text/javascript" src="../Scripts/getUserMedia.min.js"></script>
<!-- Add your JavaScript to the following file -->
<script type="text/javascript" src="../Scripts/App.js"></script>


<div id="webcam"></div>
<canvas id="canvas" height="240" width="320"></canvas>

<br />
<button class="btn" style="width:140px;" id="takeSnapshot">Take a picture</button>
this.snapshotBtn = document.getElementById('takeSnapshot');
$(this.snapshotBtn).click(this.getSnapshot);

this.getSnapshot = function () {
    // If the current context is WebRTC/getUserMedia (something
    // passed back from the shim to avoid doing further feature
    // detection), we handle getting video/images for our canvas 
    // from our HTML5 <video> element.
    if (App.options.context === 'webrtc') {
        var video = document.getElementsByTagName('video')[0];
        //App.canvas.width = video.videoWidth;
        //App.canvas.height = video.videoHeight;
        App.canvas.getContext('2d').drawImage(video, 0, 0, App.canvas.width, App.canvas.height);

        // Otherwise, if the context is Flash, we ask the shim to
        // directly call window.webcam, where our shim is located
        // and ask it to capture for us.
    } else if (App.options.context === 'flash') {
        window.webcam.capture();
        App.changeFilter();
    }
    else {
        alert('No context was supplied to getSnapshot()');
        return false;
    }
}

The code basically takes either the webrtc object and copy the image to the App canvas.  Or use the flash polyfill and Flash will write the image to the canvas element.

 

Step 2. Binary Format

This part is hairy.  But you are a developer, and dealing with Binary Encoding in JavaScript is what we do.

/*
Now, get canvas and do decoding.
1. canvas can return image data in png, in dataURL format.
2. strip the heading string "data:image/png;base64,"
*/

var ctx = App.canvas.getContext('2d');
var imageData = ctx.getImageData(0, 0, App.canvas.width, App.canvas.height);
var dataURL = App.canvas.toDataURL('image/png');
var base64 = dataURL.replace(/^data:image\/png;base64,/, "");

This is base64 dataUrl format.

 

/*
Reading.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#Solution_.232_.E2.80.93_rewriting_atob%28%29_and_btoa%28%29_using_TypedArrays_and_UTF-8

We need to convert DataURL to ByteArray
Mozilla has these two functions.
*/

function b64ToUint6(nChr) {
    return nChr > 64 && nChr < 91 ?
        nChr - 65
      : nChr > 96 && nChr < 123 ?
        nChr - 71
      : nChr > 47 && nChr < 58 ?
        nChr + 4
      : nChr === 43 ?
        62
      : nChr === 47 ?
        63
      :
        0;
}

function base64DecToArr(sBase64, nBlocksSize) {
    var
      sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
      nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);

    for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
        nMod4 = nInIdx & 3;
        nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 6 * (3 - nMod4);
        if (nMod4 === 3 || nInLen - nInIdx === 1) {
            for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
                taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
            }
            nUint24 = 0;
        }
    }
    return taBytes;
}

var byteArray = base64DecToArr(base64);

This gives us ByteArray.  We're getting close.

I'd like to tell you that I spend a good 3 evenings working to this point.  Try debugging that code and see if your eyes bleed!

/*

Finally, 

The ByteArray needs to be encoded as a string, and in the POST call, "binaryStringRequestBody" needs to be "true"

https://msdn.microsoft.com/en-us/library/office/dn769086.aspx
http://blogs.msdn.com/b/uksharepoint/archive/2013/04/20/uploading-files-using-the-rest-api-and-client-side-techniques.aspx
http://sharepoint.stackexchange.com/questions/54218/sharepoint-2013-rest-api-upload-image

*/

var binaryString = '';
var len = byteArray.byteLength;
for (var i = 0; i < len; i++) {
    binaryString += String.fromCharCode(byteArray[i])
}

SharePoint API wants data in BinaryString format.  Which is basically each byte of the ByteArray encoded as a concatenated string.

 

 

Step 3.  Upload to SharePoint

/*
Use RequestExecutor to post file back to sharepoint
*/

var appWebUrl = context.get_url();
var requestExecutor = new SP.RequestExecutor(appWebUrl);

var uploadPictureEndPoint = appWebUrl + "/_api/web/lists/getByTitle(@TargetLibrary)/RootFolder/Files/add(url=@TargetFileName,overwrite='true')?" +
    "&@TargetLibrary='" + "Pictures" + "'" +
    "&@TargetFileName='" + _spPageContextInfo.userLoginName + ".png" + "'";

/*
Using RequestExecutor, don't need REQUESTDIGEST
*/
//var digest = $("#__REQUESTDIGEST").val();

requestExecutor.executeAsync({
    url: uploadPictureEndPoint,
    method: "POST",
    headers: {
        "Accept": "application/json;odata=verbose"
    },
    contentType: "application/json;odata=verbose",
    binaryStringRequestBody: true,  // binaryStringRequestBody must be true
    body: binaryString,
    success: function (x, y, z) {
        alert("Success! Your file was uploaded to SharePoint.");
    },
    error: function (x, y, z) {
        alert("Oooooops... it looks like something went wrong uploading your file.");
    }
});

 

Step 4.  Setting User Profile Picture.

 

var appWebUrl = context.get_url();
var requestExecutor = new SP.RequestExecutor(appWebUrl);
var setPictureEndpoint = appWebUrl + "/_api/sp.userprofiles.peoplemanager/setmyprofilepicture";

requestExecutor.executeAsync({
    url: setPictureEndpoint,
    method: "POST",
    headers: {
        "Accept": "application/json;odata=verbose"
    },
    contentType: "application/json;odata=verbose",
    binaryStringRequestBody: true,
    body: App.binary,
    success: function (data) {
        alert('Set My Profile Picture succeeded, it will take a few seconds for the change to be propagated.');
    },
    error: function (error) {
        alert("Oooooops... it looks like something went wrong updating your profile picture (no permission?).");
    }
});

For this API call to /setmyprofilepicture to succeed, your App must have additional permissions.

This will ask the user (or Tenant Administrator) to grant the correct permission.

If you don't grant this permission, the webcam can still save picture to library, but it won't be able to set picture as user profile picture.

Summary

  • Get Webcam via Browser
  • Process canvas data to imageUrl to byteArray to BinaryString
  • Upload to SharePoint Library
  • Set as User Profile Picture
  • Achieve Zoolander face!
  • You can download the App for free from the Office Store to see it in action.  
    Feel free as a developer to hit F12 and step through the code.
  • Leave a comment below and let me know what you think.  For example, I think there's a need for an Outlook Add-in that uses the webcam.  Especially, if it can run on an iPad.