CHUVASH.eu

CHunky Universe of Vigourous Astonishing SHarepoint :)

Tag Archives: listdata.svc

$().SPServices is best for SOAP

SPServices is a great tool, really nice work, Marc Anderson (@sympmarc). It has been there all the time I have developed for SharePoint. But the fact that you work with XML and you must parse the attributes (!) was the reason why I prefered listdata.svc and Client Object Model, where you get objects in JSON or you have a nice API to get objects and their properties. But there is an area where SPServices are really the best tool: SharePoint Web Services which only understand SOAP like SocialDataService.asmx.

Three ways to get list items

Just a little comparison. We’ll retrieve Titles from Tasks list (Oppgaver in nb-no).
listdata.svc

$.getJSON("/_vti_bin/ListData.svc/Oppgaver()?$select=Tittel")
	.done(function(data){
	  var tasks = data.d.results;
		for(var t in tasks) {
			var title = tasks[t].Tittel;
			console.log(title);
		}
	})
	.fail(function() {
		console.log("error");
	});

Pay attention to the field name: Tittel. I think it is the biggest shortcoming of listdata.svc: the fact that we can’t use the internal (static) field names.
Client Object Model

var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var taskList = web.get_lists().getByTitle("Oppgaver");
var query = new SP.CamlQuery("<ViewFields><FieldRef Name='Title' /></ViewFields>");
var tasks = taskList.getItems(query);
ctx.load(tasks);
ctx.executeQueryAsync(function() {
	var listEnumerator = tasks.getEnumerator();
	while(listEnumerator.moveNext()) {
		var task = listEnumerator.get_current();
		var title = task.get_item("Title");
		console.log(title);
	}
}, function() {
	console.log("error");
});

SPServices

$().SPServices({
    operation: "GetListItems",
    async: true,
    listName: "Oppgaver",
    CAMLViewFields: "<ViewFields><FieldRef Name='Title' /></ViewFields>",
    completefunc: function (xData, Status) {
        $(xData.responseXML).SPFilterNode("z:row").each(function() {
            console.log( $(this).attr("ows_Title") );        
        });
    }
});
SPServices and SocialDataService

Let’s try to find the count of comments for an url. To do so we must create a SOAP envelope in javascript, send it and handle the response result:

var strUrl = "http://dev/Sider/Help.aspx";
var request = "<soap:Envelope \
    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' \
    xmlns:xsd='http://www.w3.org/2001/XMLSchema' \
    xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'> \
    <soap:Body>    \
      <CountCommentsOnUrl \
          xmlns='http://microsoft.com/webservices/SharePointPortalServer/SocialDataService'> \
        <url>" + strUrl + "</url> \
      </CountCommentsOnUrl> \
     </soap:Body> \
  </soap:Envelope>";

jQuery.ajax({
  type: "POST",
  async: true,
  url: '/_vti_bin/SocialDataService.asmx?op=CountCommentsOnUrl',
  data: request,
  contentType: "text/xml; charset=utf-8",
  dataType: "xml",
  success: function (content, txtFunc, xhr) {
    var count = jQuery(content).find('CountCommentsOnUrlResult').text();
    console.log(count);
  }
});

It works but, we have to handle the creating of SOAP envelope in our javascript code which can be error prone. SPServices provides a nice abstraction layer and handles many scenarios. The same operation in SPServices:

$().SPServices({
  operation: "CountCommentsOnUrl",
  async: true,
  url:"http://dev/Sider/Help.aspx",
  completefunc: function (xData, Status) {
    var result = $(xData.responseXML).find("CountCommentsOnUrlResult")[0];
    console.log($(result).text());
  }
});

In both cases we have to iterate through responseXML. This is a sample responseXML for CountCommentsOnUrl from SocialDataService.asmx:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
      <CountCommentsOnUrlResponse
          xmlns="http://microsoft.com/webservices/SharePointPortalServer/SocialDataService">
      <CountCommentsOnUrlResult>1</CountCommentsOnUrlResult>
    </CountCommentsOnUrlResponse>
  </soap:Body>
</soap:Envelope>

$select in listdata.svc

Sure you don’t want to load all the properties of listitems, like ContenTypeId and so on. We cannot avoid __metadata property 🙂, but we can let listdata.svc to send only properties we want. By the way, the best tool for building listdata.svc queries is linqpad. Just write a usual Linq and linqpad converts it to web service url query:

 

Another great stuff is $count, just add ?$count and you get only the count of items, no other junk.

 

Update list items with listdata.svc

In one of my previous posts I showed how to retrieve data from listdata.svc with jQuery $.getJSON method. Getting data is very simple, listdata.svc provides a powerful restful interface with filtering, expanding and other features. Read the best and short introduction to listdata.svc by Suneet Sharma or a more detailed and technical article about filters, functions and operators by Mike Flasko at Microsoft with short examples here. Now back to listdata.svc in the SharePoint environment. Corey Roth provides a good images and sample results. For REST the “http verbs” are very important: GET, POST, PUT, DELETE..

SharePoint RESTful interface defines these verbs like that:

HTTP verb Data operation
GET Retrieve
POST Create
PUT Update (update all fields and use default values for any undefined fields)
DELETE Delete
MERGE Update (update only the fields that are specified and changed from current version)

If you want to dive into http verbs in the listdata.svc, install cURL and follow the awesome tutorial at JustinLee.sg.

See this slideshare presentation for a quick introduction:

Allright, a higher level of retrieving and updating of data from listdata.svc is presented by Lee Richardson. Lee uses live binding datacontext, which gives you to save changes: dataContext.saveChanges. Another approach is Client object model.

Retrieve data

I’ll give a try $.ajax. We’ll see if it works. The goal is to update an existing item. Let’s take a usual Task list. I have a site in Norwegian (nb-NO), so we’ll see how well it is suited for i18n.

To retrieve all uncompleted tasks for “contoso\administrator”, we can put this into the browser address bar:

http://contoso/_vti_bin/ListData.svc/Oppgaver?$filter=StatusValue ne 'Fullført' and TilordnetTil/Konto eq 'contoso\administrator'

The invoke is case insensitive. This uses two filters.

To invoke this method with ajax, paste it into $.getJSON:

$.getJSON("http://contoso/_vti_bin/ListData.svc/Oppgaver?$filter=StatusValue ne 'Fullført' and TilordnetTil/Konto eq 'contoso%5cAdministrator'",
{}, null
);

Pay attention to backslash in account name. It must be endoded %5c so javascript doesn’t escape it. In this post I won’t provide any gui for this. The result can be inspected in Chrome Dev Tools Network tab.

Create a new item

To create an item, we have to use the POST verb and send a JSON serialized data in a body.

 //create
var url = "/teamsite/_vti_bin/ListData.svc/MyContacts";
var contact = {
	FirstName: "Arnie",
	Title: "Dell",
	WorkCity: "Lund"
};
var body = JSON.stringify(contact);
$.ajax({
	 type: "POST",
	 url: url,
	 contentType: 'application/json',
	 processData: false,
	 data: body,
	 success: function () {
	   alert('Contact Saved.');
	 }
});

Update an item

As described in the Microsoft resource, to update an existing item from javascript, the best verb is POST with MERGE settings. Provide this js function:

beforeSendFunction = function (xhr) {
  xhr.setRequestHeader("If-Match", "*");
  // Using MERGE so that the entire entity doesn't need to be sent over the wire.
  xhr.setRequestHeader("X-HTTP-Method", 'MERGE');
}

And use it in the $.ajax invoke:

 $.ajax({
 	type: "POST",
	...
        beforeSend: beforeSendFunction,
 });

But before we can invoke $.ajax, we must prepare some properties which will be changed. Let’s change Tittel (Title in nb-NO):

var mods = {};
mods.Tittel = "hello verden"
var body = Sys.Serialization.JavaScriptSerializer.serialize(mods);

Now we can post it:

$.ajax({
 	type: "POST",
	contentType: "application/json; charset=utf-8",
	processData: false,
        beforeSend: beforeSendFunction,
 	url: "/_vti_bin/ListData.svc/Oppgaver(3)",
 	data: body,
 	dataType: "json",
	success: function() { console.log("success"); },
	error: function() { console.log("error"); }
 });

And it works!

The beforeSendFunction can be replaced with headers:

//update 
var url = "/teamsite/_vti_bin/ListData.svc/MyContacts(3)";
var mods = {
	FirstName: "Tolle"
};
var body = JSON.stringify(mods);
//update, another example
$.ajax({
 	type: "POST",
	contentType: "application/json; charset=utf-8",
	processData: false,
        headers: {
		"If-Match": "*",
		"X-HTTP-Method": "MERGE"
	},
 	url: url,
 	data: body,
 	dataType: "json",
	success: function() { console.log("success"); },
	error: function() { console.log("error"); }
 });

To update the status is easy too:

var mods = {};
mods.StatusValue = "Fullført"
var body = Sys.Serialization.JavaScriptSerializer.serialize(mods);
$.ajax({
 	type: "POST",
	contentType: "application/json; charset=utf-8",
	processData: false,
        beforeSend: beforeSendFunction,
 	url: "/_vti_bin/ListData.svc/Oppgaver(3)",
 	data: body,
 	dataType: "json",
	success: function() { console.log("success"); },
	error: function() { console.log("error"); }
 });

To create a new item we’ll use POST as well, don’t provide beforeSendFunction:

var mods = {};
mods.Tittel = "david";
mods.TilordnetTilId = 14;
var body = Sys.Serialization.JavaScriptSerializer.serialize(mods);

 $.ajax({
 	type: "POST",
	contentType: "application/json; charset=utf-8",
	processData: false,
 	url: "/_vti_bin/ListData.svc/Oppgaver",
 	data: body,
 	dataType: "json",
	success: function() { console.log("success"); },
	error: function() { console.log("error"); }
 });

Unfortunately I didn’t find any way to set “assigned to” account name, I had to set Account ID (mods.TilordnetTilId = 14).

To delete an item, just use $.ajax and DELETE verb:

$.ajax({
 	type: "DELETE",
	contentType: "application/json; charset=utf-8",
	processData: false,
 	url: "/_vti_bin/ListData.svc/Oppgaver(3)",
 	data: {},
 	dataType: "json",
	success: function() { console.log("success"); },
	error: function() { console.log("error"); }
 });
Localization

What about i18n and localization? It is actually not so generic. One solution could be localizable javascript enums. The fact that the properties are translated can lead to problems. The first time I load the page after deploy, restart or app pool recycling, I get 400 error (bad request). The explanation can be found in the Response:

{
  "error": {
    "code": "",
    "message": {
      "lang": "sv-SE",
      "value": "No property 'TilordnetTilId' exists in type 'Microsoft.SharePoint.Linq.DataServiceEntity' at position 30."
    }
  }
}

It tries to retrieve the Swedish Properties (due my default language in the browser) of the list items. But the list is in Norwegian (nb-NO). In my other browser where I have nb-NO as default language, this problem doesn’t occur. How can we solve this? Sharepoin is too fast here.

And all this stuff: Fullført and other strings that are only valid for one particular language. How to build a solution which can be deployed in different culture environments. The sick is like SPLinq and Localization, the status values are saved as strings in the database, so you can’t use it like enums and so:

An embryo for a possible solution can be using of localization resources like core.resx, where these values are defined:

protected void Page_Load(object sender, EventArgs e)
{
	if (!IsPostBack)
	{
		RegisterLocalizationScript();
	}
}

private void RegisterLocalizationScript()
{
	var completed = SPUtility
		.GetLocalizedString("core", "Tasks_Completed", 
			(uint)CultureInfo.CurrentCulture.LCID);
	var ongoing = SPUtility
		.GetLocalizedString("core", "Tasks_InProgress", 
			(uint)CultureInfo.CurrentCulture.LCID);
	var script = string.Format("completedString = '{0}';ongoingString = '{1}'", 
			completed, ongoing);
	Page.ClientScript.RegisterStartupScript(GetType(), 
		"todo-localized-values", script, true);
}

or:

// this is used to retrieve and update todos with listdata.svc web service
Task = {
	completed: "Fullført", //$Resoures:core.resx, Tasks_Completed
	inProgress: "Pågår" //$Resoures:core.resx, Tasks_InProgress
}
Datetime

To retrieve and order it after creation date add $orderby:

http://contoso/_vti_bin/ListData.svc/Oppgaver?$filter=StatusValue ne 'Fullført' and TilordnetTil/Konto eq 'contoso\administrator'$orderby=Opprettet desc

To filter datetime follow this link:

http://contoso/_vti_bin/ListData.svc/Oppgaver?$filter=Endret+ge+datetime'2011-11-23T00:00:00'

To get all items changed the last week:

var date = new Date();
date.setDate(date.getDate()-7);
var lastWeekISO = date.toISOString();
$.getJSON("http://contoso/_vti_bin/ListData.svc/Oppgaver?$filter=Endret ge datetime'" + lastWeekISO + "'",
{}, null
);

To update/add a date we have to pass an ISO string.

var today =  new Date();
task.Forfallsdato = today.toISOString();
Misc

Only after I wrote this I found a very nice intro to REST by Jan Tielen.

The alternative is to use SPServices or maybe SPRest or SPELL by Christophe by Path To Sharepoint.

The alternative way to retrieve list items and update them is to use Client Object Model (CSOM). One of the advantages of Client Object Model is that you don’t need to care about the localization of column names and status values. You can rely on static names like you do in the Server Object Model when you use CAML.

This image shows differences for retrieving 10 list items using lisdata.svc vs csom:

$.getJSON, jQuery.tmpl and _vti_bin

Javascript is very fast, responsive and unburdens the cpu at the server. Here I show a little example how to use jQuery.getJSON, jQuery.tmpl (wonderful plugin for rendering repeating data, repo hosted on Github) and REST-based service listdata.svc from /_layouts/_vti_bin/ folder.

For this example I created a generic list “Contacts”, added two text fields Name and Phone. Add some phone numbers and try to go to /_vti_bin/ListData.svc/Contacts

If you get 404-error, install a ADO.NET Data Services v1.5 CTP2, as described at dotnetmafia. If you want to know more about how to sort and filter, skip and limit results, read more at nothingbutsharepoint. If you encounter problems, follow Michaël’s blog post and download and install Windows6.1-KB976127-v6-x64.msu (if you have Windows Server 2008 R2), or NDP35SP1-KB976126-v2-x64.exe (if you have Windows Server 2008). After installing the version of the System.Data.Services.dll file is 3.5.30729.5004 (Windows Server 2008 R2), and 3.5.30729.4466 (Windows Server 2008).

Allright, the remainder is pure javascript. In order to get it working you have to run the script from the same domain, meaning you can’t run javascript to retrieve the data from a html-file from your home folder.

Create a webpart and add it to your page.

<script type="text/javascript"
	src="http://code.jquery.com/jquery-latest.min.js">
</script>

<script type="text/javascript"
	src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js">
</script>

<script id="contactTemplate" type="text/html">
    <div>
        Name: {{= Name }} <br>
        Phone: {{= Phone }}
    </div>
</script>

<script type="text/javascript">
function loadContacts() {
    $.getJSON("http://dev/_vti_bin/listdata.svc/Contacts",
        {},
        function (data) {
            $("#contactTemplate")
                .tmpl(data.d.results)
                .appendTo("#contactContainer");
        }
    );
}

</script>
<div id="contactContainer"></div>
<input type="button" value="click to load"
	onclick="javascript:loadContacts();"/>

Now, when you click on the button, you should see something like this:

That’s all. Just a simple intro to listdata.svc, jQuery.tmpl and jQuery.getJSON. You have all the freedom to decide, when to load, how often to update, the design and much more. Another approaches are an XsltListViewWebpart with custom xsl style and ajax settings, SPServices which I will  write about in my next posts.

Вула Чăвашла

VulaCV - Чăвашла вулаттаракан сайт

Discovering SharePoint

And going crazy doing it

Bram de Jager - Architect, Speaker, Author

Microsoft 365, SharePoint and Azure

SharePoint Dragons

Nikander & Margriet on SharePoint

Cameron Dwyer

Office 365, SharePoint, Azure, OnePlace Solutions & Life's Other Little Wonders

paul.tavares

Me and My doings!

Share SharePoint Points !

By Mohit Vashishtha

Jimmy Janlén "Den Scrummande Konsulten"

Erfarenheter, synpunkter och raljerande om Scrum från Jimmy Janlén

Aryan Nava

DevOps, Cloud and Blockchain Consultant

SPJoel

SharePoint for everyone

SharePointRyan

Ryan Dennis is a SharePoint Solution Architect with a passion for SharePoint and PowerShell

SharePoint 2020

The Vision for a Future of Clarity

Aharoni in Unicode

Treacle tarts for great justice

... And All That JS

JavaScript, Web Apps and SharePoint

blksthl

Mostly what I know about SharePoint - CommunicoCuspis

SharePointDiver

SharePoint på ren svenska