SharePoint is a collaboration tool that developers and other IT professionals love to hate. It works to the point where businesses are happy to leave it as their main "information sharing and collaborating" infrastructure. It is, however,  clunky, slow and painful to change or customize from an end user perspective.

I've been making a few SharePoint sites recently, and one in particular required some JavaScript magic to meet a UX requirement. Microsoft doesn't really have much on this, and they're fairly vague on best practices for JavaScript on a site-by-site basis.

Now I could've gone down the inline script path, just hacked away some code to spec on an .aspx page and been done with it. However, there is a high probability that down the track someone else will have to reuse or change some logic in this code. Oh, and I do like the opportunity to over-engineer a solution. So I wanted to modularise my JS code cleanly, and RequireJS seemed to be a good tool for the job. It allows for a fixed file structure and uses an easy-to-follow module definition / dependency declaration format, and I'm pretty familiar with it.

Whilst Require (and Asynchronous Module Definition, a way to define a module in JavaScript that allows you to also specify dependencies, and optionally give a unique name to your module.) is difficult to get your head around if you're writing the configs from scratch, it's pretty easy to pick up from conventions in existing code. So I'm going to be optimistic in that choosing this methodology, someone will be able to (mostly) read what's happening, and I will be adding to the maintainability of the site.

If you haven't touched RequireJS before, I suggest you read my previous article on RequireJS so you get a bit more of an understanding of the mechanics driving it.

Lets dive in!

File Structure

Briefly, here's an example file tree, starting at the Site Assets list.

Style Library (root)
|_ js
|_ app
|_ util1.js
|_ util2.js
|_ lib
|_ jquery.js
|_ init.js
|_ require.js
|_ MyPageDefinition.js
I recommend you use this or something similar to this for organising your project - take advantage of path aliases too.

File Definitions

require.js

Nothing special here, just your standard RequireJS lib file.

init.js

Contains the RequireJS config object, that is require.config(). Usually you'll define your baseUrl and paths/package aliases in here.

// init.js
require.config({
  baseUrl: "../../Style Library/js/lib",
  paths: {
    glacier: '../app'
  }
});

 

util1.js, util2.js

Here are just some basic utility modules that you can require. In this case, util2 is dependent on util1.

Example code for these files:

//util1.js
define(function() {
  function MyFunction() {
    return true;
  }
  return {
    MyFunction: MyFunction
  };
})
//util2.js
define(["glacier/util1"], function(util1){
  // use util1 in here
  return {
    ValueOfMyFunction: util1.MyFunction()
  }
});

MyPageDefinition.js

Here's where it gets interesting. I've decided to use a container JS file to execute all JavaScript relevant to this page, and will define this as the data-main element in my script tag that calls RequireJS.

Example code for this file:

// First require to load config paths and package paths (like 'glacier/')
// All of your pages will need to require() the init/config module like this so you can resolve paths.
require(['./init', ], function(init) {
  // Second require to load actual page dependencies
   require(['glacier/util2'], function(redirect_sharepoint) {
     redirect_sharepoint.redirectSaveButton('/Pages/default.aspx');
   });
});
Note: I'm pretty sure you can also plop this onto your page inline, but I haven't tested it and it sort of goes against the whole 'reusable atomic module' concept that RequireJS aims for.

Conclusion

So that's a high level overview of how it all works. I haven't used this on a serious project before that spans multiple modules, but I suspect RequireJS will scale well to handle the challenge.

Whilst there is some boilerplate involved in relation to having to import the config module every time, it's far better than having no traceability or centralised source of truth for all JavaScript modules in a project.

The lack of serious web development support for a SharePoint site owner (not administrator) is quite shameful, really. You'd expect Microsoft want to provide a standardized mechanism via which to define stylesheets and JavaScript. But they haven't, and there is very little documentation around "best practices", which you'd think to be integral to something that should scale well and standardize across an organisation.

It leaves you to wonder whether they even use the damn thing internally.

Further Reading

AMD readme

Why AMD? for a history of JavaScript module definition, and why AMD is like it is

Jasper Siegmund's "[SP2013] Loading Scripts Using RequireJS" inspiration for this post