KendoUI Grid locally edited JSON


Defining the problem

I have a JSON array that I want to modify but even that is provided by a web server I don’t want to have every modification automatically synchronized (question of performance).
I do this, of course, because it is a small array with few entries and very few (expected) modifications. So there is no fear of loosing data if something goes wrong (even that it should not).
My requirements are:

  1. Display data in a grid.
  2. Edit the content of the grid cells.
  3. Remove rows.
  4. Some columns are calculated from other values in the same row.
  5. Being able to save modifications on demand.
  6. Should be able to revert any modification to the previous saving point.

KendoUI Grid and JSON edited locally

Kendo UI Grid is able to display bound JSON data, allowing you modify it and even save and cancel changes but for some sort of reason when I do this, I get duplicated rows when I do sync + cancel.
For this reason I end up defining my own commands for adding new records, delete, save changes and cancel changes.
The steps that I follow are:

  1. Define own commands in the toolbar.
  2. Define methods associated with the toolbar commands.
  3. Define methods for saving and restoring records (implement save and cancel changes).
  4. Put all pieces together

Define own commands in the toolbar

As shown in my post I just need to set toolbar to:

toolbar:[
    {
        name:"insert",
        text:"",
        className:"k-grid-insert-expense",
        imageClass:"k-icon ob-icon-only k-i-plus"
    },
    {
        name:"delete",
        text:"",
        className:"k-grid-delete-selected",
        imageClass:"k-icon ob-icon-only k-delete"
    },
    {
        name:"sync",
        text:"",
        className:"k-grid-sync-changes",
        imageClass:"k-icon ob-icon-only k-i-tick"
    },
    {
        name:"cancel",
        text:"",
        className:"k-grid-cancel-changes",
        imageClass:"k-icon ob-icon-only k-retry"
    }
]

Where we defines buttons for inserting a row, deleting the selected row, save changes (sync) and cancel changes. I’m using buttons without text since I like them more (see this post on how to do it).
Now, our interface looks like:

Define methods associated with the toolbar commands

What we do is bind a click event to toolbar.button.className.

// Custom grid command : insert
$(".k-grid-insert-expense").bind("click", function (ev) {
});

// Custom grid command : delete
$(".k-grid-delete-selected").bind("click", function (ev) {
});

// Custom grid command : sync
$(".k-grid-sync-changes").bind("click", function (ev) {
});

// Custom grid command : cancel
$(".k-grid-cancel-changes").bind("click", function (ev) {
});

Define methods for saving and restoring records (implement save and cancel changes)

This JavaScript class saves a copy of and Object on creation and then allows to save a new copy or retrieve the last saved one.

// Class for saving and restoring an Object
function SaveObject(obj) {
    // Saved Object
    this.object = null;

    // Save Object
    this.set = function (obj) {
        this.object = obj;
    };

    // Retrieve saved Object
    this.get = function () {
        return this.object;
    };
    this.set(obj);
}

The way of using it is invoking set when clicking sync button and invoking get when clicking on cancel button.
For sync’ing we get entries from stocksDataSource and save this value. We finish it invoking success for notifying the DataSource about the new data.

// Custom grid command : sync
$(".k-grid-sync-changes").bind("click", function (ev) {
    entries = stocksDataSource.data();
    savedArray.set(entries);
    stocksDataSource.success(entries);
});

For restoring, we set entries to saved data and then invoke cancelChanges from KendoUI Grid object.

// Custom grid command : cancel
$(".k-grid-cancel-changes").bind("click", function (ev) {
    entries = savedArray.get();
    grid.cancelChanges();
});

Put all pieces together

This is almost done but we have to implement add record and delete selected record.
Adding record is invoking addRow method in grid object.

// Custom grid command : insert
$(".k-grid-insert-expense").bind("click", function (ev) {
    grid.addRow();
});

Removing is detecting which row is selected and then invoking removeRow in grid object.

// Custom grid command : delete
$(".k-grid-delete-selected").bind("click", function (ev) {
    var selected = grid.select();
    if (selected && selected.length > 0) {
        grid.removeRow(grid.select());
    }
});

And that’s all folks!!! You can see it running here.

Advertisements

28 thoughts on “KendoUI Grid locally edited JSON

  1. Hi onabai,

    I created Edting page using kendo ui.

    I am passing value as json format.

    My issue is how I will get all updated value on save changes button on client side.

    I enclose my code here.

    Please help me.

    $(document).ready(function () {
        var data = [
            { id: 1, text: "text 1", position: 0 },
            { id: 2, text: "text 2", position: 1 },
            { id: 3, text: "text 3", position: 2 }
        ]
    
        var dataSource = new kendo.data.DataSource({
            data: data,
            schema: {
                model: {
                    id: "id",
                    fields: {
                        id: { type: "number", editable: false },
                        text: { type: "string" },
                        position: { type: "number" }
                    }
                }
            }
        });
        var grid = $("#grid").kendoGrid({
            dataSource: dataSource,
            scrollable: false,
            editable: true,
            toolbar: ["create", "save", "cancel"],
            columns: [
                {
                    field: "id",
                    title: "Id"
                },
                {
                    field: "text",
                    title: "Name",
    
    
                },
                {
                    field: "position",
                    title: "Position",
    
                }]
    
        }).data("kendoGrid");
    
    });
    

    Thanks,

    Piyush

    • You have to define the transport function and they depend on your server, it might be an url or you might need a function for doing some pre-processing and then send it to the server user (for example) ajax.
      A possible DataSource definition would be something like:

      var dataSource = new kendo.data.DataSource({
          data  :data,
          transport: {
              read: function(command) {
                  // Get the data from the server and then invoke
                  command.success(data);
                  // Where data is the data received from the server
                  // but transformed into the format used here.
              },
              update: function(command) {
                  // command.data is the modified row
                  console.log(command.data);
                  // send it to the server
              }
          },
          schema:{
              model:{
                  id    :"id",
                  fields:{
                      id      :{ type:"number", editable:false },
                      text    :{ type:"string" },
                      position:{ type:"number" }
                  }
              }
          }
      });
      
  2. Hi onabai,

    I had create kendo grid,and created button to copy data from kendo grid.
    my issue is here grid footer also selecting without footer how to copy?
    here is the jsfiddle jsfiddle.net/MG89G/239

    • I see your code where you show a grid and if I click on filter you apply the filter to the grid data and if I click on select gridSelectedItem you select (at HTML level) the grid. But I do not get what you are trying / willing to do. Are you trying to select (at KendoUI level) all the (visible) entries of the grid?

      • I dont need filter code.just i wnat to copy the selected data on button click.But i want to copy the data without footer.

      • Devi, I see your code but I do not get what you are trying to get so it’s hard for me saying what is wrong and fix it. If you want to select DOM elements for copying them to the clipboard, then you can access the body of the grid by selecting the tbody element.

        <input type="button" value="gridSelectedItem" onclick="selectElementContents( document.getElementById('grid').getElementsByTagName('tbody')[0]);"/>
        
      • The header of the grid is under thead HTML element. You can modify your selection function for selecting one (thead) and then the other (tbody).

      • What I mean is;

        function selectContents(id) {
            var body = document.body;
            var grid = $("#" + id);
            var tHead = $(".k-grid-header", grid).get()[0];
            var tFooter = $(".k-grid-pager", grid).get()[0];
            var range;
            var sel;
            if (document.createRange && window.getSelection) {
                range = document.createRange();
                sel = window.getSelection();
                try {
                    range.setStart(tHead, 0);
                    range.setEnd(tFooter, 0);
                    sel.addRange(range);
                } catch (e) {
                    range.selectNode(el);
                    sel.addRange(range);
                }
            } else if (body.createTextRange) {
                range = body.createTextRange();
                range.moveToElementText(el);
            }
        }
        
        <input type="button" class="k-button" value="Select" onclick="selectContents('grid');"/>
        

        Warning, I’ve only modified the try block, you should do the same for catch and body.createTextRange cases.

  3. sorry.i didn’t understood.
    i had return below function can you please modify here.

    function selectElementContents(el) {
        var body = document.body, range, sel;
        if (document.createRange && window.getSelection) {
            range = document.createRange();
            sel = window.getSelection();
    
            sel.removeAllRanges();
            try { range.selectNodeContents(el);
    
                sel.addRange(range);
            }
            catch (e)
            {
                range.selectNode(el);
                sel.addRange(range);
            }
        }
    
        else if (body.createTextRange) {
            range = body.createTextRange();
            range.moveToElementText(el);
        }
    }
    
  4. Hi onabai
    How To Make My Website Flexible Across a Range Of Target Devices,means how to My Website Flexible to all screen sizes in javascript (or) jquery (or) HTML and CSS.Can you please help for this issue.
    Subject: Question

    • Hi Divya,

      Your question is a valid and good question but it doesn’t have anything to do with the original entry of this blog where you posted it.

      Another point is that you should probably ask this same question to a much broader audience since this are going to be just my 2 cents to the topic.

      There is not such magic framework, programming language,… otherwise every company would be using it instead of having to waste money implementing it multiple times.

      Why it does not exist, simply there are too many cases: OS, browsers, languages, screen resolutions, users,…

      You should decide which is your target audience and then look for the best tool.

      Of course JavaScript + HTML5 + CSS3 will be very portable but the resolution is different so you can (and should) show more or less information on screen so you have to keep it in mind in order to get the best UX.

      These are my two cents

  5. Hi onbai..
    I have one problem in Kendo datepicker in grid filter,I have set the date picker in filter then my date format displays in 06/10/2010 00:00 this format but my actual data is 06/10/2010 02:35 ..Please help me….

  6. Hi Onobai, great article, thanks for sharing. Sorry for the tenuously related question, but do you know of any way to save as a batch in this way, but have the simple kendo grid inline edit UX? I have been looking at articles for hours and am not hopeful, but perhaps you can help.

    • Hi Gus,

      Not sure I understand what you are trying to do. Is it that you want to edit locally (as if you were offline) and then (when sync is pressed) send all changes / all data to the server?

      Thank you and Regards

      • Thanks for the quick reply! You understand perfectly, that is exactly it, but with the requirement that the UX be the same as the standard inline edit using add/update/cancel buttons but not submitting the server until sync or similar is called. All the literature I have seen for batch editing seems to be for grids in in-cell mode.

      • Do you want to send all data or only the changes? I might try to prepare some example. I use something similar for saving local edited data into Dropbox Datastore but in that case I save everything again as a stringified JSON.

      • I suppose it would be better to be able to submit only changes but the grid will work with very small amounts of data so would not be too bad if it submitted everything.

        I am currently looking at this code-library:

        http://www.telerik.com/support/code-library/save-all-changes-with-one-request

        It looks like it could be promising if I can maybe just stub out the create/update/delete controller methods and submit everything onclick later. I am just unsure if the grid will still see the items as _destroyed or isNew if I allow the request to hit the server? I imagine the grid might see that action as complete if you see what I mean. Will keep testing, any ideas would be very much appreciated. Thanks again.

      • Yep, I think that’ll do it. The records are still in the datasource and holds its _destroyed/isNew/dirty properties even after the request at the server has completed. So I guess just have the controller methods return null and action from whatever click and some other controller method. Does that sound about right?

  7. Yep, I think that’ll do it. The records are still in the datasource and holds its _destroyed/isNew/dirty properties even after the request at the server has completed. So I guess just have the controller methods return null and action from whatever click and some other controller method. Does that sound about right?

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