Single-Page Applications (SPAs) in SharePoint Using SPServices – Part 3 – GetListItemChanges

In building a Single Page Application (SPA), we’ll usually want to keep the data we’re displaying up to date. You can probably think of many examples where you see this on the Web, but newsfeeds are a prime example. While we’re sitting on the page, we see newly posted content pop up, usually on the top of the feed. To do this, we can simply set up a call to run at fixed intervals using JavaScript’s timing functions setTimeout() or setInterval() to pull back the data.

If the content we want is in a list, it’s “expensive” to request all of the items at each interval. Instead, it’s much better to either cache the items if they rarely change – as is available in SPServices 0.7.2+, though the approach in 2013.01 is much improved – or to request only those items which have changed since the last request.

There are two operations which can help with this: GetListItemChanges and GetListItemChangesWithToken.

[important]GetListItemChanges and GetListItemChangesWithToken do not work in versions of SPServices before 2013.02.[/important]

In this post, let’s take a look at the GetListItemChanges operation.

GetListItemChanges

GetListItemChanges retrieves changes to the list since a specified date/time. The parameters are similar to those for GetListItems, with a few additions:

[webURL]

See the GetListItems post in this series.

listName

See the GetListItems post in this series.

viewFields

See the GetListItems post in this series.

since

A date/time specified in Coordinated Universal Time (UTC) ISO8601 format. What that means is that the date/time has to look like “2013-06-30T15:29:43Z”. That’s “YYYY-MM-DDThh:mm:ssZ”. The “Z” means “Zulu” time, or GMT. You can also provide time offsets, like “2013-06-30T15:29:43-05:00” for Eastern (US) Time.

Because I’m a nice guy, I have a function in SPServices called SPConvertDateToISO to convert JavaScript date/time objects to the ISO8601 format.

contains

The contains parameter is much like the CAMLQuery parameter in GetListItems, but a little simpler. For instance, you may only be interested in list changes where the current user is the Author, in which case you’d pass:

contains: "<Eq><FieldRef Name='Author'/><Value Type='Integer'><UserID/></Value></Eq>",

If you don’t pass anything for contains, you’ll get back all of the changes since since.

Because the GetListItemChanges operation retrieves only changes in the list, the requests will return very slim results in all but the most active lists. However, the operation will prove valuable in our SPA development because it will allow us to keep our display current for the user with a minimum of fuss unless there have been changes to the underlying data.

GetListItemChanges Returns

When Paul Tavares (@paul_tavares) pointed out the GetListItemChangesWithToken operation a while back, I recalled that I never could get it running properly in my test environment back when I first wrapped it in SPServices. I wrote it off to either my own ineptitude or simply a bug on the Microsoft side (that wouldn’t be a first). At the time, it didn’t seem as though the operation offered enough benefit to chase it down any further.

I was wrong on several counts, of course. The bit about my ineptitude was the one part I was right about. When I wrapped the operation, I accepted two things about it that the documentation told me:

changeToken

A string that contains the change token for the request. For a description of the format that is used in this string, see Overview: Change Tokens, Object Types, and Change Types. If null is passed, all items in the list are returned.

contains

A Contains element that defines custom filtering for the query and that can be assigned to a System.Xml.XmlNode object, as in the following example.

<Contains>
   <FieldRef Name="Status"/>
   <Value Type="Text">Complete</Value>
</Contains>

This parameter can contain null.

The italicized, maroon parts were the issue. While it’s probably possible to pass null on the server side, there’s no such analogous value on the client that we can pass. Since everything we pass is text, generally an empty string will work in those operations where null is allowable. But not in this case.

This means that in all versions of SPServices until the alpha I’ve currently got posted, the operations GetListItemsChanges and GetListItemsChangesWithToken won’t work, no matter how hard you try. That’s because I passed empty strings for the elements above, which just throws an error.

All that said, GetListItemsChanges returns XML that looks the same as what GetListItems gives us.

<listitems xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
   xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
   xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema"
   xmlns="http://schemas.microsoft.com/sharepoint/soap/"
   TimeStamp="2013-11-14T16:30:36Z">
   <rs:data ItemCount="4">
      <z:row ows_Number_Field="6555.00000000000"
         ows_Created="2003-06-18T03:41:09Z"
         ows_ID="3" ows_owshiddenversion="3" />
      <z:row ows_Number_Field="78905456.0000000"
         ows_Created="2003-06-18T17:15:58Z"
         ows_ID="4" ows_owshiddenversion="2" />
         ...
   </rs:data>
</listitems>

There’s one subtle difference. Can you spot it? GetListItemChanges gives us one additional piece of data with the results, and that’s the TimeStamp value. This date/time value tells us when we requested the data, so we can use that value to inform the user when the data was last updated, should we want to.

Processing GetListItems Results

Processing the results from GetListItemChanges is just about the same as with GetListItems.

var out = "<ul>";
$().SPServices({
  operation: "GetListItemChanges",
  since: "2013-11-14T11:00:00-5:00",
  async: false,
  listName: "Announcements",
  CAMLRowLimit: 0,
  CAMLViewFields: "<ViewFields><FieldRef Name='Title' /></ViewFields>",
  completefunc: function (xData, Status) {
    var timeStamp = $(xData.responseXML).SPFilterNode("listitems").attr("TimeStamp");
    var itemCount = $(xData.responseXML).SPFilterNode("rs:data").attr("ItemCount");
    $(xData.responseXML).SPFilterNode("z:row").each(function() {
      out += "<li>" + $(this).attr("ows_Title") + "</li>";
    });
    out += "</ul>";
    $("#announcementsContainer").html(out);
  }
});

Here I’m making the call to GetListItemChanges to get all of the items in the Announcements list which have changed since 11am this morning in Boston.

Conclusion

GetListItemChanges is a great addition to our toolkit because it allows us to make requests for items in a list that have changed since a time we specify. While we could accomplish something similar with GetListItems by passing in a filter for Modified in the CAMLQuery, GetListItemChanges is built for exactly what we need, and I would hope that it is therefore more efficient on the server side.
is a workhorse, all right, but we need more for our SPA work. In the next three parts of the series, I’ll introduce its siblings: GetListItemChanges, GetListItemChangesWithToken, and UpdateListItems. These four operations together will help us to build a highly interactive SPA and provide valuable functionality for our users.

Series Navigation<< Single-Page Applications (SPAs) in SharePoint Using SPServices – Part 2 – GetListItemsSingle-Page Applications (SPAs) in SharePoint Using SPServices – Part 4 – GetListItemChangesSinceToken >>

15 Comments

  1. Hi,

    In the GetListItemChanges method, can I make the value of the parameter dynamic.

    For example, I just to look only into the fields that were changed in the last 30 days.

    Regards,
    Jude

    Reply
  2. I had the datetime value in a variable:

    declare variable $Y := fn:concat(fn:substring(fn:string(fn:current-dateTime() – xs:dayTimeDuration(‘P30D’)),1,11), ‘ ‘, fn:substring(fn:string(fn:current-dateTime() – xs:dayTimeDuration(‘P30D’)),12,8), ‘Z’);

    The value in the variable is:

    2013-12-25T 09:00:47Z

    Now, I tried using the following code:

    (: SOAP payload, actual query goes here:)
    let $soap-payload := (‘
    States At A Glance
    {$Y}
    ‘)

    But it errored out:

    axis2_svc_client_send_receive failed. Web Service returned a soap fault. Error type: XQuery Engine error. soap:ReceiverException of type ‘Microsoft.SharePoint.SoapServer.SoapServerException’ was thrown..

    Any idea, about the mistake in my code?

    Regards,
    Jude

    Reply
  3. for some reason all the tags – “getlistitems, listname and since” are missing in my previous soap payload code.

    But, I believe you get the idea, the {$Y} is within the “since” tag..

    Reply
  4. Hi Marc, great article, I have recently started using GetListItems and it is working great.

    I wondered if you knew how I could update the query results based on select /dropdown changes in my input form.

    Whenever I try it, it keep replicating the Div on my page, instead up requerying the results.

    Thanks in advance,

    Tucker

    Reply
      • Hi Marc, that works great thanks, the only problem is that it seems to return 1 result, where as ‘append’ would return my CAMLRowLimit of 5.

        I don’t suppose you know any way around this?

        Many thanks T.

        Reply
  5. Hi Marc: Thanks for your great works and exactly this is what i am looking for. I have tried with this but failed to get those items that changes. bellow are my scripts. Please correct me what i am missing:

    $(document).ready(function() {
    var out = “”;
    $().SPServices({
    operation: “GetListItemChanges”,
    since: “4/16/2014”,
    async: false,
    listName: “HighlightItemList”,
    CAMLRowLimit: 0,
    CAMLViewFields: “”,
    completefunc: function (xData, Status) {

    var timeStamp = $(xData.responseXML).SPFilterNode(“listitems”).attr(“ows_Title”);
    alert(timeStamp);
    var itemCount = $(xData.responseXML).SPFilterNode(“rs:data”).attr(“ItemCount”);
    $(xData.responseXML).SPFilterNode(“z:row”).each(function() {

    out += “” + $(this).attr(“ows_Title”) + “”;
    });
    out += “”;
    $(“#announcementsContainer”).html(out);
    }
    });

    });

    Reply
      • alert(timeStamp); this line does not return any value. Moreover I have tried with changing “Since” date format. Same result happen. Following line seems do not execute properly:
        completefunc: function (xData, Status) {

        Reply
  6. When I try this, it returns 0x81020014 One or more field types are not installed properly. I created a new list named “test” with nothing but the default Title field. I turned on versioning, and made a few versions of an item. GetListItems works as expected.

    $(document).ready(function(){

    $().SPServices({
    webURL: “http://spdevweb”,
    listName: “test”,
    operation: “GetListItemChanges”,
    since: “2013-11-14T11:00:00-5:00”,
    async: false,
    CAMLRowLimit: 0,
    CAMLViewFields: “”,
    completefunc: function (xData, Status) {
    /***********************************Debugging******************************************************/
    var out = $().SPServices.SPDebugXMLHttpResult({
    node: xData.responseXML
    });
    $(“#debug”).html(“”).append(“This is the output from the GetList operation:” + out);
    /***************************************************************************************************/
    }
    });

    });

    Reply

Have a thought or opinion?