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

How to fomat a Kendo Datagrid request into REST format?

8 Answers 1214 Views
Data Source
This is a migrated thread and some comments may be shown as answers.
Robert
Top achievements
Rank 1
Robert asked on 12 Aug 2013, 02:06 AM
Hi,

I'm building off the KendoGrid "inline editing" demo. I want the destroy url to be simply:

/api/company/delete/id ( for example, /api/company/delete/17 )

What I'm getting is the URL I specified with the querystring '?id=17' appended

/api/company/delete?id=17

So how do I get the request in REST format instead of URI format?

Many thanks in advance,

Robert

Here's my code:
$(document).ready(function() {
       var dataSource = new kendo.data.DataSource({
           transport: {
               read:  {
                   url: "/api/company/get",
                   dataType: "json"
               },
               destroy: {
                   url: "/api/company/delete",
                   dataType: "json"
               },
               parameterMap: function(options, operation) {
                   if (operation !== "read" && options.models) {
                       return {models: kendo.stringify(options.models[0].id)};
                   }
               }
           },
           batch: true,
           pageSize: 20,
           schema: {
               model: {
                   id: "id",
                   fields: {
                       id: { type: "number" },
                       name: { type: "string" },
                       admin_id: { type: "number" },
                       address_id: { type: "number" },
                       status_msg: { type: "string" }
                   }
               }
           }
       });
});
 

8 Answers, 1 is accepted

Sort by
0
Robert
Top achievements
Rank 1
answered on 12 Aug 2013, 06:06 PM
Hello,

Okay so no answer to this question so far? I've now figured out half of it myself, which that I need to set the type: "DELETE" in my datasource transport destroy function. But I still don't see how to pass the value of the selected row into the URL?

I find myself a bit aggravated that the use case of wanting to invoke a REST API isn't documented in a single example. Is this considered to be an unusual case? If there were an example I would have found it and now I wouldn't have to ask this follow up question and you wouldn't have to answer it.

Can someone please help me out here? Now that I'm getting the URL without the '?' (suitable for invoking my REST API), how do I pass the arguments to it?

Thank you,

Robert
0
Andrew
Top achievements
Rank 1
answered on 12 Aug 2013, 08:32 PM
Hi, I struggled with this a little myself. Our application uses Jersey/JAX-RS to implement REST services on the server, and I needed to setup a generic DataSource extension that would be configured to work with our standard REST endpoints. The configuration below supports a standard design which supports CRUD using the following URL patterns
Read list of items: GET "http://server/items"
Create a new item: POST to "http://server/items"
Read a single item: GET "http://server/items/{itemId}"
Update a single item: PUT to "http://server/items/{itemId}"
Destroy a single item: DELETE to "http://server/items/{itemId}"

Notice that the transport options support a function for "url", so you can execute some code to build a URL for a single item. In our case, the server generates a URL for each item in the "idUrl" property when it generates the JSON for the item.

Hope this helps,

    var DataStore = kendo.data.DataSource.extend({
 
        init: function (options) {
            function getIdentityUrl(data) {
                     return data.idUrl;
                 }
            options.transport = {
                read: {
                    url: options.url,
                    type: 'GET',
                    dataType: 'json'
                },
                create: {
                    url: options.url,
                    type: 'POST',
                    dataType: 'json',                   // The response content type
                    contentType: MIME_JSON,             // The request content type
                    processData: false
                },
                update: {
                    url: getIdentityUrl,
                    type: 'PUT',
                    dataType: 'json',
                    contentType: MIME_JSON,
                    processData: false
                },
                destroy: {
                    url: getIdentityUrl,
                    type: 'DELETE',
                    dataType: 'json'
                },
                parameterMap: parameterMap
            };
 
            kendo.data.DataSource.prototype.init.call(this, options);
        },
});
0
Robert
Top achievements
Rank 1
answered on 12 Aug 2013, 10:53 PM
Hi Andrew,

Thank for the detailed response. It does seem that the key is to use a function to build the URL. I still have no idea what the parameterMap is used for. I thought it was used for this very purpose, but I guess not.

I see that you're setting parameterMap: parameterMap. would be kind enough to tell me what that means? My parameterMap property doesn't seem to do anything, meaning that the code below will work with our without it.

Thanks again,

Robert
var dataSource = new kendo.data.DataSource({
            transport: {
                read:  {
                    url: "/admin/getcompanies",
                    dataType: "json"
                },
                destroy: {
                    url: function (options) {
                        return '/api/company/delete/' + options.models[0].id;
                    },
                    type: "DELETE"
                },
                parameterMap: function(options, operation) {
                    if (operation !== "read" && options.models) {
                        var company_id = options.models[0].id;
                        console.log(company_id);
                        return company_id;
                        //return {id: kendo.stringify(options.models[0].id)};
                    }
                }
            },
            batch: true,
            pageSize: 20,
            schema: {
                model: {
                    id: "id",
                    fields: {
                        id: { type: "number" },
                        name: { type: "string" },
                        admin_id: { type: "number" },
                        address_id: { type: "number" },
                        status_msg: { type: "string" }
                    }
                }
            }
        });
0
Andrew
Top achievements
Rank 1
answered on 13 Aug 2013, 04:12 PM
Right, the parameterMap function handles query string arguments or entity bodies depending on the type of request. Our function translates query parameters for paging and sorting based on the names we use in our server implementation. For POST/PUT, it handles JSON-encoding the data object.

BTW, in REST design, you generally don't want verbs in your URLs. The verbs are the HTTP method. For example, I would change the URL for your companies list to "/admin/companies", not "/admin/getcompanies".

/**
 * Called by the DataSource to convert parameters/body into the appropriate format.
 * @param data
 * @param requestType One of "read", "create", "update", or "destroy"
 * @returns {*}
 */
function parameterMap(data, requestType) {
    if (requestType === 'read') {
        if (!_.isUndefined(data.skip)) {
            data['pg_start'] = data.skip;   // "skip" is what kendo calls the start index
            delete data.page;
            delete data.skip;
        }
        if (!_.isUndefined(data.pageSize)) {
            data['pg_size'] = data.pageSize;    // "pageSize" and "take" mean the same thing
            delete data.take;
            delete data.pageSize;
        }
        if (_.isArray(data.sort)) {
            // The server currently supports one level of sorting
            data['sort_by'] = data.sort[0].field;
            data['sort_dir'] = data.sort[0].dir;
            delete data.sort;
        }
    }
    if (requestType === 'create' || requestType === 'update') {
        return JSON.stringify(data);
    }
    return data;
}
0
Robert
Top achievements
Rank 1
answered on 19 Aug 2013, 01:23 AM
Hi Andrew,

I've got the Kendo Grid working with the remote data source, and the request parameters into the right format. Thank you for the help. By the way, do you work for Telerik?

Now I've changed the code such that the data source for the Grid is an MVVM view model, and the data source for the view model is the remote data source. The good news is that this all seems to be working.

The question is, how can I print out the state of the view model? (I know the view model values are shown in the grid, but I want to print them out -  as they are in the MVVM widget binding example. But mine isn't formatted correctly, and it shows way to much information. I only want the contents of the Grid.

This works. This is in my html file:
<pre>
    {
    gridSource: [<span data-bind="text: displayGridSource"></span>    ]
    }
</pre>

Here's the 'displayGridSource' function, which is defined in view model definition. You'll see it in the complete script code below.
displayGridSource: function() {
    return kendo.stringify(this.get("gridSource"));
}

But the output is hard to read, JSON encoded, and contains too much information (I'm happy this is working, but it's not what I want):
{
    gridSource: [{"options":{"data":[],"schema":{"model":{"id":"id","fields":{"id":{"type":"number"},"name":{"type":"string"},"admin_id":{"type":"number"},"address_id":{"type":"number"},"status_msg":{"type":"string"}}}},"serverSorting":false,"serverPaging":false,"serverFiltering":false,"serverGrouping":false,"serverAggregates":false,"batch":true,"transport":{"read":{"url":"/api/company","dataType":"json","type":"GET"},"destroy":{"dataType":"json","type":"DELETE"}},"pageSize":20},"_map":{},"_prefetch":{},"_data":[{"id":1,"company_type":"1","name":"Acme","admin_id":1,"address_id":1,"status_msg":"unverified"}],"_ranges":[{"start":0,"end":1,"data":[{"id":1,"company_type":"1","name":"Acme","admin_id":1,"address_id":1,"status_msg":"unverified"}]}],"_view":[{"id":1,"company_type":"1","name":"Acme","admin_id":1,"address_id":1,"status_msg":"unverified"}],"_pristine":[{"id":1,"company_type":"1","name":"Acme","admin_id":1,"address_id":1,"status_msg":"unverified"}],"_destroyed":[],"_pageSize":20,"_page":1,"_group":[],"_total":1,"_events":{"change":[null,null,null],"progress":[null],"error":[null]},"transport":{"options":{"read":{"url":"/api/company","dataType":"json","type":"GET"},"destroy":{"dataType":"json","type":"DELETE"}},"cache":{}},"reader":{},"_skip":0,"_take":20,"_requestInProgress":false,"_aggregateResult":{}}    ]
}

This is what I want:
<pre>
    {
    id: <span data-bind="text: viewModel._view.id"></span>,
    company_id: <span data-bind="text: company_type"></span>,
    name: <span data-bind="text: name"></span>,
    admin_id: <span data-bind="text: admin_id"></span>,
    address_id: <span data-bind="text: address_id"></span>,
    status_msg: <span data-bind="text: status_msg"></span>,
    }
</pre>

But none of the values are printed:
{
    id: ,
    company_id: ,
    name: ,
    admin_id: ,
    address_id: ,
    status_msg: ,
}

Here's the complete code. There are two scripts, one is included on the main page, and one on a page specific page. The main javascript is always loaded, while
the page specific javascript is loaded once I click the "list companies" link. Note that the viewModel (and the data source defined therein) is defined in the main script, and the Grid is defined in the page specific page.

Main javascript
viewModel = kendo.observable({
        gridSource: new kendo.data.DataSource({
            transport: {
                read:  {
                    url: "/api/company",
                    dataType: "json",
                    type: "GET"
                },
                destroy: {
                    url: function (options) {
                        return '/api/company/' + options.models[0].id;
                    },
                    dataType: "json",
                    type: "DELETE"
                },
                parameterMap: function(options, operation) {
                    if (operation !== "read" && options.models) {
                        var company_id = options.models[0].id;
                        console.log(company_id);
                        return company_id;
                    }
                }
            },
            batch: true,
            pageSize: 20,
            schema: {
                model: {
                    id: "id",
                    fields: {
                        id: { type: "number" },
                        name: { type: "string" },
                        admin_id: { type: "number" },
                        address_id: { type: "number" },
                        status_msg: { type: "string" }
                    }
                }
            }
        }),
        displayGridSource: function() {
            return kendo.stringify(this.get("gridSource"));
        }
    });
    kendo.bind($('#base'), viewModel);

And here's the dynamically loaded content, which includes the HTML and the page specific java script:
<div id="right-top1">
    <div id="company_grid" style="height: 380px"></div>
</div>
 
<script>
    $(document).ready(function() {
        $("#company_grid").kendoGrid({
            dataSource: viewModel.gridSource,
            pageable: true,
            columns: [
                {field:"id", title: "Company ID", width: "20px"},
                {field: "name", title: "Company Name", width: "60px"},
                {field: "admin_id", title: "Admin ID", width: "20px"},
                {field: "address_id", title: "Address ID", width: "20px"},
                {field: "status_msg", title: "Status", width: "20px"},
                {command: ["destroy"], title: " ", width: "30px"}
            ],
            editable: "inline"
        });
    });
</script>



0
Andrew
Top achievements
Rank 1
answered on 20 Aug 2013, 12:27 PM
Robert, glad I can help - I don't work for Telerik, but I enjoy using their library. :-)

I don't have as much experience with Kendo's MVVM, so I'm not sure about how well it supports looping through a template. In another MVVM library like knockout, you can bind an array of objects using syntax like:
<table>
    <thead>
        <tr><th>First name</th><th>Last name</th></tr>
    </thead>
    <tbody data-bind="foreach: people">
        <tr>
            <td data-bind="text: firstName"></td>
            <td data-bind="text: lastName"></td>
        </tr>
    </tbody>
</table>
The above template would be bound using a model that has a property "people" containing an array of objects.
In your case, you can use "this.get("gridSource").data()" to get the array of objects in the data source. If Kendo MVVM supports foreach, then you might be able to pass that array directly to kendo.bind(). If not, you could use another MVVM library or just write jQuery code by hand that iterates over the array and creates a <pre> tag based on what you have above.

Good luck!
0
Zachary
Top achievements
Rank 1
answered on 12 Dec 2013, 01:31 AM
This forum thread was extremely helpful in determining how to get the transport URLs into a format suitable for a Web API controller. Particularly when Robert mentioned the key is to use a function to build the URL.

var dataSource = new kendo.data.DataSource({
    transport: {
        read: {
            url: "api/products",
            type: "GET",
            dataType: "json"
        },
        create: {
            url: "api/products",
            type: "POST",
            dataType: "json"
        },
        update: {
            url: function (e) {
                return "api/products/" + e.productID;
            },
            type: "PUT",
            dataType: "json"
        },
        destroy: {
            url: function (e) {
                return "api/products/" + e.productID;
            },
            type: "DELETE",
            dataType: "json"
        }
    }
});
0
ian
Top achievements
Rank 1
answered on 21 Aug 2014, 10:35 AM
You da man Andrew!!!!
Tags
Data Source
Asked by
Robert
Top achievements
Rank 1
Answers by
Robert
Top achievements
Rank 1
Andrew
Top achievements
Rank 1
Zachary
Top achievements
Rank 1
ian
Top achievements
Rank 1
Share this question
or