Sunday, October 27, 2013

SharePoint 2013 REST Code Tips

Technorati Tags: ,,

I have been doing a lot of Office 365 coding lately and strictly using typescript, javascript and the SharePoint 2013 REST API. The REST API is easy to use but the documentation around what you can and cannot do with REST is incomplete. In this post I will give you a few tips on what you can do with the REST API.  All of these examples will be used in the context of a SharePoint app. So they will include code to go against the host web that is hosting the app. 

Provision a Site Column

You may have a scenario where your app will want to create a new  site column on the fly. The following example uses the REST API to create a new site column. Note that you must set the FieldTypeKind value. This maps back to the server side object model’s SPFieldType enumeration. It must be an integer value. For example if you want to create a text field then use 2. The REST API does not utilize enumerations. So how did I figure out that I needed to use the name “FieldTypeKind” in the REST call? By examining the Microsoft.SharePoint.Client object model. The majority of the time you can set the values using these names when using the REST API. With the REST API you just add to the collections by constructing a URL to the collection and posting the data. It is important to remember to use the _metadata value in order to tell the server the type of object that is in your payload. Another important point is that any time you are creating or updating SharePoint data you must include the request digest value in your request.

function provisionSiteColumn() {

appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
var fieldType = 2;
var fieldInternalName = "NewCol5";
var fieldDisplayName ="NewCol5";
var fieldGroup = "Custom Columns";
var fieldRequired = false;
var fieldHidden = false;

$.ajax(
{
url: appweburl + "/_api/SP.AppContextSite(@target)/web/fields?@target='" +
hostweburl + "'",
type: "POST",
data: JSON.stringify({
'__metadata': { 'type': 'SP.Field' }, 'FieldTypeKind': fieldType,
'InternalName': fieldInternalName, 'Title': fieldDisplayName,
'Group': fieldGroup, 'Required': fieldRequired, 'Hidden': fieldHidden
}),
headers: {
"accept": "application/json;odata=verbose",
"content-type": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val()
},
success: function (data) {
var d = JSON.parse(data.body);
},
error: function (err) {
alert(JSON.stringify(err));
}


})

}


Retrieve the Choices for a SharePoint Choice Column


Another useful tip is getting the choices for a SharePoint choice column. This can be used when you are creating your own custom new and edit forms for your app. The choices can be bound to a html list so the user can choose the appropriate value. The example below is generic and passes in the name of the list and choice field. Note that it adds the SELECT command to get the choices collection of the field and uses the FILTER command to get the choice column. It also utilizes the jQuery deferred object so the calling code can wait on the asynchronous call.


function getFieldChoices(fieldName, listName) {
var dfd = $.Deferred();
hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));

var urlTemplate = appweburl + "/_api/SP.AppContextSite(@target)/web/lists/getbytitle('#mylist')/fields?@target='" + hostweburl + "'&$select=Choices&$filter=Title eq '#myfield'";
var url = urlTemplate.replace("#mylist", listName).replace("#myfield", fieldName);

$.ajax({
headers: { "Accept": "application/json;odata=verbose" },
contentType: 'application/json',
url: url,
success: function (data) {
var result = data.d.results[0].Choices.results;
var i = result.length;
dfd.resolve(result);
},
error: function (err) {
dfd.reject(err);
}
});
return dfd;

}


Call multiple asynchronous functions and wait for them all to complete


One of the biggest challenges when writing asynchronous code is chaining multiple function calls together. The best approach is to use jQuery promises in your asynchronous functions shown in the previous example. If your code needs to do multiple steps (function calls) before you can continue the next step, then use the jQuery $.when.apply function. This function will take an array of functions that return a promise and execute them in order of the array, and then execute a done function after they have all completed. The sample code below will create three function calls that call the SharePoint JSOM to retrieve all the terms for a given term set. Each function gets a different term set ID. The code then calls the $.when.apply and checks the counts of the terms after all functions have been executed.


function execMultipleRequests() {

var tsIds = ["626db900-c668-45d0-b673-f43b6929f81f",
"626db900-c668-45d0-b673-f43b6929f81f",
"626db900-c668-45d0-b673-f43b6929f81f"];

var functionCalls = [];

functionCalls = $.map(tsIds, function (id) {
var context = SP.ClientContext.get_current();
var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
var termStores = taxSession.get_termStores();
var termStore = taxSession.getDefaultSiteCollectionTermStore();

var dfd = $.Deferred();

termSet = termStore.getTermSet(id);
terms = termSet.getAllTerms();
context.load(terms);

context.executeQueryAsync(
function() {
dfd.resolve(terms);
},
function(sender, args) {
dfd.reject(args);
});
return dfd.promise();
});

$.when.apply($, functionCalls).done(function (results) {
$.each(arguments, function (k, v) {
alert(v.get_count().toString());
})
}).fail(function (e) {
var i = e;
});

}


You can read more about the jQuery when function here http://api.jquery.com/jQuery.when/



Set the Default Value for a Column


Recently there was a scenario where I needed to set the default value for a managed metadata field and I needed do this with a REST call. The sample below accomplishes this. Once again it is a generic function taking the list, field and default value as arguments. The important thing to note is to use the "IF-MATCH": "*", "X-Http-Method": "MERGE" header values.


function setDefaultMMValue(listName, fieldName, defaultValue) {

appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));

urlTemplate = appweburl + "/_api/SP.AppContextSite(@target)/web/lists/getbytitle('#ListName')/fields/getbytitle('#FieldName')?@target='" +
hostweburl + "'";

updateUrl = urlTemplate.replace("#ListName", listName).replace("#FieldName", fieldName);

$.ajax({
async:false,
url: updateUrl,
type: "POST",
data: JSON.stringify({
"__metadata": { type: "SP.Field" },
DefaultValue: defaultValue
}),
headers: {
Accept: "application/json;odata=verbose",
"Content-Type": "application/json;odata=verbose",
"X-RequestDigest": jQuery("#__REQUESTDIGEST").val(),
"IF-MATCH": "*",
"X-Http-Method": "MERGE"
},
success: function (data) {

},
error: function (data) {
alert(JSON.stringify(data));
}
});

}


The REST API is not equal to the CSOM


I hope you can find some use from these REST coding tips. The REST api is easier to use than the javascript implementation of the client object model but it can only do a small fraction of what the CSOM can do. However, this may not be true and only be caused by lack of good documentation on the capabilities of the REST API. I am currently researching a way to easily discover REST capabilities in the server object model using reflection. Putting the pieces together is proving to be very difficult. Eventually, either myself or Microsoft should provide you a Visual Studio extension to let you right click on a server object model method and display a REST code snippet example on how to use it. Dream, dream, dream.