In this post we will walk through adding a multi select system to the kendo UI Grid.  The final requirements are the following:

  • Be able to select multiple rows in the grid using a checkbox system
  • Be able to change pages and have the selections remembered from page to page
  • Be able to get the currently selected primary key values from the grid

The final result is going to look like this:

image

 

Before we get started you should review the following post as this builds on top of it:

http://sympletech.com/adding-on-row-click-to-the-kendoui-grid/

However I have made some changes to the core functionality of how I store the primary key of the data set which I will show you in a second.

But first lets look at our MVC Controller that will be serving us our data

KendoGridController

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using SympleLib.MVC;

namespace SympleJSLibTests.Controllers
{
    public class KendoGridController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public JsonResult GetData()
        {
            var people = Person.PeopleCollection.AsQueryable();
            return Json(people, JsonRequestBehavior.AllowGet);
        }
    }

    public class Person
    {
        public int id { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }

        public static IEnumerable<Person> PeopleCollection
        {
            get
            {
                var people = new List<Person>();
                ... Hard coded List of people
                return people.OrderBy(x=>x.LastName);
            }
        }
    }
}

You will need to include references to

And here is the markup page were going to be starting from.

Index.cshtml

<script type="text/javascript">
    $(function () {
        var gridDataSource = new kendo.data.DataSource({
            transport: {
                read: '/KendoGrid/GetData'
            },
            pageSize: 5
        });

        var $kgrid = $("#kGrid");
        var grid = $("#kGrid").kendoGrid({
            dataSource: gridDataSource,
            pageable: true,
            columns: [
                { field: "check_row", title: " ", width: 30, template: "<input class='check_row' type='checkbox' />" },
                { field: "LastName", title: "Last Name", filterable: true, sortable: true, width: 300 },
                { field: "FirstName", filterable: true, sortable: true },
            ]
        });
    });

</script>

<div id="kGrid"></div>

This is essentially the end result of the http://sympletech.com/adding-on-row-click-to-the-kendoui-grid/ guide with the single row selection functionality pulled out.

Initially our Grid looks like this:

image

First thing we want to do is to add the primary key from our dataset to each row in the grid so that we can extract it when a row is selected.  To do this were going to go through each row in the content portion of the grid, read a property off it named “data-uid” that kendo ui uses to store a guid that references the entry in the kendo datasource.  Ok so maybe that confusing so lets do a little show and tell.

This is what the gridDataSource object looks like that Kindo binds to the grid

image

And here is the html skeleton that Kendo renders out:

Kendo Rendering Skeleton

<div id="kGrid" class="k-grid k-widget" data-role="grid" tabindex="0" style="">
    <div class="k-grid-header">
        ...Header Table in here with colums etc
    </div>
    <div class="k-grid-content">
        <table class="k-focusable" cellspacing="0">
            <colgroup>...cols defined here</colgroup>
            <tbody>
                <tr data-uid="b4c0c426-94d5-4e10-840d-b2ebd6fb91bf">
                    ...Data Cols
                </tr>
            </tbody>
        </table>
    </div>
    <div class="k-pager-wrap k-grid-pager" data-role="pager">
        ...Pager in here
    </div>
</div>

So what we want to do is go through each row in the tbody and take that data-uid and use it to find the primary key in our dataset.  Then were going to add a property to that row to store the primary key so we can get it later.  I’m also going to add a class to the row named “kendo-data-row” just to make it easier to get the rows.

So back to our code, were going to add some code to the databound method of the kendo grid

        var grid = $("#kGrid").kendoGrid({
            ...
            dataBound: function (e) {
                //Go Through Each visible row and add a data property with the unique ID from the dataset
                $(".k-grid-content tbody tr").each(function () {
                    var $tr = $(this);
                    var uid = $tr.attr("data-uid");
                    var data_entry = _.find(gridDataSource._data, function (data_source) {
                        return data_source.uid === uid;
                    });
                    $tr.addClass('kendo-data-row').attr('data-sympleTech-KendoGrid-rowid', data_entry.id);
                });
            }
        });

As you can see we loop through each row and get the “data-uid” property.  We then use underscore.js to search the gridDatasource for the matching entry.  And finally we add our class and custom attribute to the row. 

With that were now ready to create an onclick handler for our checkbox row.  To start lets just paint and un-paint the row as we check the check box.

    $('.check_row').live('click', function (e) {
        var $cb = $(this);

        var $row = $cb.parents('.kendo-data-row').first();
        if ($cb.is(':checked')) {
            $row.addClass('k-state-selected');
        } else {
            $row.removeClass('k-state-selected');
        }
    });

Hopefully this is pretty straight foreword.  As you can see were using the custom class “kendo-data-row” we added earlier to make it easier to get the checkbox’s parent row we want to work with.

With this code in place you can now run the apps and see the rows change color as you click them.  However there is no data persistence, we can’t page the grid, and we can’t get the values of what’s selected.

As a data storage mechanism we will use a custom data attribute on the grid it’s self and imply push or remove the value from the attribute depending on weather the checkbox is clicked or not.  The code blow refractors the previous method adding in this functionality.

    $('.check_row').live('click', function (e) {
        var $kgrid = $("#kGrid");
        var $cb = $(this);

        //Get Current Selected Values
        var selectedVals = [];
        var selectedRowIds = $kgrid.attr('data-sympleTech-KendoGrid-selected');
        if (selectedRowIds != null) {
            selectedVals = selectedRowIds.split(',');
        }

        var $row = $cb.parents('.kendo-data-row').first();
        var rowId = $row.attr('data-sympleTech-KendoGrid-rowid');
        if ($cb.is(':checked')) {
            $row.addClass('k-state-selected');
            selectedVals.push(rowId);
        } else {
            $row.removeClass('k-state-selected');
            selectedVals = _.without(selectedVals, rowId);
        }

        //Set selected values to a custom data attribute on the grid
        $kgrid.attr('data-sympleTech-KendoGrid-selected', selectedVals);
    });

As you can see we set up an attribute named “data-sympleTech-KendoGrid-selected” that were going to store an array of the selected values in.  Push and prune as appropriate and then set back to the attribute.  The use of underscore’s without method again saves us some time and effort.  The more I use underscore the more I’m loving it.

So now as we check and uncheck we can inspect the grid and see that attribute is changing accordingly.

image

And as such the data can be extracted as easily as the following line of jQuey:

$("#kGrid").attr('data-sympleTech-KendoGrid-selected');

The one last item is if we page from one page to another the grid looses the selected rows.   Our attribute still has the data but the ui does not show what is selected.  To accomplish this functionality were going to move back into the databound function and add some code that will read our custom attribute and then look over the visible rows and add the selected class to the row as well as check the checkbox. 

To do this we should so something like this:

                //Mark any selected rows as selected (persists selections from page to page)
                var selectedRowIds = $kgrid.attr('data-sympleTech-KendoGrid-selected');
                if (selectedRowIds != null) {
                    var selectedRowIdArray = selectedRowIds.split(',');
                    var visibleRows = $kgrid.find('.kendo-data-row');
                    $(visibleRows).each(function () {
                        $row = $(this);
                        var rowID = $row.attr('data-sympleTech-KendoGrid-rowid');
                        if (_.contains(selectedRowIdArray, rowID)) {
                            $row.addClass('k-state-selected');
                            $row.find('.check_row').attr('checked', 'checked');
                        }
                    });
                }

And with that we can now page around our grid and all of our selected rows will be remembered.

Final Code

<script type="text/javascript">
    $(function () {
        var gridDataSource = new kendo.data.DataSource({
            transport: {
                read: '/KendoGrid/GetData'
            },
            pageSize: 5
        });

        var $kgrid = $("#kGrid");
        var grid = $("#kGrid").kendoGrid({
            dataSource: gridDataSource,
            pageable: true,
            columns: [
                { field: "check_row", title: " ", width: 30, template: "<input class='check_row' type='checkbox' />" },
                { field: "LastName", title: "Last Name", filterable: true, sortable: true, width: 300 },
                { field: "FirstName", filterable: true, sortable: true },
            ],
            dataBound: function (e) {
                //Go Through Each visible row and add a data property with the unique ID from the dataset
                $(".k-grid-content tbody tr").each(function () {
                    var $tr = $(this);
                    var uid = $tr.attr("data-uid");
                    var data_entry = _.find(gridDataSource._data, function (data_source) {
                        return data_source.uid === uid;
                    });
                    $tr.addClass('kendo-data-row').attr('data-sympleTech-KendoGrid-rowid', data_entry.id);
                });

                //Mark any selected rows as selected (persists selections from page to page)
                var selectedRowIds = $kgrid.attr('data-sympleTech-KendoGrid-selected');
                if (selectedRowIds != null) {
                    var selectedRowIdArray = selectedRowIds.split(',');
                    var visibleRows = $kgrid.find('.kendo-data-row');
                    $(visibleRows).each(function () {
                        $row = $(this);
                        var rowID = $row.attr('data-sympleTech-KendoGrid-rowid');
                        if (_.contains(selectedRowIdArray, rowID)) {
                            $row.addClass('k-state-selected');
                            $row.find('.check_row').attr('checked', 'checked');
                        }
                    });
                }
            }
        });
    });

    $('.check_row').live('click', function (e) {
        var $kgrid = $("#kGrid");
        var $cb = $(this);

        //Get Current Selected Values
        var selectedVals = [];
        var selectedRowIds = $kgrid.attr('data-sympleTech-KendoGrid-selected');
        if (selectedRowIds != null) {
            selectedVals = selectedRowIds.split(',');
        }

        var $row = $cb.parents('.kendo-data-row').first();
        var rowId = $row.attr('data-sympleTech-KendoGrid-rowid');
        if ($cb.is(':checked')) {
            $row.addClass('k-state-selected');
            selectedVals.push(rowId);
        } else {
            $row.removeClass('k-state-selected');
            selectedVals = _.without(selectedVals, rowId);
        }

        //Set selected values to a custom data attribute on the grid
        $kgrid.attr('data-sympleTech-KendoGrid-selected', selectedVals);

        
    });

</script>

<div id="kGrid"></div>

I hope you have found this guide helpful.  This functionality has also been added into my sympleJSLib that’s up on GitHub here : https://github.com/sympletech/SympleLib

As always Questions and comments are welcome and appreciated.

Tagged with →  
Share →

12 Responses to Kendo UI Grid Multiselect with Checkboxes and Multipage Support

  1. Jenna says:

    Hi!
    Thanks for a nice post!!
    I tried to use the idea, but had some problems.
    1. I can select only a single row, when I select the next checkbox, the previous selection vanishes.
    2. If I select a row – checkbox remains unselected.
    Maybe I ‘m just missing something, or this was not considered in your implementation :) )
    I would appreciate an answer and any advise!

    Thanks,
    Jenna

  2. Sri says:

    Hi…I want to update the Kendo grid and stock chart When we check the check boxes…. Please any one help me

  3. totes says:

    i’m using a grid that i create on the server side using cshtml.
    i find the grid won’t check the check box for you as is seemingly implied by the author here. the author is applying the selected css style, but i find the grid does that for you when you click the row, but it won’t check the check box for you; so you can change the code above accordingly to something like $cb.prop(‘checked’, !$cb.is(‘:checked’)) //toggle check state of clicked row. now the problem is that grid will remove the selected style for the previously selected/checked row. right now it seems like every time i click on a row, i’m going to have to iterate through the rest and change the style back to selected style if the check box is checked……

  4. Thomas says:

    Thanks for this tutorial. I used it with some expansions to deal with a ‘select all’ checkbox in the header row. One point which may be helpful to others:

    I would advise that instead of using the JQuery click event:
    $('.check_row').live('click', function (e) { ... } );

    Instead substitute it for the Jquery change event:
    $('.check_row').live('change', function (e) { ... } );

    This avoids all sorts of problems when the user double click checkboxes in the grid very quickly in IE8 and IE9. I was getting the click event being fired twice when this happened. The ‘click’ event did work fine in Chrome and Firefox though. Using JQuery 1.8.3 in this project.

    • Bala says:

      I am looking at this functionality with the enhancement you have done(add checkbox to header). If you have demo code, pls share. This will be really helpful to understand KendoUI more.

  5. Rod says:

    Thank you! This is exactly what I was looking for.

    ROD>

  6. messfilm says:

    Hi~, I found something…

    if grid has groupd-column, then need this…

    $(“.k-grid-content tbody tr”).not(“.k-grouping-row, .k-group-footer”).each(function () {

    });

    thank you anyway~ Have a nice day~

    • dlewis says:

      Thanks for contributing!

    • Matthew Garber says:

      I ran into this problem as well. I solved it a bit different before noticing your post. This should ensure we are only looping through rows with data in them (just in case Kendo has other rows that show up that aren’t .k-grouping-row or .k-group-footer):

      $(“.k-grid-content tbody tr[data-uid]“).each(function () {

      });

  7. april fair pahayahay says:

    i have a grid header checkbox. upon clicking, all tbody checkboxes should be changed to check/unche. yet the next pages won’t receive the event of the check all checkboxes..Please help. I am new to kendo. thank you. and more power!

  8. Radek says:

    Thanks a lot man!
    Cool workaround.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>