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

Select across "pages" in virtual scrolling grid

5 Answers 863 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Kenny
Top achievements
Rank 1
Kenny asked on 24 Apr 2013, 10:35 PM
I have a grid set up with virtual scrolling. I want to be able to use "multiple row" selection but it doesn't seem to be behaving as expected. Currently selecting a row on one "page", scroll to another page, and shift-clicking another row results in some fairly random selections. 

I basically need to be able to click one row, scroll down a few "pages" and then shift-click another row and have everything in between selected. 

I have a feeling this is not supported but I just want to double check.  

5 Answers, 1 is accepted

Sort by
0
Dimiter Madjarov
Telerik team
answered on 26 Apr 2013, 10:26 AM
Hello Kenny,


Each time a new data is loaded with the virtual scrolling enabled, a dataBound is performed. The default behavior of the Grid is to not maintain the previous selection when the data is bounded. Here is a sample custom implementation, in which I am using a custom collection to store the IDs of all selected items. On each dataBound I am traversing the items in the current dataSource view, for each of them I check if it's in the collection and select it if needed.

E.g.
<script>
    var allSelected = {};
    function change(e) {
        var grid = $("#Grid").data("kendoGrid");
        var gridView = grid.dataSource.view();
        var selection = this.select();
  
        for (var i = 0; i < gridView.length; i++) {
            var id = gridView[i].OrderID;
            if (allSelected[id] == true) {
                var isSelectedNow = false;
                for (var j = 0; j < selection.length; j++) {
                    var row = grid.dataItem(selection[j]);
                    if (row.OrderID == id) {
                        isSelectedNow = true;
                        break;
                    }
                }
                if (!isSelectedNow) {
                    allSelected[id] = false;
                }
            }
        }
  
        for (var i = 0; i < selection.length; i++) {
            allSelected[grid.dataItem(selection[i]).OrderID] = true;
        }
    }
  
    function dataBound(e) {
        var grid = $("#Grid").data("kendoGrid");
        var pageData = grid.dataSource.view();
        var newSelection = [];
        for (var i = 0; i < pageData.length; i++) {
            if (allSelected[pageData[i].OrderID]) {
                newSelection.push(grid.tbody.find(">tr[data-uid='" + pageData[i].uid + "']"));
            }
        }
        grid.select(newSelection);
    }
</script>

I hope this example was helpful for you. Feel free to modify it according to your needs.

 

Regards,
Dimiter Madjarov
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Patrick
Top achievements
Rank 1
answered on 27 Mar 2014, 09:34 AM
We've tried the above implementation, but it must be said, that this is not a full answer to the request above. The mentioned 'fairly random selections' are there as there is lack of following detections in the change event:
- type of click (shift-click, regular click or control-click)
- element (row or cell) on which the click took place
- basically, there is no DOM information available

So in the case of several control-clicks and a shift-click there is no way of telling what row was selected last, where to start the shift-click, or where to end it. I find multiple row selection not te be working with virtual scrolling, neither can it be fully implemented by the change event alone.

Hence, if you want to fully implement multiple row selection in combination with virtual scrolling over "pages" you should either
- load all the data at once (which isn't convenient with large data)
- prevent the regular click events on the grid and implement them yourself manually
 
0
Dimiter Madjarov
Telerik team
answered on 28 Mar 2014, 08:37 AM
Hi Patrick,

The Grid selection is working only on rendered DOM elements by adding the k-state-selected class to them. When the scrolling is performed and the Grid binds to a new set of data, the selection is not maintained as the previously selected rows does not exist anymore.

Regarding the "random selections" I am not sure that I understand what exactly are you referring to. By design when the shift button is pressed, the Grid selects all rows from the first one to the current one (if there were no previously selected rows) or all rows from the last selected one to the current one.

Indeed information about the pressed shift or control buttons is not available in the change event and this should be manually implemented. If you consider that this would be useful addition to the widget, please post it as a suggestion in our Feedback portal.

Regards,
Dimiter Madjarov
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
0
Kenny
Top achievements
Rank 1
answered on 31 Mar 2014, 05:11 PM
Loading all the data is not an option for us. We ended up writing a wrapper for the Kendo Grid to handle a number of features that we need. This includes manually handling the multiple selection features. We keep a record of row indexes to data object keys and selected indexes/keys. For larger selections where data has not yet been loaded between the start and end of the selection we make the Kendo datasource load the data on demand. As the grid is scrolled we manually check the our collection of selected keys and apply k-state-selected as needed to re-select the row. 
0
Patrick
Top achievements
Rank 1
answered on 01 Apr 2014, 08:17 AM
Hi, Kenny,

Preloading all the data is not required. I've built a custom implementation that works in my case, and might be useful to others, too. I added a rownumber field to the recordset from the datasource, retrieved by the datasource (called 'rn' in the example below). This rownumber would be enough to calculate what items are selected.

Of course, with little adaptations, given the pagenumber and the limits, this rownumber could be precalculated in the databound event, prior to binding the next code. In that case the code below would work visually for the grid, however, the developer would need some sort of serverside logic do determine the corresponding database fields.

Furthermore, following code is linked to the table rows (tr elements), but a similar approach could be used to implement this for td's.

Hoping it might help someone else, here is my code:

var gridData = $("#grid").data("kendoGrid");
var intStartRN = 0;                 // start rownumber
var intEndRN = 0;                    // end rownumber
var nextStartRN = 0;                // if ctrl-clicked, this will be the next startpoint for shift-click
var arrSelectedRows = [];             // Rows to be selected
var arrCCSelectedRows = [];            // ctrl click selected rows (global)
var arrCCUnSelectedRows = [];        // ctrl click unselected rows (global)

gridData.bind('dataBound', function()
{
    // First check what items to select from previous selections
    gridData.clearSelection(); // always clear selection

    if(elements = $("#gridOutboundOrders").find("table.k-selectable tr.k-master-row"))
    {
        if(intStartRN>0)    // User has previously clicked
        {
            arrSelectedRows = []; // empty previous selection
            $.each(elements, function(index, objTr) // walk through all selectable rows
            {
                currentUid    = $(objTr).attr("data-uid"); // determine uid of current row
                currentRn    = gridData.dataSource.getByUid(currentUid).rn; // determine current row number from datasource

                if($.inArray(currentRn, arrCCSelectedRows)>=0 || ($.inArray(currentRn, arrCCUnSelectedRows) < 0 && currentRn >= intStartRN && currentRn <= intEndRN)) // check if row should be selected
                {
                    arrSelectedRows.push(objTr); // add item to arrSelectedRows (array of rows to be selected)
                }
            });

            gridData.select(arrSelectedRows); // do actual select via grid select function
        }
    }

    // Bind onclick to all trs
    if(elements = $("#gridOutboundOrders").find("table.k-selectable tr.k-master-row"))
    {
        $.each(elements, function(index, objTr) // bind click events to all selectable rows
        {
            $(objTr).off('click'); // clear all clicks first
            $(objTr).on('click', function(e) // bind new onclick
            {
                gridData.clearSelection(); // always clear selection, as Kendo delivers random selections on shift-click, and doesn't clear selections properly, either
                arrSelectedRows        = []; // in case of a click event, clear arrSelectedRows
                var selectedUid     = $(e.currentTarget).attr("data-uid"); // uid of clicked row
                var selectedRn        = gridData.dataSource.getByUid(selectedUid).rn; // get rn from datasource by uid

                // check what to select, based by rownumber
                if(e.shiftKey) // shift-click
                {
                    // empty all selections
                    arrCCSelectedRows     = [];
                    arrCCUnSelectedRows    = [];

                    if(nextStartRN>0) // previous click was ctrl-click;
                    {
                        intStartRN = nextStartRN; //use previous click location as start-location
                        nextStartRN = 0;
                    }

                    // set endrn to current row number
                    intEndRN            = gridData.dataSource.getByUid(selectedUid).rn;

                    if(intStartRN <= intEndRN) // make sure we start the loop from small to large number
                    {
                        loopCCStart = intStartRN;
                        loopCCEnd = intEndRN;
                    }
                    else
                    {
                        loopCCStart = intEndRN;
                        loopCCEnd = intStartRN;
                    }

                    for(i=loopCCStart, j=loopCCEnd; i<=j; i++) // loop from start to end for next possible control-click
                    {
                        arrCCSelectedRows.push(i); // add rownumber to selected rows for next possible control-click
                    }
                }
                else if(e.ctrlKey) // control-click
                {
                    // in case of a previous active shift-click selection, check what items are selected
                    nextStartRN = selectedRn; // set nextStartRN on current rn (to be used by next possible shift-click)
                    if($.inArray(selectedRn, arrCCSelectedRows) < 0) // ctrl-clicked row not in arrCCSelectedRows yet (not selected yet)
                    {
                        arrCCSelectedRows.push(selectedRn); // thus add it to arrCCSelectedRows
                        if($.inArray(selectedRn, arrCCUnSelectedRows)>=0) arrCCUnSelectedRows.splice($.inArray(selectedRn, arrCCUnSelectedRows),1); // and remove it from arrCCUnSelectedRows
                    }
                    else // ctrl-clicked row already in arrCCSelectedRows (thus already selected)
                    {
                        arrCCUnSelectedRows.push(selectedRn); // hence deselect row (add it to unselected rows)
                        if($.inArray(selectedRn, arrCCSelectedRows)>=0) arrCCSelectedRows.splice($.inArray(selectedRn, arrCCSelectedRows),1); // and remove it from arrCCSelectedRows
                    }
                }
                else // normal click (or other click+key combi)
                {
                    arrCCSelectedRows     = []; // remove all ctrl-click selected rows
                    arrCCUnSelectedRows    = []; // remove all ctrl-click unselected rows
                    intStartRN    = selectedRn; // set start rownumber to currently clicked row
                    intEndRN    = selectedRn; // set end rownumber to currently clicked row
                }

                // walk through currently shown rows and create selection
                // make sure we start from small row number to large rownumber
                var myStartRN     = intStartRN;
                var myEndRN        = intEndRN;
                if(intStartRN > intEndRN)
                {
                    myStartRN = intEndRN;
                    myEndRN = intStartRN;
                }
                var currentRows = $("#gridOutboundOrders").find("table.k-selectable tr.k-master-row"); // all currently shown selectable rows
                $.each(currentRows, function(index, currentRow) // walk over all these rows
                {
                    var currentUid    = $(currentRow).attr("data-uid");                // uid of current row
                    var currentRn    = gridData.dataSource.getByUid(currentUid).rn;    // rn of current row

                    if($.inArray(currentRn, arrCCSelectedRows) >= 0 || (currentRn >= myStartRN && currentRn <= myEndRN && $.inArray(currentRn, arrCCUnSelectedRows) < 0)) // check if row should be selected
                    {
                        arrSelectedRows.push(currentRow);                            // add row to arrSelectedRows array
                    }
                });

                gridData.select(arrSelectedRows); // select rows via grid select function
            });
        });
    }
});
Tags
Grid
Asked by
Kenny
Top achievements
Rank 1
Answers by
Dimiter Madjarov
Telerik team
Patrick
Top achievements
Rank 1
Kenny
Top achievements
Rank 1
Share this question
or