Quantcast
Viewing latest article 2
Browse Latest Browse All 5

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!
Image may be NSFW.
Clik here to view.

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:

Image may be NSFW.
Clik here to view.

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

Image may be NSFW.
Clik here to view.

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:

Image may be NSFW.
Clik here to view.


Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.

Viewing latest article 2
Browse Latest Browse All 5

Trending Articles