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

Issue with PersistSelection, when programatically selecting rows

7 Answers 1642 Views
Grid
This is a migrated thread and some comments may be shown as answers.
David
Top achievements
Rank 1
David asked on 15 Sep 2020, 05:00 PM

Hello,

I am encountering a problem with the grid control and PersistSelection. After a user selects rows across the pages of the grid, there's a javascript function (ProcessGrid()) that builds an array of the selected rows and passes it as a parameter to an AJAX call on a controller method for processing. This process works correctly when a user manually selects the rows through clicking on the control. However, if I set the selected rows programatically by handling the DataBound event in Javascript, only the rows selected on the current page are picked up when I call ProcessGrid(). Note that after DataBound is handled, the correct rows are selected across every page. 

Am I making a mistake somewhere when setting the selected rows in the DataBound event handler?  

Sample grid:

@(Html.Kendo().Grid<SampleApp.Models.CompanyViewModel>()
    .Name("grdCompanies")
    .Columns(col=> {
        col.Select().Width(50);
        col.Bound(b=>b.CompanyName);
        col.Bound(b=>b.CompanyType);
    })
    .ToolBar(tb => {
        tb.Search();       
    })
 
    .Search(search=> { search.Field(p=>p.CompanyName); })
    .Pageable()
    .Sortable()
    .PersistSelection()
    .Events(e=>e.DataBound("onDataBound_grdCompanies"))
    .DataSource(ds => ds
        .Ajax()
        .Model(model=>model.Id(p=>p.CompanyID))
        .Read(read=>read.Action("Company_Read","Sample"))))

 

DataBound event handler:

// set select flag on companies in progress
function onDataBound_grdCompanies(e) {
    var m_grid = this;
    var m_dataSource = m_grid.dataSource;
 
    // loop through each record
    $.each(m_grid.items(), function (index, item) {
        var m_uid = $(item).data("uid");
        var m_dataItem = m_dataSource.getByUid(m_uid);
        if (m_dataItem.IsSelectedCompany == true) {
            m_grid.select($(item));
        }
    });
}

 

ProcessGrid()

 

function ProcessGrid() {
     
    var m_ViewModelID = '@Model.ViewModelID';
    var m_url = '@Url.Action("Company_Save", "Sample")';
    var m_grid = $("#grdCompanies").data("kendoGrid");
    var m_SelectedCompanies = [];
     
    m_grid.select().each(function () {
        m_SelectedCompanies.push(m_grid.dataItem(this));
    });
     
    $.ajax({
    type: 'POST',
    url: m_url,
    cache: false,
    data: JSON.stringify({
        pViewModelID: m_ViewModelID,
        pCompanies: m_SelectedCompanies,
    }),
    dataType: "json",
    contentType: 'application/json;',
    success: function (result) {
        if (result.indexOf("ERROR:") !== -1) {
            // some code
        }
    }
    });
}

7 Answers, 1 is accepted

Sort by
0
Georgi Denchev
Telerik team
answered on 17 Sep 2020, 05:05 PM

Hi, David

Thank you for the provided code snippets.

Whenever the server operations of the Telerik UI Grid are enabled, any programmatic selection would be applied only to the current page. This is due to the fact that the grid has the data only for a single page at a time.

With that said, what I can recommend as an alternative is to make use of the selectKeyNames() function of the grid. It would return the IDs of the selected rows across all pages of the grid. You could send the array to the server-side and obtain the data items from the database directly. 

https://docs.telerik.com/kendo-ui/api/javascript/ui/grid/methods/selectedkeynames

As per loading the previously selected rows, you can do so by dynamically modifying the "_selectedIds" internal collection of the grid. Here is an example of how to add the specific rows to the collection:

$("#grid").getKendoGrid()._selectedIds[10253] = true; // 10253 is ID of the specific data item

The suggestion from the code snippet above can be integrated into a loop. For instance, if you would like to loop through a predefined collection of data items, take their IDs and pass them to the selectedIds collection.

In order for the rows on the current page to take place, there are two approaches that you could undertake:

1. Call the refresh() method of the grid. This would cause the widget to redraw its content and trigger all of its events once again.

2. Set the AutoBind() property of the grid to false. This would cause it to not fetch its data the first moment it is initialized. You could modify the selectedIds collection first and manually fetch the data. A suitable event for executing this is the jQuery document.ready() event:

$( document ).ready(function() {
    $("#grid").getKendoGrid()._selectedIds[10253] = true; // 10253 is ID of the specific data item
    // perform a for-loop if needed
   
   $("#grid").getKendoGrid().dataSource.fetch(); // substitute grid with the name of the grid on your side
});

I hope you find those suggestions helpful. If additional information is needed, feel free to contact me back.

 

Best regards,
Georgi Denchev
Progress Telerik

Five days of Blazor, Angular, React, and Xamarin experts live-coding on twitch.tv/CodeItLive , special prizes and more, for FREE?! Register now for DevReach 2.0(20).

0
David
Top achievements
Rank 1
answered on 30 Sep 2020, 03:35 PM
[quote]Georgi Denchev said:

 

With that said, what I can recommend as an alternative is to make use of the selectKeyNames() function of the grid. It would return the IDs of the selected rows across all pages of the grid. You could send the array to the server-side and obtain the data items from the database directly.

[/quote]

I have spent some more time playing with this. Why does selectedKeyNames() return all selected ID's across pages, but select() only returns the selected rows from current page of the grid?

 

0
Georgi Denchev
Telerik team
answered on 30 Sep 2020, 10:52 PM

Hello David,

The selectedKeyNames() function stores the model id of a record once it has been selected. The reason select() returns only the rows on the current page is because the other pages don't "exist".

When using server operations the grid requests data only for the current page. It would take much more time and resources if we were to obtain every single record from the database and preload the grid. Therefore, because only one page exists, there is no way to apply the "selected" class onto the rows in the other pages.

Every time you switch to another page, the previous disappears, ._selectedIds stores the ids of the selected rows and reapplies the "selected" class once you visit the page where you have previously selected items. Select() returns the rows based on whether they have that class or not.

Let me know if you have more questions.

Best Regards,
Georgi Denchev
Progress Telerik

Five days of Blazor, Angular, React, and Xamarin experts live-coding on twitch.tv/CodeItLive, special prizes, and more, for FREE?! Register now for DevReach 2.0(20).

0
David
Top achievements
Rank 1
answered on 13 Oct 2020, 08:05 PM

I'm looking for a method to deselect a single row, across the entire data set. I'm guessing the solution is to find the index of the row in _selectedIds and setting it to false. Is this the correct way to deselect a single grid row programatically? 

 

 

To paint a picture of what I'm trying to do, I have a checkbox-selectable Grid on the left hand side of the page, and a ListView on the right side of the page, which serves as a tracker for the selected rows on the grid. As a user pages through the Grid and selects rows, it adds the row to the data source of the ListView. If they deselect a row, it removes it from the ListView. To tie this together, I'd like it so that if a user removes an item from the ListView, it deselects the row in the Grid.

 

 

0
David
Top achievements
Rank 1
answered on 13 Oct 2020, 08:36 PM
[quote]David said:

I'm looking for a method to deselect a single row, across the entire data set. I'm guessing the solution is to find the index of the row in _selectedIds and setting it to false. Is this the correct way to deselect a single grid row programatically? 

 

 

[/quote]

 

This approach mostly works, however I found an issue where when it comes to the last selected item in the grid, when I set _selectedIds[lastItem.id] = false, it does not call the onChange() handler that I have for the grid. 

onChange() {
 
whenever this function is called, it iterates over grid.selectedKeyNames() and adds them to a global array. It then sets that global array to be the datasource for the ListView control
 
 
}

 

0
David
Top achievements
Rank 1
answered on 13 Oct 2020, 08:47 PM
[quote]

This approach mostly works, however I found an issue where when it comes to the last selected item in the grid, when I set _selectedIds[lastItem.id] = false, it does not call the onChange() handler that I have for the grid. 

[/quote]

Let me clarify, I'm doing this:

 

grid._selectedIds[dataItem.id] = false;
grid.refresh();

 

 

And refresh() it triggers the Change event on the grid, unless there's only 1 item in _selectedIds, in which case it redraws the Grid but doesn't trigger the Change.  I have code tying together my Grid and my ListView controls in that Change event. Is this the expected behaviour for refresh()?

 

 

0
Georgi Denchev
Telerik team
answered on 15 Oct 2020, 04:34 PM

Hi David,

Your current approach is correct, the problem comes from the logic behind the change event.

The change event of the grid is triggered when a row is selected. When you refresh the grid the rows that are in ._selectedKeys are re-selected again and the event is called. However once you get to the final selection and you remove it, refreshing the grid won't trigger the event because there are no more selected rows left. 

You could work your way around by checking if the ._selectedKeys collection is empty and trigger the event manually.

delete grid._selectedIds[dataItem.id]; // remove the item from the collection.

if(jQuery.isEmptyObject(grid._selectedIds))  { // check if there are any items left
        grid.trigger("change"); // trigger the change event if there are not.
    }

grid.refresh(); // call the refresh

Best Regards,
Georgi Denchev
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Tags
Grid
Asked by
David
Top achievements
Rank 1
Answers by
Georgi Denchev
Telerik team
David
Top achievements
Rank 1
Share this question
or