The SharePoint JavaScript Object Model–Resources and Real World Examples

Series Contents

  • Part 1: Introduction and Basics
  • Part 2: Deploy & Download
  • Part 3: The Client Side Object Model  (this post)
  • Part 4: Functions, Objects and Classes
  • Part 5: Avoid the Global Namespace *
  • Part 5: Download & Caching *
  • Part 6: Efficiency & Performance*
  • Part 7: Testing *
  • Part 8: Professional JavaScript Development *
  • Part 9: Error Handling *
  • Part 10: Advanced Topics *
  • Part 11: Security *
  • Part 12: Implementing Business Logic *
  • Part 13: Tools and Resources *
  • Part 14: Wrap-Up *
  • Bonus: JavaScript in SharePoint 2013 *

*: Coming Soon…

Synopsis and Key Take-Aways

This article introduces the JavaScript client-side object model (CSOM) added to SharePoint 2010, covering a discussion of when and why you would use the JavaScript CSOM and some important aspects that impact the performance and scalability of your application.  It finishes with a handful of examples of the CSOM in action performing typical (and perhaps not so typical) actions as well as links to other examples and reference material.

Introduction

If you’re new to the JavaScript CSOM, the best place to start learning about it is a decent overview.  You need to get an

Why Use the JavaScript CSOM?

There are a few key pieces of information about the JavaScript CSOM that will help to answer this question:

  • The code runs off of the SharePoint server in the users browser
  • The code can still interact with SharePoint at a pretty low level – retrieving content from SharePoint, creating and modifying content stored in SharePoint, modifying aspects of SharePoint itself (managing lists and libraries, pages web parts, etc.)
  • The code can be combined with other JavaScript libraries – most importantly JQuery

With that initial understanding, you can start to see benefits offered by the JavaScript CSOM: we can provide the rich, engaging user experience that our user’s want while still interacting with SharePoint in a way that maintains the integrity of our SharePoint server environment.

To give you an idea of what is possible before going into detail, here are a few things that can be done from the CSOM:

  • Full CRUD (Create, Read, Update & Delete) capabilities for List Items, Lists, Content Types, Webs and other key objects.
  • Add web parts to a page
  • Remove web parts form a page
  • Create new workflows associations for existing workflow templates
  • Work with user permissions for securable objects
  • Manage security groups
  • Activate/deactivate Features from a given scope
  • Manage fields on lists, libraries and content types
  • Work with files in document libraries
  • …and a whole lot more…

Why Would You NOT Use the JavaScript CSOM?

Not everything is available in the CSOM – some areas of SharePoint simply cannot be accessed by code utilizing the CSOM.  More on this in a moment.  In addition, some requirements are best met with server side code – see below for details.

What Can We Access?

In general, CSOM code can interact with SharePoint at the site collection level and below.  This means that the following are available:

  • SPSite (although we can’t reach outside of our current site collection)
  • SPWeb
  • SPList
  • SPDocumentLibrary
  • SPListItem
  • SPFile
  • SPField
  • SPUser
  • web parts
  • security
  • etc.

The The Official JavaScript Object Model Reference lists out all of the available objects.  It is important to note that not all aspects of some of these objects are available – some properties and methods available in the server side object model are blocked.  For example, you can access the Site object to get to a site collection, but not the SPSite constructor, which would allow you to connect to a different site collection; it is blocked.  One final note, the SPSecurity object is not available as well.  This means that you cannot do any type of elevation of privilege from the JavaScript CSOM.  All code runs as the current user.

When analyzing requirements to determine whether CSOM is an option, consider the level of access required.  In general, if it is within a single collection, you can probably do it with CSOM.

What Scenarios Don’t Work?

Even if you are only using allowed objects in your JavaScript code, there are some scenarios that just don’t lend themselves to client-side processing.  For example:

  • Processing large amounts of data – pulling all of that data across the wire down to the client may not be a good way to process it
  • Security concerns – pulling data across the wire to process it without some type of encryption (e.g. SSL) may present security problems
  • Code that needs to run whether users are connected to and browsing your site or not, for example workflows and timer jobs. 
  • Code that must respond to server-side events, such as after an item has been added to the database
  • Code that must run, and must run exactly as written.  This is a hard one.  User’s can turn off JavaScript.  Malicious users could also modify the cached version of the JavaScript file on their machine and run their modified code instead of the code you intended for them to run.  What happens to your data integrity and environment if your JavaScript code doesn’t run, or doesn’t run the way you expect it to run?  Understand that large portions of SharePoint will stop functioning or at least have a severely reduced level of functionality, but it is not impossible to use SharePoint with JavaScript turned off.  What happens to your environment then?  It seems to me that this is a largely ignored area, but that doesn’t mean it isn’t important.  Honestly, I don’t have a really good answer for this yet. It’s just an area you need to think about – what are the implications?

Requirements

To make use of the JavaScript CSOM in your SharePoint site, you need to make sure that the CSOM library is loaded by your page.  This will be done for you from the Master Page if you use one of the out-of-the-box  Master Pages.  If you use a custom Master Page, you must make sure that the sp.js file is loaded.  Part 2: Deploy & Download covers some aspects of this.  Additional material will be covered in later modules.

Advanced Topics

With a basic understanding of the JavaScript CSOM and what is and is not possible, we can move on to some more advanced topics.

Batch processing

All commands in the JavaScript CSOM are not executed immediately when the JavaScript engine in your browser evaluates the line of code.  Instead, the command is added to a batch and executed along with the other commands in the batch at a point in the future.  This is one of the fundamental paradigms of JavaScript CSOM programming that must be understood.  The hardest part of this whole operation is understanding when you must commit a batch and how to do so. 

At the most basic level, you must commit a batch of commands prior to interacting with the properties of any objects referenced in the commands.  An example will help to clarify this:

   1: function runCode() {

   2:   var ctx = new SP.ClientContext(); 

   3:   //Store the Announcements list in a variable:

   4:   var list = ctx.get_web().get_lists().getByTitle('Announcements');

   5:   //Store a list item in a variable

   6:   listItem = list.getItemById(4);

   7:   //Load the list item so we have access to its 

   8:   //properties

   9:   ctx.load(listItem);  

  10:   //To this point, we have not left the client and 

  11:   //none of our code has actually "done" anything.  

  12:   //All of the commands have simply been added to a 

  13:   //batch.  The next line sends the batch to the 

  14:   //server so all of our commands will execute.  

  15:   //After the following line has returned from its 

  16:   //call to the server, the list and list item we 

  17:   //"retrieved" are available for us to access and 

  18:   //work with its properties

  19:   ctx.executeQueryAsync(onQuerySucceeded, onQueryFailed);

  20: }

  21:  

  22: function onQuerySucceeded() {

  23:     alert('Item Title is ' + listItem.get_item('Title'));

  24: }

  25:  

  26: function onQueryFailed(sender, args) {

  27:   alert('Request failed.');

  28: }

Don’t worry about the details of what’s going on in that code sample for now.  We’ll look into those details later.  Read the comments to get an understanding of the code, but here are the things you need to know to understand the batch process:

  • All of the code from line 1 to line 18 runs on the client and has no interaction with the server.  For example, on line 4, the call to getItemById does not actually connect to the server and grab a reference to the SPListItem.  It simply adds the command(s) to the list of commands queued up to be committed (this is the “batch” referred to earlier)
  • Line 19 commits the batch to the server.  When this line executes, the batch is packaged up and sent across the wire to the server.  The server executes the code we’ve told it to run and returns a result to our page.
  • Line 19 also registers 2 callback methods – one for if the batch runs successfully (on lines 22-24), one if the batch fails (on lines 26-28).  After the batch has returned form the server, the appropriate callback method is run and the items collected in lines 1-18 (list & listItem) are available for use.
  • Line 23 references one of the properties from the listitem (specifically the Title property) that is now available to us because we have committed the batch to the server and gotten a populated ListItem back.

Why Batches?

The JavaScript CSOM code runs on the client machine, inside their browser.  As we saw above, it commits batches of commands to the server to be executed.  It then receives data from the server.  By operating in batches like this, the number of round trips across the wire is minimized.  Sending data to the server and getting a response is the slowest part of the CSOM; traversing the network is inherently slow.  The batch operation keeps our CSOM applications from being excessively chatty which would slow things down considerably.

Things to Know About Batching and Objects

The batch operations have a couple of quirks that are important to know.

Scalar and Collection Properties

Besides the fact that CSOM operates in batches like this, the only really important thing to know about the batched nature of CSOM operations is how we go about accessing objects and their properties in a CSOM application. 

For example, consider the following code:

var ctx = new SP.ClientContext(); 

var web = ctx.get_web();

ctx.load(web);

This code is responsible for filling up the (in this case) web object so that we can access its properties.  It is important to note, however, that not all properties of the object are filled up (populated) by this command.  It is only the scalar (i.e. non-collection) properties that will have data after a load command and its corresponding executeQueryAsyc command.  In other words, simple properties – ones with data types like string, int, bool, etc. are populated.  Properties that contain collections are not populated; they must be explicitly filled up.  In the case of the Web object, the following are examples of properties that would be populated after a call to load like we have in the code above:

  • id
  • rootFolder
  • serverRelativeUrl
  • title
  • etc.

These are all simple scalar properties.  (Note that this is not an exhaustive list of the scalar properties, and each object has its own collection of scalar properties.)

On the other hand, collection properties such as the following are not populated by the call to load shown above:

  • lists
  • folders
  • features
  • fields
  • availableContentTypes
  • etc.

These properties all represent a collection of items.  In order to populate these properties, we need to explicitly load the collection property:

var ctx = new SP.ClientContext(); 

var web = ctx.get_web();

var lists = web.get_lists();

ctx.load(lists);

ctx.executeQueryAsync(onQuerySucceeded, onQueryFailed); 

We can now work with the collection of lists from the Web.  Notice that we don’t need to load up the web object because we don’t access any of its properties, except the lists collection property, which we are explicitly loading.

If you don’t fill up a collection property and commit the batch before trying to access the collection property, you will get an error message that says The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.

Performance: Property/Column Values and RowLimit

A key tenet of querying SharePoint data server-side is to only pull from the database the minimum you need to accomplish your requirement.  In other words, don’t pull back all of the columns on a list item if you’re only going to work with, for example, the Title and Body column values.  A similar approach is important for retrieving content from SharePoint with the JavaScript CSOM, with one additional reason – as stated previously, sending data across the wire is expensive in terms of performance.  The more data that has to traverse the wire, the greater the performance impact.  Therefore, it is important to only extract and pull down the values we need to work with.  In the JavaScript CSOM, this is done in one of two ways:

  1. Listing the property names we need in the load statement
  2. Trimming our queries to retrieve only the columns we need

The latter is the same concept as what we’ve done using SPQuery for years, although the syntax is slightly different, as you’ll see in a moment.  The former is new to CSOM. Unlike the load statements shown above which do not specify the properties we need, and therefore pull down all properties, a more efficient load statement would look like this:

ctx.load(lists, 'Include(Title,DataSource)');  

This would limit the properties that are populated on our lists object to just the Title of each list and the DataSource. If you do limit the properties that are populated in this way, you need to make sure that you specify every property that you need.  Similar to the error above if a collection property is not populated, if you attempt to access a scalar property that has not been populated, you will get an error The property or field has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.

The syntax for trimming the columns sent down to the client is similar to what we use on the server, but just slightly different syntactically:

var ctx = new SP.ClientContext();

targetList = ctx.get_web().get_lists().getByTitle('Announcements');

query = new SP.CamlQuery();

query.set_viewXml("<View><ViewFields><FieldRef Name='Title'/></ViewFields><RowLimit>2</RowLimit></View>");

listItems = targetList.getItems(query);

ctx.load(listItems); 

ctx.executeQueryAsync(onQuerySucceeded, onQueryFailed);

You can see the way the ViewFields elements are set into the query, which is stored in the viewXml property of the CamlQuery object.  This example will only retrieve the Title column value from the database and send it down the wire.

This example also shows how to limit the number of records that are sent down to the client.  This is another important aspect of maintaining performance. In this example, the result set is limited to the first two records by use of the RowLimit element..

Examples of Real World Tasks

There are only a certain number of core operations that are typically going to be done via the JavaScript CSOM.  There are dozens or hundreds of other operations to perform in CSOM, but the majority of the time, your operations will involve the following in some form and then add the other operations on top of these.

NOTE: I wrote some of these samples for MSDN back during the beta of 2010 and some of them are available in the SDK, although I tweaked some of them a little and tested them all for this blog post.

Get ListItem By ID

If you know the ID of a ListItem, this sample demonstrates how to retrieve it.  It also demonstrates how to retrieve a List by name.

 

var itemId = 4;   

var targetListItem;

 

function runCode() {

  var ctx = new SP.ClientContext(); 

  var targetList = ctx.get_web().get_lists().getByTitle('Announcements');

  targetListItem = targetList.getItemById(itemId);

  ctx.load(targetListItem, 'Title');  

  ctx.executeQueryAsync(onQuerySucceeded, onQueryFailed);

}

 

function onQuerySucceeded() {

    alert('Request Succeeded. nRetrieved Item Title is ' + targetListItem.get_item('Title'));

}

 

function onQueryFailed(sender, args) {

  alert('Request failed. nError: ' + args.get_message() + 'nStackTrace: ' + args.get_stackTrace());

}

Submit a CAML Query

This sample shows you how to query a list using CAML and iterate through all items in a collection.

 

var listItems;

 

function runCode() {

  var ctx = new SP.ClientContext();

  var targetList = ctx.get_web().get_lists().getByTitle('Announcements');

  var query = new SP.CamlQuery();

  query.set_viewXml("<View><Query><Where><Contains><FieldRef Name='Title'/><Value Type='Text'>MyValue</Value></Contains></Where></Query></View>");

  listItems = targetList.getItems(query);

  ctx.load(listItems);

  ctx.executeQueryAsync(onQuerySucceeded, onQueryFailed);

}

 

function onQuerySucceeded() {

  var listEnumerator = listItems.getEnumerator();

  while (listEnumerator.moveNext()) {

    alert("Found matching Item! nTitle=" + listEnumerator.get_current().get_item("Title"));

  }  

}

 

function onQueryFailed(sender, args) {

  alert('Request failed. nError: ' + args.get_message() + 'nStackTrace: ' + args.get_stackTrace());

}

Paging Through a ListItemCollection

This sample pages through the items in a list two at a time, unlike the previous example which iterates through all items in the returned collection in one operation.

var listItems;                   //holds retrieved list items

var query;                       //must continue to reuse the same query object for paging to work

var targetList;                  //list from which to retrieve items

var ctx;

               

 

function runCode() {

  ctx = new SP.ClientContext();

  targetList = ctx.get_web().get_lists().getByTitle('Announcements');

  query = new SP.CamlQuery();

  query.set_viewXml("<View><ViewFields><FieldRef Name='Title'/></ViewFields><RowLimit>2</RowLimit></View>");

  listItems = targetList.getItems(query);

  ctx.load(listItems); 

  ctx.executeQueryAsync(onQuerySucceeded, onQueryFailed);

}

 

function onQuerySucceeded() {

  var message = "Found Matching Items! ";

  var listEnumerator = listItems.getEnumerator();

  while (listEnumerator.moveNext())  {

    message += "nTitle=" + listEnumerator.get_current().get_item("Title")

  }

  alert(message);

  var position = listItems.get_listItemCollectionPosition();

  if (position != null) {

    query.set_listItemCollectionPosition(position);

    listItems = targetList.getItems(query);

    ctx.load(listItems);  

    ctx.executeQueryAsync(onQuerySucceeded, this.onQueryFailed);

  }

  

}

 

function onQueryFailed(sender, args) {

  alert('Request failed. nError: ' + args.get_message() + 'nStackTrace: ' + args.get_stackTrace());

}

 

Retrieve a Web – #1

This sample just retrieves the current web (whichever Web hosts the page containing the JavaScript.)

var targetWeb;

 

function runCode() {

    var ctx = new SP.ClientContext();

    targetWeb = ctx.get_web();

    ctx.load(targetWeb);

    ctx.executeQueryAsync(onQuerySucceeded, onQueryFailed);

}

 

function onQuerySucceeded() {

    var message = "Web retrieved:";

    message += "n Title: " + targetWeb.get_title();

    message += "n ID: " + targetWeb.get_id();

    message += "n Language: " + targetWeb.get_language();

    message += "n UI Version: " + targetWeb.get_uiVersion();

    message += "n Description: " + targetWeb.get_description();

    message += "n Created: " + targetWeb.get_created();

    alert(message);

}

 

function onQueryFailed(sender, args) {

    alert('Request failed. nError: ' + args.get_message() + 'nStackTrace: ' + args.get_stackTrace());

}

Retrieve a Web – #2

This sample shows how to retrieve a Web based on its server-relative URL.  Note: the Web being retrieved must be in the same site collection.

var targetWeb;

 

function runCode() {

    var ctx = new SP.ClientContext('/relative_url');

    targetWeb = ctx.get_web();

    ctx.load(targetWeb);

    ctx.executeQueryAsync(onQuerySucceeded, onQueryFailed);

}

 

function onQuerySucceeded() {

    var message = "Web retrieved:";

    message += "n Title: " + targetWeb.get_title();

    message += "n ID: " + targetWeb.get_id();

    message += "n Language: " + targetWeb.get_language();

    message += "n UI Version: " + targetWeb.get_uiVersion();

    message += "n Description: " + targetWeb.get_description();

    message += "n Created: " + targetWeb.get_created();

    alert(message);

}

 

function onQueryFailed(sender, args) {

    alert('Request failed. nError: ' + args.get_message() + 'nStackTrace: ' + args.get_stackTrace());

}

Retrieve a Web – #3

This sample shows how to retrieve the site at the root of a site collection from a child web somewhere else in the site collection.

var targetWeb;

 

function runCode() {

    var ctx = new SP.ClientContext('/');

    targetWeb = ctx.get_web();

    ctx.load(targetWeb);

    ctx.executeQueryAsync(onQuerySucceeded, onQueryFailed);

}

 

function onQuerySucceeded() {

    var message = "Web retrieved:";

    message += "n Title: " + targetWeb.get_title();

    message += "n ID: " + targetWeb.get_id();

    message += "n Language: " + targetWeb.get_language();

    message += "n UI Version: " + targetWeb.get_uiVersion();

    message += "n Description: " + targetWeb.get_description();

    message += "n Created: " + targetWeb.get_created();

    alert(message);

}

 

function onQueryFailed(sender, args) {

    alert('Request failed. nError: ' + args.get_message() + 'nStackTrace: ' + args.get_stackTrace());

}

 

Additional Examples From the SDK

There’s a section in the SDK with some good examples of common tasks.  Rather than completely reinvent the wheel, I’ll just link to them:

Reference Material

SDK: The Official JavaScript Object Model Reference (object/class reference)

SDK: CSOM Introduction (covers all three client object models and gets pretty deep pretty fast, but not a bad read)

One thought on “The SharePoint JavaScript Object Model–Resources and Real World Examples

  1. Belay

    David – thank you for the great posts. They are very timely and immensely appreciated. When will you have the other posts coming?

Comments are closed.