This is a migrated thread and some comments may be shown as answers.

SharePoint 2010 datasource

13 Answers 493 Views
Data Source
This is a migrated thread and some comments may be shown as answers.
Toby
Top achievements
Rank 1
Toby asked on 21 Jun 2012, 10:19 AM
Hi,

I've been playing around with the Kendo UI datasource and have created a data plugin(?) for using the listdata.svc for SharePoint 2010, it is attached to this post as a zip file

it can be implemented using the following code

var dataSource = new kendo.data.DataSource({
        type: "sharepoint",
        transport: {
        read: "_vti_bin/listdata.svc/Inventory"
    },
    schema: {
        model: {
            fields: {
                Make: { type: "string" },
                Model: { type: "string" },
                Price: { type: "number" },
                PictureUrl: { type: "string" }
            }
        }
    },
    pageSize: 5,
    serverPaging: true,
    serverFiltering: true,
    serverSorting: true
});

I'm going to look at adding full CRUD functionality and will post any updates, this currently only does reading.

SharePoint 2010 listdata.svc ODATA implementation does not currently support JSONP, so this can only be used in the same domain.

Any comments or obvious errors please.

13 Answers, 1 is accepted

Sort by
0
Clint
Top achievements
Rank 1
answered on 09 Jul 2012, 09:46 AM
Full CRUD would be awesome!  Let us know it goes.
0
Remco
Top achievements
Rank 1
answered on 14 Sep 2012, 09:37 AM
I almost managed to get all crud operations to work with the SharePoint 2010 list odata service. Here is the code:

var crudServiceBaseUrl = "/_vti_bin/listdata.svc/RiskMatrices",
    dataSource = new kendo.data.DataSource({
        type: "odata",
        transport: {
            read: {
                url: crudServiceBaseUrl,
                dataType: "json"
            },
            update: {
                // type must be POST instead of PUT when using MERGE.
                //type: "POST",
                url: function (options) {
                    return options.__metadata.uri;
                },
                dataType: "json",
                beforeSend: function (jqXHR, settings) {
                    var options = JSON.parse(settings.data);
                    jqXHR.setRequestHeader("If-Match", options.__metadata.etag);
                    // Using MERGE so that the entire entity doesn't need to be sent over the wire.
                    //jqXHR.setRequestHeader("X-HTTP-Method", 'MERGE');
                }
            },
            destroy: {
                url: function (options) {
                    return options.__metadata.uri;
                },
                dataType: "json"//,
                //beforeSend: function (jqXHR, settings) {
                //    var options = JSON.parse(settings.data);
                //    jqXHR.setRequestHeader("If-Match", options.__metadata.etag);                       
                //}
            },
            create: {
                url: crudServiceBaseUrl,
                dataType: "json",
            }
        },
        schema: {
            model: {
                id: "Id",
                fields: {
                    Id: { type: "number", editable: false, nullable: true },
                    Title: { type: "string", validation: { required: true } },
                    Created: { type: "date" },
                    Modified: { type: "date" },
                    CreatedById: { type: "number" },
                    ModifiedById: { type: "number" }
                }
            }
        },
        pageSize: 10,
        serverPaging: true,
        serverFiltering: true,
        serverSorting: true,
        error: function (e) {
            var response = JSON.parse(e.responseText);
            alert(response.error.message.value);
        }
    });

I can do read, create and delete, but I can only do an update once. The difficulty is with the SharePoint 2010 list odata service requiring that a concurrency token is round-tripped. The SharePoint 2010 list odata service responds with an ETAG http header that contains the concurrency token. The SharePoint 2010 list odata service requires the use of an If-Match http header in a request to roundtrip the concurrency token. This etag value is also returned in the response body in a __metadata property in the json object when doing read and create operations. This way I can retrieve this etag value from this __metadata property and set the value in the If-Match http header before doing an update. When the update is successful the response includes the new etag value in the ETAG http header, but the update response body is otherwise empty. The kendo ui grid or datasource needs to update the etag value in the __metadata property of the updated object from the ETAG http header in the response so that I can do subsequent updates. This is currently not happening. As a result subsequent updates pass the out-of-date concurrency token in the If-Match request header. The SharePoint list odata service then throws a concurrency violation error.
0
Remco
Top achievements
Rank 1
answered on 18 Sep 2012, 12:26 PM
I managed to get the transport create, read, update and destroy settings defined as functions working. I was not able to get the ETag response header as the jqXHR.getResponseHeader('ETag') would return null. Since I also wanted to get updated values for the Modified, ModifiedById and ModifiedBy properties, I thought it was best to re-load the updated entity. See below:
var crudServiceBaseUrl = "/_vti_bin/listdata.svc/RiskMatrices",
    dataSource = new kendo.data.DataSource({
        type: "odata",
        transport: {
            read: function (options) {
                return $.ajax({
                    type: "GET",
                    url: kendo.format("{0}?$expand=CreatedBy,ModifiedBy", crudServiceBaseUrl),
                    dataType: "json",
                    data: options.data
                }).done(function (data, textStatus, jqXHR) {
                    options.success(data);
                }).fail(function (jqXHR, textStatus, errorThrown) {
                    options.error(jqXHR);
                });
            },
            update: function (options) {
                return $.ajax({
                    type: "POST",
                    url: options.data.__metadata.uri,
                    dataType: "json",
                    contentType: "application/json",
                    data: kendo.stringify({
                        Id: options.data.Id,
                        Title: options.data.Title
                    }),
                    headers: {
                        "If-Match": options.data.__metadata.etag,
                        "X-HTTP-Method": 'MERGE'
                    }
                }).done(function (data, textStatus, jqXHR) {
                    return $.ajax({
                        type: "GET",
                        url: kendo.format("{0}?$expand=CreatedBy,ModifiedBy", options.data.__metadata.uri),
                        dataType: "json"
                    }).done(function(data, textStatus, jqXHR) {
                        options.success(data);
                    }).fail(function(jqXHR, textStatus, errorThrown) {
                        options.error(jqXHR);
                    });
                }).fail(function (jqXHR, textStatus, errorThrown) {
                    options.error(jqXHR);
                });
            },
            destroy: function (options) {
                return $.ajax({
                    type: "DELETE",
                    url: options.data.__metadata.uri,
                    dataType: "json",
                    contentType: "application/json",
                    headers: {
                        "If-Match": options.data.__metadata.etag
                    }
                }).done(function (data, textStatus, jqXHR) {
                    options.success(data);
                }).fail(function (jqXHR, textStatus, errorThrown) {
                    options.error(jqXHR);
                });
            },
            create: function (options) {
                return $.ajax({
                    type: "POST",
                    url: crudServiceBaseUrl,
                    dataType: "json",
                    contentType: "application/json",
                    data: kendo.stringify({
                        Id: options.data.Id,
                        Title: options.data.Title
                    })
                }).done(function (data, textStatus, jqXHR) {
                    return $.ajax({
                        type: "GET",
                        url: kendo.format("{0}?$expand=CreatedBy,ModifiedBy", data.d.__metadata.uri),
                        dataType: "json"
                    }).done(function (data, textStatus, jqXHR) {
                        options.success(data);
                    }).fail(function (jqXHR, textStatus, errorThrown) {
                        options.error(jqXHR);
                    });
                }).fail(function (jqXHR, textStatus, errorThrown) {
                    options.error(jqXHR);
                });
            }
        },
        schema: {
            model: {
                id: "Id",
                fields: {
                    Id: { type: "number", editable: false, nullable: true },
                    Title: { type: "string", validation: { required: true } }
                    Created: { type: "date" },
                    Modified: { type: "date" },
                    CreatedById: { type: "number" },
                    ModifiedById: { type: "number" }
                }
            }
        },
        pageSize: 10,
        serverPaging: true,
        serverFiltering: true,
        serverSorting: true,
        error: onError
    });

Also for a create I wanted to re-load the entity. Even though the create response contained the entity, I needed it to expand certain related objects (CreatedBy and ModifiedBy). This seems to work now. Next: get batch editing working.
0
Remco
Top achievements
Rank 1
answered on 21 Sep 2012, 08:26 AM
I managed to get batch editing working for the SharePoint 2010 list odata/rest service with the kendo ui datasource using the Microsoft datajs library (http://datajs.codeplex.com/). 
0
Toby
Top achievements
Rank 1
answered on 21 Sep 2012, 09:02 AM
That is stirling work Sir!

I hadn't had any time to work on the CRUD operations as my client only needed read operations, but this will be very useful
0
Clint
Top achievements
Rank 1
answered on 26 Sep 2012, 07:33 PM
Remco, can you give us an example of how you used datajs and Sharepoint?  Obviously the read is pretty easy by itself, but how did you integrate into the kendoui datasource?
0
Remco
Top achievements
Rank 1
answered on 27 Sep 2012, 09:13 AM
I wrote some helper function which allow you to integrate datajas with the kendoui datasource as follows:



categoriesDataSource = new kendo.data.DataSource({
    type: "odata",
    transport: {
        read: function (options) {
            var url ="/_vti_bin/listdata.svc/Categories";
            read(url).done(options.success).fail(options.error);
        },
        update: function (options) {
            requestBatchUpdate(options.data.models, "Categories({0})").done(options.success).fail(options.error);
        },
        destroy: function (options) {
            requestBatchDestroy(options.data.models, "Categories({0})").done(options.success).fail(options.error);
        },
        create: function (options) {
            requestBatchCreate(options.data.models, "Categories").done(options.success).fail(options.error);
        }
    },
    schema: {
        model: Category
    },
    batch: true,
    error: onError
});


the helper functions are (there are batch and non-batch versions of create, update and destroy):



function read(url) {
    var deferred = new $.Deferred();
    OData.read(url, function (data, response) {
        deferred.resolve({ d: data });
    }, function (error) {
        deferred.reject(error);
    });
    return deferred;
}
 
function requestUpdate(model, selector) {
    var deferred = new $.Deferred();
    OData.request({
        requestUri: model.__metadata.uri,
        method: "POST",
        data: selector ? selector(model) : model,
        headers: {
            "If-Match": model.__metadata.etag,
            "X-HTTP-Method": 'MERGE'
        }
    }, function (data, response) {
        model.__metadata.etag = response.headers.ETag;
        deferred.resolve({ d: model });
    }, function (error) {
        deferred.reject(error);
    });
    return deferred;
}
 
function requestDestroy(model) {
    var deferred = new $.Deferred();
    OData.request({
        requestUri: model.__metadata.uri,
        method: "DELETE",
        headers: {
            "If-Match": model.__metadata.etag
        }
    }, function (data, response) {
        deferred.resolve(null);
    }, function (error) {
        deferred.reject(error);
    });
    return deferred;
}
 
function requestCreate(model, requestUri, selector) {
    var deferred = new $.Deferred();
    OData.request({
        requestUri: requestUri,
        method: "POST",
        data: selector ? selector(model) : model
    }, function (data, response) {
        deferred.resolve({ d: data });
    }, function (error) {
        deferred.reject(error);
    });
    return deferred;
}
 
function requestBatchUpdate(models, requestUriFormat, selector) {
    var deferred = new $.Deferred();
    OData.request({
        requestUri: "/_vti_bin/listdata.svc/$batch",
        method: "POST",
        data: getUpdateChangeRequests(models, requestUriFormat, selector)
    },
        function (data, response) {
            if (data.__batchResponses[0].__changeResponses[0].response) {
                deferred.reject({ response: data.__batchResponses[0].__changeResponses[0].response });
            } else {
                $.each(models, function (index, model) {
                    model.__metadata.etag = data.__batchResponses[0].__changeResponses[index].headers.ETag;
                });
 
                deferred.resolve({ d: { results: models } });
            }
        },
        function (error) {
            deferred.reject(error);
        },
        OData.batchHandler);
 
    return deferred;
}
 
function getUpdateChangeRequests(models, requestUriFormat, selector) {
    var changeRequests = [];
    $.each(models, function (index, model) {
        changeRequests.push({
            requestUri: kendo.format(requestUriFormat, model.Id),
            method: "MERGE",
            data: selector ? selector(model) : model,
            headers: {
                "If-Match": model.__metadata.etag
            }
        });
    });
    return {
        __batchRequests: [
            {
                __changeRequests: changeRequests
            }
        ]
    };
}
 
function requestBatchDestroy(models, requestUriFormat) {
    var deferred = new $.Deferred();
    OData.request({
        requestUri: "/_vti_bin/listdata.svc/$batch",
        method: "POST",
        data: getDestroyChangeRequests(models, requestUriFormat)
    },
        function (data, response) {
            if (data.__batchResponses[0].__changeResponses[0].response) {
                deferred.reject({ response: data.__batchResponses[0].__changeResponses[0].response });
            } else {
                deferred.resolve(null);
            }
        },
        function (error) {
            deferred.reject(error);
        },
        OData.batchHandler);
 
    return deferred;
}
 
function getDestroyChangeRequests(models, requestUriFormat) {
    var changeRequests = [];
    $.each(models, function (index, model) {
        changeRequests.push({
            requestUri: kendo.format(requestUriFormat, model.Id),
            method: "DELETE",
            headers: {
                "If-Match": model.__metadata.etag
            }
        });
    });
    return {
        __batchRequests: [
            {
                __changeRequests: changeRequests
            }
        ]
    };
}
 
function requestBatchCreate(models, requestUri, selector) {
    var deferred = new $.Deferred();
    OData.request({
        requestUri: "/_vti_bin/listdata.svc/$batch",
        method: "POST",
        data: getCreateChangeRequests(models, requestUri, selector)
    },
        function (data, response) {
            if (data.__batchResponses[0].__changeResponses[0].response) {
                deferred.reject({ response: data.__batchResponses[0].__changeResponses[0].response });
            } else {
                var results = $.map(data.__batchResponses[0].__changeResponses, function (changeResponse, index) {
                    return changeResponse.data;
                });
 
                deferred.resolve({ d: { results: results } });
            }
        },
        function (error) {
            deferred.reject(error);
        },
        OData.batchHandler);
 
    return deferred;
}
 
function getCreateChangeRequests(models, requestUri, selector) {
    var changeRequests = [];
    $.each(models, function (index, model) {
        changeRequests.push({
            requestUri: requestUri,
            method: "POST",
            data: selector ? selector(model) : model
        });
    });
    return {
        __batchRequests: [
            {
                __changeRequests: changeRequests
            }
        ]
    };
}
0
Clint
Top achievements
Rank 1
answered on 27 Sep 2012, 04:47 PM
Remco, sorry to be a bother, but what does new$ refer to?

0
Remco
Top achievements
Rank 1
answered on 27 Sep 2012, 05:37 PM
There is a space between new and $.deferred(). See http://api.jquery.com/category/deferred-object/
0
Clint
Top achievements
Rank 1
answered on 27 Sep 2012, 05:38 PM
BAHAHAHAHA!



Ever have one of those days?  Silly me.
0
Remco
Top achievements
Rank 1
answered on 27 Sep 2012, 07:02 PM
I think when you subscribe to this thread by email the spaces don't all appear in the email. It's better to view the code in a web browser.
0
Dhanavanth Venkatesan
Top achievements
Rank 1
answered on 07 Feb 2014, 04:31 PM
This is an awesome solution! Would you be able to attach the solution project to this post? It would be easy for us to incorporate it?
Thanks in advance!

Dan
0
Jason
Top achievements
Rank 1
answered on 01 Jun 2015, 05:18 PM
would love to see the final result of this effort. I tried to get this to work on my dev box, to no avail. 
Tags
Data Source
Asked by
Toby
Top achievements
Rank 1
Answers by
Clint
Top achievements
Rank 1
Remco
Top achievements
Rank 1
Toby
Top achievements
Rank 1
Dhanavanth Venkatesan
Top achievements
Rank 1
Jason
Top achievements
Rank 1
Share this question
or