SharePoint 2013 Apps without the ASPX

The good news with making this type of decision is that you lose very little.  In fact, the only clear losses we’ve noted so far are the standard SharePoint chrome and the ability to use SharePoint controls.  Some may not even count these as losses.  Another thing you lose is a ton of html, css and javascript that are being rendered onto the SharePoint page, most of which you likely don’t need and some of which could even conflict with something you may want to add onto the page.

Two important gains made by this decision are performance and complete control over your markup, style and script.  But with that comes tackling a few hurdles on your own that SharePoint was taking care of for you.

Before getting started, a quick disclaimer: this post assumes that you have a working knowledge on some basic tenants, like jQuery and making REST calls within SharePoint 2013.  Also, this post is not exhaustive and hopes to simply cover some pitfalls and speed bumps one might encounter with getting started on your own “naked” html page.

The Scripts

Here is what our basic header might look like, script-wise.  We’re big fans of starting with Bootstrap.

<!-- Core Web libraries -->
<script type="text/javascript" src="../Scripts/libs/jquery-1.10.2.min.js"></script><script type="text/javascript" src="../Scripts/libs/knockout-2.2.1.js"></script>
<script type="text/javascript" src="../Scripts/libs/fullcalendar.min.js"></script><script type="text/javascript" src="../Scripts/libs/bootstrap.min.js"></script>
<script type="text/javascript" src="../Scripts/libs/bootstrap-datepicker.js"></script><!-- Core SharePoint libraries --><script type="text/javascript" src="/_layouts/1033/init.js"></script>
<script type="text/javascript" src="/_layouts/15/MicrosoftAjax.js"></script><script type="text/javascript" src="/_layouts/15/sp.runtime.js"></script>
<script type="text/javascript" src="/_layouts/15/sp.js"></script><script type="text/javascript" src="/_layouts/15/sp.core.js"></script>

Query Strings – Be Prepared

Let me add one more script to the pile.  In your AppManifest.xml file for your SharePoint app, you will notice, by default, SharePoint sets up your app to have a set of query string values passed in whenever someone clicks on your app from the Host web.  These are denoted as {StandardTokens}.  You can customize these as well.  If you leave the default, then you can expect the following values are in your query string: SPHostUrl, SPLanguage, SPClientTag, SPProductNumber, and SPAppWebUrl.  At a minimum, you’re probably going to find yourself needing SPAppWebUrl, particularly for your REST calls into SharePoint.  I personally really like the following solutions produced by BrunoLM on StackOverflow.  Essentially, build a jQuery plugin that looks like the following, into a file called jQuery-QueryString.js:

(function($) {
 $.QueryString = (function(a) {
 if (a == "") return {};
 var b = {};
 for (var i = 0; i < a.length; ++i)
 {
 var p=a[i].split('=');
 if (p.length != 2) continue;
 b[p[0]] = decodeURIComponent(p[1].replace(/+/g, " "));
 }
 return b;
 })(window.location.search.substr(1).split('&'))})(jQuery);

This then allows you to pull something from the query string using jQuery, like so:

$.QueryString["param"]

You’ll see this structure employed during our RESTful call examples later on. So our html files also then have the following script reference in the header as well, below our jQuery reference:

<script type="text/javascript" src="../Scripts/utils/jQuery-QueryString.js"></script>

I’m Ready

With Single Page Apps, you’re more than likely loading a decent amount of information asynchronously once the page is loaded.  In many of the apps we build, we’re establishing ViewModels, loading up observable arrays and binding them to views using Knockout.  If that’s the case, you’re more than likely planning on using document.ready to start loading in data and maybe even checking some information within SharePoint.

If you’re hoping to reference any objects found in the core javascript libraries used by SharePoint, then you need to account for the fact that SharePoint’s objects are still doing a number of things after the document.ready event fires (see SharePoint 2013 add javascript after whole page load – Stack Overflow for some further details and other tips).  I personally still like to use CSOM to do some initial context loading, like user and web information.  I found this structure to be particularly elegant from Ian Chivers with dealing with SharePoint’s load events: Client-side People Picker control for SharePoint Apps.  Taking a cue from this, we have in our App.js the following function:

function loadSharePointInformation() {
 var dfd = $.Deferred(function () {
 //Establish what we want
 web = context.get_web();
 user = web.get_currentUser();
 siteUsers = web.get_siteUsers();//Load everything
 context.load(web);
 context.load(user);
 context.load(siteUsers);

//Execute CSOM
 context.executeQueryAsync(
 function () {
 dfd.resolve(web, user, siteUsers);
 }),
 function () {
 dfd.reject(args.get_message());
 };
 });
 return dfd.promise();
 }

Here is what our document.ready event handler looks like:

$(document).ready(function () {
 SP.SOD.executeFunc('sp.js', 'SP.ClientContext', sharePointReady);
 })

Once SharePoint is ready, we can run what might normally be all of our logic found in document.ready.

function sharePointReady() {
 "use strict";context = new SP.ClientContext.get_current();

//Setup any knockout bindings

//Load our context information prior to making our restful viewModel calls
 loadSharePointInformation().done(function (web, user, siteUsers) {
 //Your code here
 });
 }

Proper Digestion

At some point, your app is going to need to perform CRUD operations on SharePoint data.  To do this, you’re going to need to POST.  In order to POST anything to SharePoint, you’re going to need a Form Digest value in the header, along with whatever else may be specified in the endpoints documentation for listsfiles and users.  It’s important to note that the form digest changes over time, so it needs to be the current value prior to posting.  From our research, we initially found this approach, which is to mimic SharePoint’s functionality:

  • Create a hidden input field with an id of __REQUESTDIGEST.
  • In your sharePointReady method, invoke a SharePoint function called UpdateFormDigest, where you pass the server relative url and the interval (in milliseconds?) for which the __REQUESTDIGEST input will be updated

From our experience, this path is flawed, or at a minimum, we were unable to get it working properly when we tried it; it did not actually appear to ever update our hidden input.  Not to mention, it presumes that all of your POST functions, wherever they might live, are going to pull the request digest from a hidden input on the page that references them.

This brings us to our preferred way of accomplishing this task.  In your individual methods, where a POST needs to occur, obtain the latest form digest first, then make your post.  While this ends up being a “double hop,” it would only be limited to your POSTs only.  It is also a surefire way of knowing you have the latest digest.  It also isn’t dependent on some value being present on the referencing page.  Let’s take a look at it.

As noted above, we enjoy using Knockout. In our viewModels, we have the following utility function, which employs the jQuery deferment process:

self.digest = function () {
 var dfd = $.Deferred(function () {
 $.ajax({
 url: $.QueryString["SPAppWebUrl"] +
 "/_api/contextinfo",
 type: "POST",
 contentType: "application/json;odata=verbose",
 headers: {
 "accept": "application/json;odata=verbose"
 },
 success: function (data) {
 if (typeof data.d.GetContextWebInformation.FormDigestValue !== 'undefined') {
 dfd.resolve(data.d.GetContextWebInformation.FormDigestValue);
 } else {
 dfd.reject('No digest found');
 }
 },
 error: function (err) {
 dfd.reject(JSON.stringify(err));
 }
 });
 });
 return dfd.promise();
 };

The gist of it is this: have a function that is prepared to grab the request digest at any time and only fire the resolve event once it has something.  The call to get a digest is made through the SharePoint API’s context endpoint: /_api/contextinfo.  As you can see above, the Form Digest is found at d/GetContextWebInformation/FormDigestValue in the JSON response.

Now that we have this method in our viewModel, let’s take a look at how it might be used.  Here is an example function for creating an item in a custom list, as a knockout viewModel operation:

self.createSomething = function (title, description, dateValue, status) {
 self.digest().done(function (digest) {
 $.ajax({
 url: $.QueryString["SPAppWebUrl"] +
 "/_api/web/lists/getByTitle('ListForThings')/items",
 type: "POST",
 contentType: "application/json;odata=verbose",
 data: JSON.stringify(
 {
 '__metadata': {
 'type': 'SP.Data.ListForThingsListItem'
 },
 'Title': title,
 'SomeDate': dateValue,
 'Description': description,
 'Status': status
 }),
 headers: {
 "accept": "application/json;odata=verbose",
 "X-RequestDigest": digest
 },
 success: function () {
 //Do something after success, like reload your collection
 self.getMyThings();
 },
 error: function (err) {
 console.log(JSON.stringify(err));
 }
 });
 }).fail(function (error) {
 console.log(error);
 });
 };

Notice that we first ensure that we have a digest, using deferment, waiting for it to come back as done.  At this point we can trust we have the latest digest, which then gets passed into the header as “X-RequestDigest”.  You now have the ability to post and you didn’t need anything from a SharePoint page to do it.

Hopefully, reading through some of these topics clears up any confusion on creating your own “naked” html page.  Enjoy!

Related Content: