Updating SharePoint list data from a knockout ViewModel

In previous posts I showed the basics of using SharePoint with knockout and how to use lookup columns with a knockout computed observable. This post will explain how to update SharePoint list data using the data stored in your view model using the REST services of SharePoint.

Understanding the Metadata

If you navigate to a single list item using  a rest url such as https://yourserverurl/KOTest/_vti_bin/listdata.svc/Company(1) you will see the xml representation of the data that is returned to your JavaScript when you make an ajax call to that url:

xml While the xml version shown in your browser is useful for many scenarios, sometimes you’ll need to see the actual JSON data that is returned. One easy way to get at this is by using Fiddler.

Go ahead and start fiddler, then refresh your your browser window. Switch back to Fiddler and select the item in the Web Sessions (left-hand) panel that has the url KOTest/_vti_bin/listdata.svc/Company(2):

sessions

The upper-right pane now displays the request sent to sharepoint, and the lower-right pane displays the response received from Sharepoint.  Click on the Headers tab in the upper-right pane and you will see that the browser, by default, sent Sharepoint an Accept header with a value of */*:

Headers

This header tells SharePoint to return the information in any format it likes. Now in Fiddlers lower-right pane click the TextView tab. The lower-right pane now shows the XML data returned from sharepoint, the same as you saw in your browser:

fiddlerxml

To get the JSON response from SharePoint, right click on the url in the Web Sessions (left-hand) pane. From the popup menus select Replay, then Reissue and Edit.

reissueAndEdit

This will let you edit the request and reissue it to sharepoint. Now, In the upper-right pane select the Headers Tab, then right click the “Accept:*/*”  header and select Edit Header from the popup menu. In the popup dialog box, set the value of the header to application/json;odata=verbose.

acceptHeader acceptHeader

Click the Save button, then on the lower-right pane click Run to Completion. This requests SharePoint to return your listitem in JSON format.  In the lower-right pane click the TextView tab (to see the raw JSON returned to the browser) or the JSON tab (to see a formatted view of the JSON):

fiddlerJSON

This is the actual JSOn that is returned to your JavaScript when you make an AJAX call to Sharepoint with an Accept heade if application/json;odata=verbose.  Notice the first element within the ‘d’ element is named __metadata. This element contains some infomation that is useful if you want to update this listitem. The etag property within the d.__metadata represents the current ‘version’ of the item. When you retreive an item from SharePoint using the rest interface, SharePoint includes the current version of the item in this etag.. (Note that the etag value is alos included in an ETag Header on the Response). Whenever you want to update an item using rest , you must include a header on the request named ‘If-Match’ with a value that matches the items current etag. If Successful, SharePoint will respond with  an HTTP 204 (no content) response with another header named ETtag that conatins the  newly assigned etag which must be included in the next update request.This prevents issues with one user overwriting another users changes.

The type property within the __metdata contains the type of content in this item. This could be used to determine the EntityType in the metadata as explained in the previous post.

Finally , the uri property of the __metadata contains the url that should be used when you want to update or delete this item.

Add Metadata to the koItem

As you can see this  metadata will be needed if you want to update or delete items in our your view model. To save it  you need to change the success callback of the  getJSON call you created based on the  previous post. so that it adds the metadata to the koItem:

koItem.__metadata = result.__metadata;

You don’t need to make this a ko.observable because it will not be updated in the UI.

Add the toJSON method

When you want to save an item you will need to send the updated values of  the koItem to Sharepoint in JSON format. To convert the koItem to JSON you can call ko.toJSON(koItem). This will convert all the fields in the koItem into  JSON format. However, there are some fields in the koItem that you do NOT want to send to sharepoint for an update, such as the __metadata, and any computed fields you may have created for use on the client. If these are sent to SharePoint, SharePoint will respond with a 401 ‘Bad Request’.  To accommodate this, you need to create a ‘toKSON’ method within your koItem that will return an object that contains just the fields you want sent to SharePoint:

koItem.toJSON =function () {

return {

Title:this.Title

};

}

Any fields you want saved when you update the list item must be included in the object you return from your toJSON method. Now when you call ko.toJSON(koItem), knockout will call your objects toJSON method, and convert the object returned from your toJSON method into a JSON string that can be sent to SharePoint. In the example above, the JSON sent to SharePoint will contain only the title — the metadata, and any other properties of the object will be ignored.

Code the Save Method

Next, you’ll need to add a method to your koItem that can be called to save the updated values of the item to sharepoint:

koItem.save =function () {

self.saveCompany(this);

};

This method of the koItem will call a saveCompany method of the viewModel (indicated by the ‘self’ in self.saveCompany) passing in the current koItem. You could put all the code to save your item in this method, rather than calling self.saveCompany, but this would be inefficient since all the code would be duplicated in each koItem.

The saveCompany of the viewnodel is shown below:

self.saveCompany = function (item) {

$.ajax({

type: “POST”,

url: item.__metadata.uri,

headers: {

“X-HTTP-Method”: “MERGE”,

“If-Match”: item.__metadata.etag

},

contentType: “application/json”,

data: ko.toJSON(item),

success: function (data, textStatus, jqXHR) {

var newEtag = jqXHR.getResponseHeader(“etag”);

if (!newEtag) { //ie8 strips off all headers on a ‘No-Content’ response

$.ajax({

type: “GET”,

url: item.__metadata.uri,

contentType: “application/json”,

success: function (data, textStatus, jqXHR) {

item.__metadata.etag = jqXHR.getResponseHeader(“etag”);

},

error: function (jqXhr, textStatus, errorThrown) {

alert(errorThrown);

}

});

}

else {

item.__metadata.etag = newEtag;

}

},

error: function (jqXhr, textStatus, errorThrown) {

alert(errorThrown);

}

});

The method is a simple ajax call sending JSON data to SharePoint.  The url of the request parameter is set to the url of the item that your received from sharepoint and stored in your koItems metadata. Two headers are added to the request , an X-HTTP-Method header and an If-Match header. The X-HTTP_Method header is set to MERGE  indicating that you only want to update the values of the fields that are contained in your JSON object as explained here. The If-Match header is set to the etag you previously retrieved from the items metadata and stored in your koItem object. Finally the data of the request is set to ko.toJSON(item). This calls the toJSON method that you created  on the koItem and converts the results of that method call to JSON format to be sent to SharePoint, as previously discussed.

The success callback, in this case, is used to set the new etag in the koItems metadata so that it will be included in subsequent updates. As noted earlier, you include a header on the update request named ‘If-Match’ with a value that matches the items current etag and sharepoint responds with  an HTTP 204 response containing an etag header whose value is the  newly assigned etag (which must be included in the next update request) The line:

var newEtag = jqXHR.getResponseHeader(“etag”);

gets the etag from the response header.  If the newEtag is valid, the code simply stores it in the metadata.etag property of the koItem so that subsequent updates will use this new etag value . There is an issue however with ie9 and below in that they remove all response headers from an HTTP 204 response(see here). To accomodate this  issue, if there is no etag header, the code executes an ajax GET against the same url (item.__metadta.uri) just to get the new etag so that subsequent updates will have the new value. Not that if your Sharepoint list has any Calculated Columns or uses Event Receivers to update the list Item when it is added, it may be neccesary to make this additional call to get the updated list  data.

Lastly, the code includes a notificication so that you can see that the item was updated and tne new etag was assigned:

var notification = “<div><span>Item” + item.Title() + ” Updated. New etag is “ + item.__metadata.etag + “</span></div>” 

SP.UI.Notify.addNotification(notification);

The full javascript (and html) source can be downloaded here.

Now that you have saved the metadata and coded the save function  you can add a a button to your template to call the save method on your koItem:

<td style=”width: 250px”>

<input type=”button” data-bind=”click:save” value=”save” ></input>

</td>

Now you can load your page,  change the items title multiple times, and see the new etag in the Sharepoint notification:

itemUpdated

The full javascript (and html) source can be downloaded here.

Advertisements
This entry was posted in jquery, knockout, Uncategorized. Bookmark the permalink.

One Response to Updating SharePoint list data from a knockout ViewModel

  1. Carlos says:

    Very interesting blog, i follow it and try to make an equivalent VBA MERGE Rest, i need to add in the requestHeader the __metadata.etag,, unfortunadtly after addding the entity metadata just get a HTTP Status of 400 i will try to see what is missing, i think rest API for sharepoint should be easier, a lot of instruction seems like a collection of hacking to achive simple operations. Congrat by the post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s