Sunday, 21 November 2010

SP2010 AJAX part 4 – returning JSON from a HTTP handler

  1. Boiling jQuery down to the essentials (technique)
  2. Using the JavaScript Client OM to work with lists (technique)
  3. Using jQuery AJAX with a HTTP handler (technique)
  4. Returning JSON from a HTTP handler (technique) - this article
  5. Enable Intellisense for Client OM and jQuery (tip)
  6. Debugging jQuery/JavaScript (tip)
  7. Useful tools when building AJAX applications (tip)
  8. Migrating existing applications to jQuery/AJAX

In the last post in this series, we discussed using a HTTP handler with AJAX - a highly useful technique when building SharePoint (and plain .Net) applications which aren’t postback-heavy, and as I said I believe that’s the most important post in this series. In the example there, my HTTP handler returned a simple string saying “Returned from handler at “ + DateTime.Now() – strings are fine of course (and remember that the value returning by a HTTP handler is always a string, so we’re somewhat constrained by that) but often we want something a bit more structured. This could be for one of the following reasons:

  • You’re returning many small pieces of information from one call, and putting them in say, a separated string and then parsing on the client is, um, nasty
  • You already have an object in code on the server, and frankly it would be nice if you could just work with that on the client too

In such cases, using JSON as the format for your return data could be a good choice, and is indeed what Microsoft’s own Client OM uses. Even better, you don’t really need to know much about JSON and it’s extremely easy to use. Aside from the basic need to return structured data, some reasons why you might choose JSON (over XML for example) might be:

  • JSON is more compact than XML
  • JSON is (arguably) simpler to consume in your JavaScript than XML
  • JSON has some useful tooling support e.g. Firebug

JSON 101

JSON is JavaScript Object Notation, and is simply a format for describing name/value pairs. They can be nested too, which means it can represent reasonably complex objects - in the end it’s always one big string when it goes over the AJAX wire, but effectively it’s easy to work with at either end. To show what it looks like, consider a class like this:

public class Employee
{
    public int ID
    {
        get;
        set;
    }
 
    public string Name
    {
        get;
        set;
    }
 
    public Address HomeAddress
    {
        get;
        set;
    }
 
    public Salary Salary
    {
        get;
        set;
    }
}

An Employee instance represented as JSON would look like this:

{"ID":1,"Name":"Chris","HomeAddress":{"HouseNumber":55,"StreetName":"Acacia Avenue","City":"Birmingham","Postcode":"B26 8LM"},"Salary":{"Currency":"Sterling","Amount":100}}

Now clearly that looks like a horrible string to construct yourself, but of course since JSON is a common format  you don’t have to. In the same way that an object can be serialized to XML in .Net using the XmlSerializer class, you can do the same to JSON format using the JavaScriptSerializer class. To send our Employee data to the client so that the page can be updated (e.g. using the jQuery page manipulation techniques from part 1), we would use the JavaScriptSerializer class in our HTTP handler, like so:

public void ProcessRequest(HttpContext context)
{
    Employee emp = getEmployee();
 
    // serialize and send..
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    StringBuilder sbJsonResults = new StringBuilder();
    serializer.Serialize(emp, sbJsonResults);
 
    context.Response.Clear();
    context.Response.ContentType = "application/json; charset=utf-8";
    context.Response.Write(sbJsonResults.ToString());
}

Note that I’m setting the MIME type of the response header to “application/json” – this enables your app and any tools to recognise the string as a JSON string, and thus potentially provide any JSON-specific support. I’ll show an example shortly of how Firebug uses this to help you see the JSON coming from the server.

Consuming JSON in JavaScript code

As we noted earlier, JSON is ideal when we are dealing with many small pieces of information, like all the properties of an Employee. In the image below, I have an area on the page with many DIVs and SPANs to present the information – these get populated when the AJAX call to the server completes:

 JsonResults
The HTML and jQuery code to join up with the server side looks like this:

<fieldset id="fldDemo1">
    <legend>HTTP handler which returns JSON</legend>
    <div id="jsonDemo1Row" class="demoRow">
            <div class="demoControls">
                <button id="btnDemo1" type="button">Call handler</button>
            </div>
            <div class="demoResults">
                <div><span class="employeeField">Employee name: </span><span id="empName" /></div>
                <div><span class="employeeField">Salary: </span></div>
                    <div><span class="employeeSubField">Amount: </span><span id="empSalaryAmount" /></div>
                    <div><span class="employeeSubField">Currency: </span><span id="empSalaryCurrency" /></div>
                <div><span class="employeeField">Address: </span></div>
                    <div><span class="employeeSubField">House number: </span><span id="empAddressHouseNumber" /></div>
                    <div><span class="employeeSubField">Street: </span><span id="empAddressStreet" /></div>
                    <div><span class="employeeSubField">City: </span><span id="empAddressCity" /></div>
                    <div><span class="employeeSubField">Postcode: </span><span id="empAddressPostcode" /></div>
            </div>
    </div>
</fieldset>
<script type="text/javascript">
    $('#btnDemo1').click(function () {
        $.getJSON('/_layouts/COB/DemoJsonHandler.json',
        function (data) {
         $('#empName').html(data.Name);
            $('#empSalaryAmount').html(data.Salary.Amount);
            $('#empSalaryCurrency').html(data.Salary.Currency);
            $('#empAddressHouseNumber').html(data.HomeAddress.HouseNumber);
            $('#empAddressStreet').html(data.HomeAddress.StreetName);
            $('#empAddressCity').html(data.HomeAddress.City);
            $('#empAddressPostcode').html(data.HomeAddress.Postcode);
        });
    });
</script>

A couple of things to note here:

  • The JSON value is passed to the success callback of the AJAX call as the data parameter.
  • I can just use dot notation to access a property as if I was in C#/VB.Net/whatever (e.g. data.Salary.Amount) – no dodgy parsing for each value (woohoo!).
    • However, don’t expect any Intellisense here! Currently there’s no way for the tools to infer members on JavaScript objects you create yourself (jQuery and the SharePoint Client OM do it by way of a special documentation file - the next article in this series shows how to enable Intellisense for those). Given the move away from .Net web forms apps towards AJAX however, I don’t doubt that VS2013 (or whatever) may well have this.
  • Notice that jQuery has a convenient $.getJSON() method when you know that the server call is going to be returning JSON.
    • This is just like the $.get()/$.post()/$.ajax() methods we’ve looked at previously in this series, but this method performs an additional step of parsing the response to check it’s valid JSON. This let’s you handle such an error in a nice way (though note you need to use $.ajaxError() or $.ajaxSetup() to do this, rather than a failure callback on this call). Although we wouldn’t necessarily expect this to happen when we’ve used the JsonSerializer to build the JSON in the first place, in any case it’s much better than getting an obscure lower down problem e.g. when trying to get a piece of data from the Employee object.
  • The extension of my handler is .json – it doesn’t have to be, but if you’re happy to have a web.config entry (see my last article, Using jQuery AJAX with a HTTP handler for more discussion around this) then you can deviate from the standard .ashx which .Net automatically hooks up for you.

Tool support for JSON

It might not be a life-changing thing, but something very popular with developers using JSON in this way is the ‘Console’ tab in Firebug (which I briefly showed in the last article). Importantly, it allows me to see the ‘raw’ response coming back from a web service or AJAX call from the client - in the case of JSON, this is of course somewhat difficult to decipher:

RawJsonInFirebug
However, in contrast to Fiddler or many other tools, Firebug (via the Console > JSON tab) allows you to see a nice ‘formatted’ view of your JSON object, and you can expand/contract complex properties to see their values - much easier to digest:

JsonInFirebug

Final note - alternatives to JSON

Of course, JSON won’t always be the answer to passing data between the server and the client – sometimes you’ll prefer to work with XML, so here’s a good tutorial on parsing XML with jQuery.


Next time: Enable Intellisense for Client OM and jQuery (tip)

Friday, 5 November 2010

SP2010 AJAX part 3– using jQuery AJAX with a HTTP handler

  1. Boiling jQuery down to the essentials (technique)
  2. Using the JavaScript Client OM to work with lists (technique)
  3. Using jQuery AJAX with a HTTP handler (technique) - this article
  4. Returning JSON from a HTTP handler (technique)
  5. Enable Intellisense for Client OM and jQuery (tip)
  6. Debugging jQuery/JavaScript (tip)
  7. Useful tools when building AJAX applications (tip)
  8. Migrating existing applications to jQuery/AJAX

So far we’ve looked at jQuery for page manipulation and using the Client OM for talking to SharePoint. Today I want to talk about something else, and I think this is possibly the most important post in this series. jQuery and the Client OM are unlikely to deal with every scenario the AJAX-minded SharePoint developer will need to deal with. To be sure, the Client OM is surprisingly extensive and deals with way more than just fetching items from lists – that said, it’s not a full mirror of the server API and in any case, when writing server code which talks to client code (e.g. JavaScript)  sometimes you need full control over both sides of the fence. So, how do you build an AJAX-style  SharePoint application without using the Client OM (for whatever reason)?

Let’s consider the server side first - there are a couple of options: 

  • Write a WCF service (or indeed, ‘classic’ .asmx web service)
  • Write a HTTP handler
  • Write some other form of server code which (like the others) generates a response over HTTP
    • e.g. technically a good old .aspx page could work as the server-side component. However, this is a bad choice compared to the other options for a few reasons (mentioned shortly)

Arguably the purist route would be to develop a WCF service – indeed SharePoint 2010’s implementation of the Client OM is a WCF service. If you’re experienced with WCF, this is probably a good choice for you. However, my view is that a HTTP handler is a hundred times simpler, and that the features of WCF (e.g. transport flexibility, ability to pass large files, security etc.) are typically not necessary in an “internal to the farm” SP2010/AJAX app. Of course, if you’re building a public API to expose stock quotes to the world then things might be different. Alternatively, an esoteric approach could be to use something like a regular .aspx page – however an ASP.Net web forms page (i.e. not MVC) will be less efficient here as the page will go through the full ASP.Net lifecycle (Init, OnPreRender, Render etc.) despite the fact you probably don’t have any ASP.Net controls on the page. In other words we are using ASP.Net pages for something other than what they were designed.

So, a HTTP handler is a simple and effective means of  building an AJAX-style app. To some folks it’s a new technique, and for others it’s old news – but it’s my view that this approach is THE key to unlocking the ability to write AJAX apps, whether that’s on top of SharePoint or plain .Net.

The great thing about this technique is that the possibilities are unlimited – you can AJAX-ify anything, since you would write the C# code in the handler, then just call it from jQuery. Clearly this cannot be said about a ‘provided’ API such as the Client OM. By the way, I’d be interested to hear opposing opinions on the WCF vs. HTTP handler point.

In terms of the client, if we are talking about JavaScript (as we are in this series), then we need a way of calling ‘HTTP resources’ like those mentioned above from a JavaScript method. This has been possible for years, but the advent of jQuery means it’s way simpler than before – jQuery provides a handful of AJAX methods to call a server resource by URL, and the response is passed back to the JavaScript. Once you have the value from the server call (more on this later), you can display feedback to your user without reloading the page by simply using the methods described in part 1 (essential jQuery methods for updating pages). The jQuery AJAX methods are:

$.get() Requests a URL using a HTTP GET – calls .ajax() underneath
$.post() Requests a URL using a HTTP POST – calls .ajax() underneath
$.ajax() Allows full control – exposes full set of parameters
$.getJson() Requests a URL GET and parses response for JSON format using $.parseJSON

Creating a HTTP handler (server)

So, we’ve hopefully established that a HTTP handler combined with jQuery’s AJAX methods is a powerful technique – now let’s look at the detail.

HTTP handler

A HTTP handler is a .Net class which implements System.Web.IHttpHandler – the ProcessRequest() method is the main method. Here, you write code which determines what is returned when the URL for the handler is requested. So where a URL to an .aspx page would return HTML, you can return whatever you want – a simple string, some more complex XML, or perhaps JSON (the next article in this series). A simple handler returning a string looks like this:

using System;
using System.Web;
 
namespace COB.SPSaturday.Demos.Handlers
{
    public class DemoHandler : IHttpHandler
    {
        public bool IsReusable
        {
            // Return false in case your Managed Handler cannot be reused for another request.
            // Usually this would be false in case you have some state information preserved per request.
            get { return true; }
        }
 
        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("From the handler at " + DateTime.Now);
        }
    }
}

The .Net framework needs to know about such a handler before it can be used. Two options here – either associate an .ashx file with your .cs file above (the handler is called by the path to the .ashx in this scenario), or add a web.config entry telling .Net how to route the request to your class (the handler is called by whatever path you define in web.config in this scenario).

Using an .ashx

Create a file in your project with an .ashx extension (there is no VS item template, unless I keep missing it), and use the WebHandler directive to point to your implementation. This works because .ashx is a special extension where .Net knows to resolve the class by looking for the WebHandler directive:

<%@ Assembly Name="COB.SPSaturday.Demos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64" %>
<%@ WebHandler Language="C#" Class="COB.SPSaturday.Demos.Handlers.DemoHandler" CodeBehind="DemoHandler.cs" %>
Using a web.config entry

Add an entry (e.g. via SPWebConfigModification) like this – notice you can specify a custom path/extension if that’s preferable for any reason:

<handlers>
    <!-- other handlers here -->
    <add name="DemoHandler" path="/_layouts/COB/DemoHandler.cob" verb="*" type="COB.SPSaturday.Demos.Handlers.DemoHandler, COB.SPSaturday.Demos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64" />
</handlers>

Most folks opt for the .ashx route since it avoids defining something in web.config for every handler your application uses.

Calling a HTTP handler (client)

Earlier, we detailed jQuery’s four core methods for making a request to the server. Here’s an example (using the .get() method):

<fieldset id="fldDemo1">
    <legend>Demo 1 - simple handler</legend>
    <div id="demo1Row" class="demoRow">
            <div class="demoControls">
                <button id="btnDemo1" type="button">Call handler</button>
            </div>
            <div class="demoResults">
                <span id="demo1Result" />
            </div>
            <div class="clearer" />
    </div>
    </fieldset>
    <script type="text/javascript">
    $('#btnDemo1').click(function () {
        $.get('/_layouts/COB/DemoHandler.cob',
            function (data) {
                $('#demo1Result').html(data);
            });
    });
    </script>

As you can see, the key is taking the data property which is passed from the server to the jQuery callback, and using jQuery’s ability to manipulate the page with methods like .html(). Hey presto, you can now do anything you like without a full postback!

Something to note is that if you’re modifying data in the handler, a good practice is to make it a .post() request rather than .get() – of course, SharePoint disallows updates on a GET request by default so you must do something to work around this anyway (the ghetto alternative being to set SPWeb.AllowUnsafeUpdates = true).

Putting it into practice

Now that we’ve learnt the mechanisms, consider how usage might pan out in the real-world:

  • Return data:
    • If you’re returning unstructured data (e.g. a string), things are pretty simple (N.B. another jQuery AJAX method I omitted to mention is $.load() method which simplifies things further)
    • For structured data (e.g. objects), this is often returned as a JSON-serialized string – commonly preferred to XML, but there are plenty of samples out there for XML too
    • Because setting the HTML of a page element is so easy, it’s tempting to have a handler return a huge string of hardcoded HTML (e.g. when we’re updating a big complex area of the page) – indeed I mentioned this as a “SP2010/jQuery/AJAX survivor’s technique” in my SharePoint Saturday talk. This works fine but a far better approach is to use the new jQuery templates capability – Jan Tielen’s Getting Started with jQuery Templates and SharePoint 2010 is a great post.
  • Factoring
    • A good way to structure your code is to have a handler implement multiple methods (e.g. for related functionality), and pass a querystring parameter in the AJAX request to indicate which method to execute. You certainly don’t need a handler for every method you might implement.

Next time - Returning JSON from a HTTP handler

Wednesday, 20 October 2010

SP2010 AJAX–Part 2: Using the JavaScript Client OM + jQuery to work with lists

  1. Boiling jQuery down to the essentials (technique)
  2. Using the JavaScript Client OM to work with lists (technique) - this article
  3. Using jQuery AJAX with a HTTP handler (technique)
  4. Returning JSON from a HTTP handler (technique)
  5. Enable Intellisense for Client OM and jQuery (tip)
  6. Debugging jQuery/JavaScript (tip)
  7. Useful tools when building AJAX applications (tip)
  8. Migrating existing applications to jQuery/AJAX

Having looked at essential jQuery techniques to update the page last time, now we start to talk to SharePoint. Of course, the Client Object Model is an obvious tool for developers looking to build AJAX applications on SharePoint 2010 – it’s whole purpose is to make it simpler to call server methods from JavaScript or Silverlight, or ‘offline’ .Net code in the case of the Managed Client OM, and work with the SharePoint in those environments. It does this by providing a layer which takes care of the complex bits of calling SharePoint web services from e.g. JavaScript. This means building SharePoint applications which aren’t posting back to the server/fully refreshing the page is easier than it would be otherwise. This articles walks through some examples and illustrates some important performance tips.

Note that this article focuses on the JavaScript/ECMAScript flavor of the Client OM. The other two flavors (Silverlight and Managed Code) have slightly different syntax, so if you are cross-referencing MSDN/other articles, make sure you’re looking at the right thing!

Some key things to consider if you’re looking at the Client OM for the first time:

  • Async programming model – the basic sequence of steps is to get a reference to the ClientContext (similar to SPContext), read/update data, then call ClientContext.ExecuteQueryAsync(). This last method is what triggers things to actually happen (i.e. the request to the server), and you pass 2 ‘callback’ method names to run on success/failure.
    • This is similar to other disconnected programming, and also jQuery’s AJAX methods which we’ll look at next article
  • Calling .Load() on objects you will use – although the SharePoint object hierarchy is intentionally the same as on the server, one difference is that you must call ClientContext.Load(myJavaScriptVariable) on every variable which represents a SharePoint object (site/web/list/list item/something else) you wish to use. This ensures the details for that object are passed to the client. However, we’ll also see later that you should *only* call ClientContext.Load() on objects you genuinely do need to.

Examples

  1.   Fetching some data (title of current web)

    A simple example to start with – we’ll retrieve the title of the current web in jQuery (a jQuery button click event) and display it in a JavaScript alert box. This is about as simple as it gets, which is perfect for illustrating the callback model. One important ‘pattern’ thing to notice is that we’re declaring a global JavaScript variable which gets loaded, and because it’s global is also available in the success callback: 

    ClientOM_Demo1_Off
    ClientOM_Demo1_On
    <fieldset id="fldDemo1">
        <legend>Demo 1 - fetching web title</legend>
        <div id="Div1" class="demoRow">
                <div class="demoControls">
                    <button id="btnDemo1" type="button">Fetch web title</button>
                </div>
        </div>
    </fieldset>
    <script type="text/javascript">
        var currentWeb;
     
        $('#btnDemo1').click(function () {
            var ctx = new SP.ClientContext.get_current();
     
            currentWeb = ctx.get_web();
            ctx.load(currentWeb);
     
            ctx.executeQueryAsync(getWebSuccess, getWebFailure);
        });
     
        function getWebSuccess(sender, args) {
            alert('Title of current web is: ' + currentWeb.get_title());
        }
     
        function getWebFailure(sender, args) {
            alert('Failed to get web. \nError: ' + args.get_message() + '\nStackTrace: ' + args.get_stackTrace());
        }
    </script>
  2.  Fetching items from a list/library

    Something we may definitely want to do at some point with the Client OM is retrieve the items in a list or library. The same pattern is used but we effectively navigate down the SharePoint hierarchy to the list in question, and then run a CAML query (no LINQ in the Client OM). Notice that there is no ‘indexer’ property to get a single list on the client – we call .get_lists() to return all the lists in the web, then call .getByTitle(‘My list name’) on the resulting collection – a good example of the occasional difference compared to the server-side object model. In the callback, we have a collection we can enumerate through, with each item having a property named ‘FileLeafRef’ (which you may have seen previously in CAML queries) for the filename:

    ClientOM_Demo2_Off
    ClientOM_Demo2_On
    <fieldset id="fldDemo2">
        <legend>Demo 2 - fetching list items</legend>
        <div id="demo2Row" class="demoRow">
                <div class="demoControls">
                    <button id="btnDemo2" type="button">Fetch items</button>
                </div>
                <div class="demoResults">
                    <span id="demo2Result" />
                </div>
                <div class="clearer" />
        </div>
    </fieldset>
    <script type="text/javascript">
        var allDocs;
     
        $('#btnDemo2').click(function () {
            var ctx = new SP.ClientContext.get_current();
     
            var targetList = ctx.get_web().get_lists().getByTitle('Shared Documents');
            var query = SP.CamlQuery.createAllItemsQuery();
     
            allDocs = targetList.getItems(query);
            ctx.load(allDocs);
     
            ctx.executeQueryAsync(Function.createDelegate(this, getDocsAllItemsSuccess), 
                Function.createDelegate(this, getDocsAllItemsFailure));
        });
     
        function getDocsAllItemsSuccess(sender, args) {
            var listEnumerator = allDocs.getEnumerator();
            while (listEnumerator.moveNext()) {
                $('#demo2Result').append(listEnumerator.get_current().get_item("FileLeafRef") + '<br />');
            }
        }
     
        function getDocsAllItemsFailure(sender, args) {
            alert('Failed to get list items. \nError: ' + args.get_message() + '\nStackTrace: ' + args.get_stackTrace());
        }
    </script>

    A couple of other things to notice about this example:

    - I’m using the slightly more complex syntax of Function.createDelegate(this, myFunctionName) with ClientContext.ExecuteQueryAsync(). The MSDN samples use this syntax, but I understand it’s not strictly necessary and does clutter the code somewhat. Certainly my testing shows that the code still works and the args/sender variables are still passed to the callbacks, which is something I’d wondered about.
    - The SP.CamlQuery.createAllItemsQuery() method provides a handy shortcut in the Client OM to creating a CAML query for all the items in the list.

  3.  Fetching list items with query (type-ahead)

    Let’s turn the last example into something more useful and cool – a list of items on the page which dynamically filters as you type the filename into a box. This is an extremely useful pattern to understand, and is essentially the same mechanism for many cool things you may have seen e.g. Google Instant Search. In fact we only need to change a couple of things to enable this – instead of a button click event we respond to the textbox’s keyup event, and instead of a CAML query which returns all items, we need a CONTAINS query which drops the value from the text box into the clause:

    ClientOM_Demo3_On1
    ClientOM_Demo3_On2
    <fieldset id="fldDemo3">
        <legend>Demo 3 - fetching list items with query</legend>
        <div id="demo3Row" class="demoRow">
                <div class="demoControls">
                    <label for="txtFilenameContains">Filename contains:</label>
                    <input type="text" id="txtFilenameContains" />
                </div>
                <div class="demoResults">
                    <span id="demo3Result" />
                </div>
                <div class="clearer" />
        </div>
    </fieldset>
    <script type="text/javascript">
        var selectedDocs;
     
        $('#txtFilenameContains').keyup(function (event) {
            filterDocs();
        });
     
        function filterDocs() {
            var ctx = new SP.ClientContext.get_current();
     
            var docLib = ctx.get_web().get_lists().getByTitle('Shared Documents');
            var query = new SP.CamlQuery();
            query.set_viewXml("<View><Query><Where><Contains><FieldRef Name='FileLeafRef'/><Value Type='Text'>" + $('#txtFilenameContains').val() + "</Value></Contains></Where></Query></View>");
     
            selectedDocs = docLib.getItems(query);
            ctx.load(selectedDocs);
     
            ctx.executeQueryAsync(getDocsWithQuerySuccess, getDocsWithQueryFailure);
        }
     
        function getDocsWithQuerySuccess(sender, args) {
            $('#demo3Result').empty();
            var listEnumerator = selectedDocs.getEnumerator();
            while (listEnumerator.moveNext()) {
                $('#demo3Result').append(listEnumerator.get_current().get_item("FileLeafRef") + '<br />');
            }
        }
     
        function getDocsWithQueryFailure(sender, args) {
            alert('Failed to get list items. \nError: ' + args.get_message() + '\nStackTrace: ' + args.get_stackTrace());
        }
    </script>

  4.  Add new list items to a list

    Adding new data to SharePoint generally revolves around creating a somethingCreationInformation object (e.g. WebCreationInformation, ListCreationInformation etc.) in JavaScript, populating values for the item, then calling .update() and .executeQueryAsync(). In this example I’m using the ListItemCreationInformation object to add a new item to the ‘Tasks’ list in the current web, based on a title entered into a textbox:

    ClientOM_Demo4_Off
    ClientOM_Demo4_On1
    ClientOM_Demo4_Result
    <fieldset id="fldDemo4">
        <legend>Demo 4 - add list items</legend>
        <div id="demo4Row" class="demoRow">
            <div><span class="demoLabel">Task title:</span><input id="txtTaskTitle" type="text" /></div>
            <div><button id="btnAddTask" type="button">Add task</button></div>
            <div><span class="demoLabel">Result:</span><span id="addResult" /></div>
        </div>
    </fieldset>
    <script type="text/javascript">
        var newTask;
     
        $('#btnAddTask').click(function () {
            var taskTitle = $('#txtTaskTitle').val();
            var taskDesc = $('#txtTaskDescription').val();
     
            var ctx = new SP.ClientContext.get_current();
            var taskList = ctx.get_web().get_lists().getByTitle('Tasks');
            // use ListItemCreationInformation to provide values..
            var taskItemInfo = new SP.ListItemCreationInformation();
            newTask = taskList.addItem(taskItemInfo);
            newTask.set_item('Title', taskTitle);
            // could set other fields here in same way..
            newTask.update();
     
            ctx.load(newTask);
            ctx.executeQueryAsync(addTaskSuccess, addTaskFailure);
     
            function addTaskSuccess(sender, args) {
                $('#addResult').html("Task " + newTask.get_item('Title') + " added to the Tasks list");
            }
     
            function addTaskFailure(sender, args) {
                alert('Failed to add new task. \nError: ' + args.get_message() + '\nStackTrace: ' + args.get_stackTrace());
            }
        });
    </script>

Performance/writing efficient Client OM code

Now that we understand the basics of the JavaScript Client OM, we should be aware that how we write the code can have a dramatic impact on performance. We mentioned earlier that you should only call ClientContext.Load() on objects you will actually use, and the always-excellent Steve Peschka mentions this in his series which focuses on the Managed Client OM. Essentially the more objects you call .Load() on, the more data goes over the wire to the client – my preferred way of looking at this is in Firebug (a Firefox add-on), but Fiddler works fine too. Both will show the JSON-formatted response (N.B. JSON is something I discuss later in this series):

ViewingJsonResponse
So what kind of things make a difference? Well, there are two main ones:

  • Calling .Load() on objects where it’s not needed
  • Returning more properties (e.g. fields for a list item) than are needed

For the first, consider that on our way to get some lists items we can write some code to get a list in two ways:

// bad way..
var web = ctx.get_web();
var lists = ctx.get_web().get_lists();
var targetList = lists.getByTitle('Shared Documents');
ctx.load(web);
ctx.load(lists);
ctx.load(targetList);
/* although we can't see the surrounding code, the 'web' and 'lists' objects 
   actually weren't used on the client for anything.. */
// better way:
var targetList = ctx.get_web().get_lists().getByTitle('Shared Documents');

In both cases, we would later have a ctx.load(listItems) line. However in the second case, the JSON for the web, lists and target list are not sent over the wire – only the JSON for actual list items. This will cut down the data significantly (I’ll show numbers later).

For the second (returning more properties for an object than are needed), this is obviously analagous to a SQL SELECT * vs SELECT [mySingleColumn] query. We haven’t yet shown how to filter the properties returned, but it’s very important – first let’s look at what we have been doing so far:

var query = SP.CamlQuery.createAllItemsQuery();
demo5listItems = targetList.getItems(query);
// bad way - requesting all properties here..
ctx.load(demo5listItems);

The syntax for the limiting the fields returned in such a query in the JavaScript Client OM looks like this – here I’m just getting the filename only:

var query = SP.CamlQuery.createAllItemsQuery();
demo5listItems = targetList.getItems(query);
// better way:
ctx.load(demo5listItems, 'Include(FileLeafRef)');

When I was testing the different code patterns, this is what I saw – note I was only testing on a query which returned 8 items:

image

My observations from this are:

  • None of these numbers are scary, but then again we’re talking a mere 8 items here! However, the proportions are very interesting, and if (say) 100 items were being returned then clearly the amount of data would mean big benefits from writing code the right way
  • Proportions - the largest data set is 15 times the size of the smallest
  • The automatic compression in the Client OM saves your ass. The ‘Uncompressed size’ column is technically irrelevant as data is always compressed over the wire – however at some point it exists decompressed, and we can see how big the data really is here (61KB for 8 list items would be very bad indeed).

So to reiterate, only call ClientContext.Load() on objects you need to and be sure to restrict the properties returned. Hopefully though you can see that the Client OM and jQuery are great tools for building SharePoint apps which aren’t postback hell.

Next time: Using jQuery AJAX with a HTTP handler