Pages

Saturday, June 15, 2013

Creating JavaScript List Repositories for the SharePoint REST API

I've recently been working on a project where I've been leveraging the SharePoint 2010 REST API. It is a great experience especially after moving from SharePoint 2007 client side development. I've been able to develop web parts that are 100% client driven with no reliance on any server side code other than the REST services that SharePoint already exposes.

As I began developing I quickly noticed that calls to jQuery ajax and getJSON functions were littering my code. Of course, these calls are completely necessary, but I wanted to find a way to reduce duplication, increase reuse, and create a better programming experience for the rest of the effort. I decided to take these calls and create a wrapper object that would be responsible for the plumbing of calling my lists. What I ended up with was an object very similar to a traditional repository used in other languages.

The object looks like this:

var TCSC = TCSC || {};

TCSC.SharePoint = TCSC.SharePoint || {};

TCSC.SharePoint.ListRepository = function (listName) {

    if (this instanceof TCSC.SharePoint.ListRepository) {

        this.url = window.location.protocol + "//" + window.location.host + "/_vti_bin/ListData.svc/" + listName;

    } else {
        return new TCSC.SharePoint.ListRepository(listName);
    }
};

TCSC.SharePoint.ListRepository.prototype = function () {

    function addParameters(url, parameters) {

        if (url !== undefined && url !== '' && parameters !== undefined && parameters !== '') {
            return url + '?' + parameters;
        } else {
            return url;
    }

    function deleteItem(id) {
        var itemUrl = this.url + "(" + id + ")";
        return jQuery.ajax({
            url: itemUrl,
            type: "DELETE"
        });
    }

    function insertItem(item) {
        return jQuery.ajax({
            url: this.url,
            type: "POST",
            contentType: 'application/json',
            data: JSON.stringify(item)
        });
    }

    function selectAll(parameters) {
        var listUrl = addParameters(this.url, parameters);
        return jQuery.getJSON(listUrl);
    }

    function selectById(id, parameters) {
        var itemUrl = addParameters(this.url + "(" + id + ")", parameters);
        return jQuery.getJSON(itemUrl);
    }

    function updateItem(item) {
        var itemUrl = this.url + "(" + item.Id + ")";
        var etag = item.ETag;

        delete item["ETag"];

        beforeSendFunction = function (xhr) {
            xhr.setRequestHeader("If-Match", etag);
            xhr.setRequestHeader("X-HTTP-Method", 'MERGE');
        }

        return jQuery.ajax({
            url: itemUrl,
            type: "POST",
            contentType: 'application/json',
            beforeSend: beforeSendFunction,
            processData: false,
            data: JSON.stringify(item)
        });
    }

    return {
        deleteItem: deleteItem,
        insertItem: insertItem,
        selectAll: selectAll,
        selectById: selectById,
        updateItem: updateItem
    }

} ();


Namespace objects are created to house our repository definitions. We then create a constructor function that takes the name of the SharePoint List that we want to work with and assign it to a local variable. We then use the revealing module pattern to create an object to assign to our ListRepositories prototype. The revealing module pattern is too deep of a subject to cover in this post, so here are some links that cover what it is all about:

Module Pattern:
http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript

Revealing Module Pattern:
http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript
http://www.bobholt.me/2012/08/using-the-revealing-module-pattern/

The gist of why I decided to use the revealing module pattern is that I wanted to create a functional JavaScript object that provides all of the functionality that the repository needs, but hides all of the plumbing while exposes the necessary functions in a clean and simple manner. The revealing module pattern makes this possible.

After assignment, the prototype object contains all of the functions that we want to expose for use in our repository.

To utilize the repository it is as simple as “newing” up a new repository object and calling a function….

var repository = new TCSC.SharePoint.ListRepository(“People”);
repository.selectAll().done(function(data) {
    var people = data;
});


In this case I'm intentionally returning the jQuery Deferred objects because they give you more control over your processing. I initially attempted to populate "properties" on the object, but I found that approach quickly got out of hand. Using the deferred objects allows you to chain your operations together while still taking advantage of all of the asynchronous goodness that JavaScript has to offer. Again, the Deferred object is too complex to go into detail, so here are some links that should provide some background:

http://api.jquery.com/category/deferred-object/
http://api.jquery.com/jQuery.Deferred/
http://eng.wealthfront.com/2012/12/jquerydeferred-is-most-important-client.html

HINT: if you need to access a lists Choice Field using the REST API, you call the REST functions using the List Name and Field Name concatenated together (ex. List Name-Person, Choice Field Name-Gender --- call using PersonGender).

I hope this helps!

Note: This posts revolves around the SharePoint 2010 REST API, but all of the concepts covered can be easily ported to SharePoint 2013.

Another Note: I recently had an interesting conversation with a colleague about whether or not SharePoint 2010's listdata.svc is an actual REST implementation. While it uses HTTP Methods and much of it's convention follows traditional REST API's, I think I agree with him that technically it is not a true REST API... at least not in the sense that the 2013 REST API is. But, that being said, much of the web refers to this functionality as the SharePoint 2010 REST API, and in this blog past, I am going to follow suit.

No comments:

Post a Comment