Tag Archive for JavaScript

Hide empty fields on a SharePoint 2013 Page Layout

Nice this is already my second post this month, that doesn’t happen often! This time a little trick to help developers with Custom Page Layouts. I’ve seen quite a few solutions where Custom Page Layouts where used to display content in a sort of Form feel. For instance where all the Content Type fields are rendered with Labels to indicate what the content means. A typical problem with this solution is that whenever you decide to leave the contents of a field empty, still the Label is being rendered. And so is the HTML of the field.

Custom Controls

When searching for a solution lots of BlogPosts pop up about creating a custom ASP control based on the SPSecurityTrimmedControl to manage the rendering like in this Post of Brian Farnhill.

This approach works in an On-Premises environment where moving to the cloud is not an issue. But since I’m a huge fan of Cloud Ready programming I was searching for a different solution.

jQuery for the win

The first open door is to use jQuery on the page for some good ol’ DOM manipulation to make the fields disappear when empty. We can easily work our way through the DOM looking for elements to hide. This is an ok solution but I prefer to have it a little bit more generic. Besides that I took some time to investigate in other Frameworks over jQuery and came across this post:

http://stackoverflow.com/questions/18414012/why-use-angularjs-instead-of-jquery

In short: Libraries and FrameWorks are two different things used for different purposes. Since I’m a fan of data-binding and the abstraction level a framework provides, I decided to use Angular.

Angular for the win

When using Angular this issue can be solved in many ways. I choose to create a controller and applied it to a <div> element that wraps the SharePoint Control that needs hiding. My Page Layouts is looking like this.

Here we have the Display of the Titel Property and a Field calld “DoelGroep” which is a TaxonomyField. Preceding the contents of the Taxonomy field is a label Displaying the DisplayName of the Field.

Wrap the Field

First I have to add the data-ng-app to the top element where all the fields are childs of.
<div class="article-content row" data-ng-app="Empty">

To make sure my Controller works on the specific Fields, I have to wrap those fields with a new HTML element.
<div data-ng-controller="EmptyController"></div>

How my HTML finally looks.

Build the Controller

Now all we have to do is write out the controller. Since we are only wrapping the SharePoint Field that holds content we have to keep in mind to remove its parent since the Label is part of the parent as well.

Edit Mode

Since we don’t want this javascript code to execute when the page is in Edit Mode, we need to add this piece of script to a EditModePanel with DisplayProperties set to View.

That’s it

Just by applying these small changes to the Page Layout (of course make sure AngularJS is loaded). Pretty awesome and practically works with most SP Fields (I haven’t checked them all). Hopefully this will help build a solid solution to deal with empty SharePoint Fields on Page Layouts and is a “cloud ready” solution.

4 Steps to smarter App Part resizing

Wow it feels like forever since I wrote something and finally I got some good new content. I started to research a little bit more on the resizing of App Parts in SharePoint Hosted Apps and came up with some really small changes in basic CSS and JS that make App Part resizing a lot more stable and X-browser compatible. #winning

 

1. No horizontal scrolling

In this example I’ll use an App that Lists Apps on the Host Web. This particular App displays a list of Title just below one another. Nothing fancy. However when we want to display the page in an App Part, this happens.

Capture1

Visually it seems that we have more then enough space between the Titles and the end of the IFrame (300px width by default). Still we see a nasty scrollbar! What happened? Inspecting some elements up in the DOM explains a lot. Very funny SharePoint.

Capture1-2

The defaultcss.ashx causes trouble! This control is loaded on the page by default in an ASPX page like Default.aspx or most likely by a dev in a plain HTML page to keep default SharePoint branding alive in the App. That is a good principle, but this width style is not really helping us out.

Full Width Power

In order to get rid of the scrollbars in this scenario there’s a simple CSS fix we can apply in our App.css.

#contentBox { min-width:100%; margin:0; }
#contentRow { padding:0; }

This way we make sure no weird margins or paddings will cause our App Part more pain and it’s also going to help us when resizing the WebParts height later on.

Capture2

Better! Now the content, that we know for sure should fit, actually fits. Width wise. Still the height of the App Part is not right.

 

2. Resize your App Part

Resizing App Parts using the postMessage technique is nothing new to most people. We need a function in our App that sends a message to the parent of the IFrame. SharePoint can pickup this message and then resizes the IFrame for you using a given width and height. I used the following code

resizeIFrame = function (height, width) {
 if (window.parent == null) return;
 var senderId = '';
 var params = document.URL.split("?")[1].split("&amp;");
 for (var i = 0; i &lt; params.length; i = i + 1) {
  var param = params[i].split("=");
  if (param[0].toLowerCase() == "senderid")
   senderId = decodeURIComponent(param[1]);
 }

 //Bug for self reference in href='#'.
 senderId = senderId.replace('#', '');
 var message = "<Message senderId=" + senderId + ">"
 + "resize(" + width + "," + height + ")</Message>";
 window.parent.postMessage(message, "*");
}

In this common piece of code I added a check for “#”. When this is used in the App (I came cross it once), the QueryString is extended with this “#”. This causes the SenderId, which is always last in the QueryString, to be compromised and the Resize function from SharePoint to fail!

Function call

To call this function we first determine the height of the DOM element that holds all the content. The width shall be “100%” to have an App Part that will always fill the entire space.

var height = jQuery('#OverView').outerHeight(true);
var width = "100%";
resizeIFrame(height, width);

This function resizes the entire iframe to the dimensions given.

Capture3

But wait! there is still a vertical scrollbar! Something must be wrong. In the Elements File of my ClientWebPart I removed the Height and Width properties of the App Part.

Capture3-1

Here is where a very weird phenomenon shows itself. Since we don’t have any requirements set regarding width or height, SharePoint sets a default width and height to the AppPart which is the same as the ClientWebPart xml states when it is just created.

Capture3-2

Here our default width is 300px. The funny bit is that because we use the ResizeIFrame method, the width is only set to 100% after the Resizing is done. To make it even more complex, the Height is meisuring the content that now is rendered within 300px width. Therefor the height also cannot be trusted. This makes the resizing of the App very buggy. Now we see it still as 300px width. Refreshing a couple of times shows 100% width but still with a vertical scrollbar and height that is just a couple of px off. Some DOM element must be holding back some padding info.

 

3. Body Container

Again the main obstacle here is some styling that comes from defaultcss.ashx. By default the CSS sets a padding-bottom:35px; on #s4-bodyContainer. This is why we add to our App.css this piece as well.

#s4-bodyContainer { padding:0; }

Capture4

Instantly this is starting to look good! The App Part now scales over the entire width of the Page using a correct height and most important: NO scrollbars! Since I placed my App Part in the text part of this SitePage it is displayed in full width. When I use a Publishing page with multicolon WebPart Zones, it will also adept to the width of the colons. This comes in very handy for instance with use of Bootstrap.

 

4. One last thing

One last nag, which is actually really tough. Thereby I don’t mean the fix, that ones easy. I’m talking about consideration on how to deploy the fix. The thing is, as I just explained, the height is calculated at the wrong time in rendering. In order to get the height right, the IFrame must have it’s width set to 100% first. We’ve seen SharePoint set it to a default value of 300px. When a dynamic list of <span>’s wrap over 3 lines for instance, the height at a 100% width is very different from the height at 300px width.

A fix here can be set all IFrames to a default width of 100%. I don’t see why you shouldn’t do that in the first place. But still it depends right.

Custom CSS

The best option in my opinion is to use the capability to upload and add your custom CSS when using Publishing Sites. You can do this in the MasterPage section of the Site Settings. Just create a blank CSS file and add

iframe{ width:100%; }

When you aren’t using publishing sites, there is another possibility.

UserCustomAction

You can add some code to your App to deploy a UserCustomAction to the HostWeb. In the UCA you can add a ScriptBlock that writes this CSS style to every page. This is a dangerous option when not done right. Waldek wrote about these things and explained why it can break your site. The following code will do so, but keep in mind that everytime this executes another UCA is created. You still have to create a “Run Once” type of mechanism.

P42.ScalingAppPart.setFrameWidth = function ($) {
 var execute = function () {
  var deferred = $.Deferred();
  var hostUrl = decodeURIComponent(P42.Utils.getQueryStringProperty("SPHostUrl"));
  var appWebUrl = decodeURIComponent(P42.Utils.getQueryStringProperty("SPAppWebUrl"))

  var context = new SP.ClientContext(appWebUrl);
  var factory = new SP.ProxyWebRequestExecutorFactory(appWebUrl);
  context.set_webRequestExecutorFactory(factory);
  var appContextSite = new SP.AppContextSite(context, hostUrl);
  var site = appContextSite.get_site().get_rootWeb();
  var userCA = site.get_userCustomActions();

  var customAction = userCA.add();
  customAction.set_location("ScriptLink");
  customAction.set_scriptBlock("document.write('<style>iframe{width:100%}</style>');");
  customAction.set_sequence(1000);
  customAction.update();

  context.executeQueryAsync(function () {
   deferred.resolve();
  }, function (sender, args) {
   deferred.reject(sender, args);
  })

  return deferred.promise();
 };

 return {
  execute: execute
 }
}(jQuery);

Famous Last Words

These steps will get you some smoother App Part resizing experience that will work cross-browser. By removing all the extra margins and paddings you are now in control over the exact width and height of your App Part. I never got it right untill I put some work in it, hopefully this post will help you too getting it right.

– Cas

Elevating User permissions in SharePoint Hosted App Web

Lately we’ve done some SharePoint Hosted App development and came across a Permission Issue when it comes to the App Web. Also inspired by this thread on the MSDN forums: http://social.msdn.microsoft.com/Forums/sharepoint/en-US/a34b1a2d-40d4-46d8-838b-1ca90dc860a2/user-permissions-inside-a-sharepoint-hosted-app-web we decided to follow up on this challenge.

Host Web vs. App Web

There are lots of different opinions when it comes to the subject of Host Web vs. App Web. Many people believe that in order to be completely isolated from “Out of the Box” SharePoint, all App data must be stored and managed within the App Web. This eliminates the need of complex App Permissions on the Host Web and the risk of rejection by users that don’t want to install the App because of those. The drawback of this however in my experience is that by default an App Web fully inherits it’s permissions from the Host Web. Meaning users that have read permissions on the Host Web aren’t allowed to create, update or delete the data stored in Lists in the App Web. In some scenario’s you want even users that are just visitors on the Host Web, to work with your App and therefor be able to update data stored in the App Web. What to do?

Setting Permissions in the App Web

Fortunately for us, the App Web is very similar to just any SharePoint SPWeb object. It’s possible to manage the Permissions in the App Web the same way we do on the Host Web, by using the Applications Page “/_layouts/15/user.aspx”. Of course this page is only available to users with the right permissions on the Host Web (due to the inheritance). But if you navigate to this page from your App Web we can see what’s going on with the Permissions.

PermissionsAppWeb

If you have a List deployed in the App Web that holds App Data just add a QueryString to this page “?List=[ListId]” and you’ll be able to see the permissions of that specific List.

Break the chain

In our case we needed All Users to be contributors to this List but without touching the Host Web’s permissions. Because we had just one List deployed in our App we decided to Break the Inheritance on the App Web level and so the List inherits all Permissions from the App Web level. We needed a one time event by a user that is allowed to do this on the App Web in the first place. This is were we need App Permissions on the Host Web. The idea is to let an admin install the App. When asked for App Permissions, the admin grant the permissions thereby redirecting him to the App Web after installation and allow us to let him “Initialize” the App. That initialization step executes some JavaScript that breaks role inheritance and adding Contribute Permissions to the group Everyone on the App Web.

App Installation

When dealing with App Permissions obvisouly the “Least Priviliges” Approach is best practice. In this particular scenario it doesn’t really matter what App Permissions are granted in terms of abuse. Since the App doesn’t have any interaction with the Host Web we don’t load any libraries that let users make Cross Domain calls back to the Host Web. So even if the App Permissions are set to “Manage Web” users will not likely to be able to make CSOM calls back to the Host Web unless they are very skilled. In this example I’ll use the Manage Web Permissions for the App.

AppPermissions

Now when an Admin Trusts this App he will immediately be redirected to the Default Page of the App Web.

Redirect

Now we have the event set up to get some cool script going for changing the App Web’s permissions.

Break Role Inheritance

In order to break the Role Inheritance and add a new Role Assignment we used the example from Yuri Leontyev:http://spsite.pro/Blog/Post/3/SharePoint-2013-REST-API-%E2%80%93-How-to-set-Unique-Permissions-(Item-Level-Permissions)

My function to break role inheritance:

P42.UpdateAppPermissions = function ($) { 
 var execute = function (appWebUrl) {
  var deferred = $.Deferred();
  $.ajax({
    url: appWebUrl + "/_api/web/breakroleinheritance(copyRoleAssignments = true, clearSubscopes = true)",
    type: 'POST',
    headers:{ "accept": "application/json;odata=verbose", "content-type": "application/json;odata=verbose", "X-RequestDigest": $("#__REQUESTDIGEST").val() },
    success: function () { deferred.resolve(true); },
    error: function (sender) { deferred.reject(sender); }
  });
  return deferred;
 };
 return { 
  execute: execute
 }
}(jQuery);

This will only break the role inheritance between the Host Web and the App Web, but also copies the default permissions.

UniquePermissions

Add Role Assignments

The second part is to get the Principal ID of the group “Everyone” and add a Role Assignment that says Everyone has Contribute permissions. We will need two functions for that.

P42.GetEveryone = function ($) {
 var execute = function (appWebUrl) {
  var url = "/_api/web/siteusers?$filter=Title eq 'Everyone'&amp;$select=Id";
  var deferred = $.Deferred();
  $.ajax({ 
    url: appWebUrl + url,
    type: 'GET',
    headers: { "accept": "application/json;odata=verbose" },
    success: function (data) { deferred.resolve(data); },
    error: function (sender) { deferred.reject(sender); }
  });
  return deferred;
 };
 return {
  execute: execute
 }
}(jQuery);

P42.AddEveryone = function ($) {
 var execute = function (appWebUrl, principalId) {
  var deferred = $.Deferred();
  var ctx = SP.ClientContext.get_current();
  var web = ctx.get_web();
  var roleDefinition = web.get_roleDefinitions().getById(1073741827);
  // Create a new RoleDefinitionBindingCollection
  var newBindings = SP.RoleDefinitionBindingCollection.newObject(ctx);
  // Add the role to the collection
  newBindings.add(roleDefinition);
  // Get the RoleAssignmentCollection for the target list
  var assignments = web.get_roleAssignments();
  // Add the user to the target list and assign the use to the new RoleDefinitionBindingCollection
  var roleAssignment = assignments.add(web.getUserById(principalId), newBindings);
  ctx.executeQueryAsync(function () { deferred.resolve(); }, function (sender, args) { deferred.reject(sender, args); });
  return deferred;
 };
 return {
  execute: execute
 }
}(jQuery);

To obtain the ID of the default Contribute permission we can use a simple REST call on any SharePoint Web. This ID is fixed and always usable. From this overview we can also see other Permissions we could set if we like.

https://your_sp_site.sharepoint.com/_api/web/roledefinitions​ 

image

Run Once

Ok now the script part only has to run once. In order for that to happen there are lots of different options, but the one I always remembered from early SharePoint Development is setting a Property in the property bag that indicates the script has already been executed. I’ve created two functions, one to check if the Property has been set and one to actually set it.

P42.GetInitProp = function ($) {
 var execute = function () {
  var deferred = $.Deferred();
  var url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/AllProperties/?$select=" + "_AppInit";
  $.ajax({
    url: url,
    type: 'GET',
    headers: { "accept": "application/json;odata=verbose" },
    success: function (data) { deferred.resolve(data); },
    error: function (sender) { deferred.reject(sender); }
  });
  return deferred;
 };
 return {
  execute: execute
 }
}(jQuery);

 
P42.SetInitProp = function ($) {
 var execute = function (value) {
  var deferred = $.Deferred();
  var ctx = SP.ClientContext.get_current();
  var web = ctx.get_web();
  var properties = web.get_allProperties();
  properties.set_item("_AppInit", value);
  web.update();
  ctx.load(web);
  ctx.executeQueryAsync(function () { deferred.resolve(); }, function (sender, args) { deferred.reject(sender, args); });
  return deferred;
 };
 return {
  execute: execute
 }
}(jQuery);

On the default page we can use this piece to check the Property.

P42.GetInitProp.execute().promise().then(function (data) {
 if (data.d.OData__x005f_AppInit) {
  jQuery('#AfterInit').show();
 }else {
  jQuery('#PreInit').show();
 }
}, function (sender, args) { 
 //Error 
});

Where my property in this case is “_AppInit”.

Promises

Now the final piece of this puzzle is to make all these functions run in controlled flow. You may have noticed there is quite some dependency on one another hence the jQuery Deferred that is in all of these functions. The one function that makes all these calls happen is:

initApp = function (appWebUrl) {
 P42.UpdateAppPermissions.execute(appWebUrl).promise().then(function (success) {
  P42.GetEveryone.execute(appWebUrl).promise().then(function (data) {
   var principalId = data.d.results[0].Id;
   P42.AddEveryone.execute(appWebUrl, principalId).promise().then(function () {
    //Create Web Property
    P42.SetInitProp.execute(true).promise().then(function () { 
      //Success! Do other stuff 
     }, 
     function (sender, args) { 
      console.log(args.get_message() + '\n' + args.get_stackTrace());
    });
   }, function (sender, args) {
    console.log(args.get_message() + '\n' + args.get_stackTrace());
   });
  }, function (sender, args) {
   console.log(args.get_message() + '\n' + args.get_stackTrace());
  });
 }, function (sender, args) {
  console.log(args.get_message() + '\n' + args.get_stackTrace());
 });
};

Basically this is it. We do need to pass the AppWebUrl to this method. I think some of this code can actually be done nicer, but just for this example it should work.

image

Final Considerations

With these pieces of code it is easy to set the App Permissions the way you want to in one initial step however this workaround is not really ideal. Even though it’s hard and users have to be real code guru’s, it is possible to abuse the App Permissions we granted. Using GetScript and the Cross Domain Library it’s possible to hack your way into the Host Web,  but frankly this is always the case when using App Permissions.

Besides that using an Initial step that must be done by an Admin is also not ideal. Still with the given tools at the moment I think this is the best solution yet to take control of the permissions in the App Web. Hopefully this will improve in the future.

And finally another great explanation on why Breaking Role Inheritance is not that good of a practice. A very well explained 3 part Blog Post.

Breaking is not hard to do

Site Permissions Breaking Bad Episode 1

Site Permissions Breaking Bad Episode 2

But remember with SharePoint it always depends! </out>

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!