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

For paged grid, changing the current page based on requested ID

10 Answers 1595 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Mark
Top achievements
Rank 1
Mark asked on 06 Aug 2015, 01:18 PM

Hi there,

This is a fairly common scenario which others have posted about but can't seem to find a solution.  I have a paged Kendo grid loaded from a remote datasource.  When the page loads, optionally there is an ID in the querystring representing the ID of a row.  I need the grid to go to the page of the grid for that ID.

The approach I'm taking is that in the dataBound event, query the datasource to get the index of the row with that ID, and then use that along with the page size to work out which page to goto.  However, I can't seem to find my ID in the datasource if it isn't on the currently visible page.  Here is my jQuery code to try and get the index :-

//// jump to page of querystring ID
        var ds = $('#grid').data('kendoGrid').dataSource;

        var view = kendo.data.Query.process(ds.data(), {
            filter: ds.filter(),
            sort: ds.sort()
        }).data;

        var index = -1;
        // find the index of the matching dataItem
        for (var x = 0; x < view.length; x++) {
            if (view[x].QuoteID == "11847") {  // hard-coded here for testing but will become a variable from querystring
                index = x;
                break;
            }
        }

Is my approach correct, and if so how can I make this work?  I need to be able to access the other items in the datasource not on the current visible page.  A code example would be appreciated.

Thanks, Mark

10 Answers, 1 is accepted

Sort by
0
Daniel
Telerik team
answered on 10 Aug 2015, 09:13 AM
Hello Mark,

Could you provide the grid dataSource configuration? The code that you provided should work unless you are using server-side paging. If you are using server paging then you should find the page on which the specific record is on the server.

Regards,
Daniel
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
0
Mark
Top achievements
Rank 1
answered on 10 Aug 2015, 12:09 PM

Hi Daniel,

Here is my view code...

@( Html.Kendo().Grid<Models.QuoteModel>()
    .Name("grid")
    .DataSource(datasource => datasource
        .Ajax()
        .Read(read => read.Action("GetQuotes", "Quote"))
        .Sort(sort => sort.Add("Created").Descending())
    )
    .Columns(columns =>
    {
        columns.Bound(Quote => Quote.QuoteNoFormatted).Title("Quote Number");
        columns.Bound(Quote => Quote.QuoteTypeDescription).Title("Quote Type");
        columns.Bound(Quote => Quote.Value).Title("Value");
        columns.Bound(Quote => Quote.Created).Title("Created");
        columns.Bound(Quote => Quote.Status).Title("Notes");
        columns.Template(Quote => { }).ClientTemplate(" ").Title("Quote");
    })
    .ClientRowTemplate(Html.Partial("~/Views/Quote/QuoteTemplate.cshtml").ToHtmlString())
    .Sortable()
    .ToolBar(tools =>
    {
        tools.Template(@<text>
            <div class="toolbar">
                <a class="k-button k-button-icontext k-grid-excel" href="#"><span class="k-icon k-i-excel"></span>Export to Excel</a>
            </div>
        </text>);
    })
    .Pageable(pageable => pageable
                .Refresh(true)
                .PageSizes(true)
                .ButtonCount(5))
    .Events(e => e
                .ExcelExport("excelExport")
                .DataBound("quotesDatabound"))
    .Excel(excel => excel
                .FileName("Quotes.xlsx")
                .AllPages(true))
)

Thanks, Mark

0
Daniel
Telerik team
answered on 12 Aug 2015, 08:34 AM
Hello again Mark,

The dataSource is configured for server operations(the default for the MVC Grid wrapper) and so all records will not be available on the client. You should either enable client-side operations or get the page number from the server. I attached a sample project that demonstrates one possible implementation to get the page when using server operations.

Regards,
Daniel
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
0
Mark
Top achievements
Rank 1
answered on 13 Aug 2015, 02:01 PM

Hi Daniel,

Thanks for the reply - I have got that working now (also generically across a number of grids!).  One minor thing, and I'm not sure if there's a way around this, but is it possible to load the matched tab straight away - this way loads the grid on page 1 and then does a separate load of the correct page.  I've saved the collection into session to save waiting for another DB hit which speeds it up, but visually it is noticeable that it's a 2-stage loading.

Any ideas would be appreciated.

Thanks, Mark

0
Accepted
Daniel
Telerik team
answered on 17 Aug 2015, 08:35 AM
Hi Mark,

You can use the autoBind option as in the example from my previous reply. It will prevent the grid request on initialization for the first page and the grid data will be loaded only once when the request is triggered with the query method.

Regards,
Daniel
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
0
Mark
Top achievements
Rank 1
answered on 17 Aug 2015, 09:03 AM

Thanks Daniel,

Mark

0
Mark
Top achievements
Rank 1
answered on 02 Sep 2015, 02:33 PM

Hi Daniel,

We've put this code live and it's working fine.  However, because the datasets being played with are quite large, if an ID is passed in via the URL then I'd like the first load of the grid to go to that page, rather than re-load the grid with the required page after the first load as currently.  I've tried getting this working by adding Data parameters to the read.Action value of the datasource on the grid.  This works fine to change the requested page number data returned, however the page still thinks it's page 1 loaded (as page 1 is highlighted underneath the grid data in the grid controls).  I've put my view and controller code below, plus attached the output in an image attached.  Please can you take a look and advise how I can achieve this functionality?

===VIEW=== 

@( Html.Kendo().Grid<TEAMSPortalV2.Models.SurveyViewModel>()
    .Name("grid")
    .DataSource(datasource => datasource
        .Ajax()
        .PageSize(15)
        .Read(read => read.Action("GetSurveys", "Survey").Data("{ PageOverride: 2}"))
    ) ........

==== CONTROLLER ====

        // Get Surveys
        public ActionResult GetSurveys([DataSourceRequest]DataSourceRequest request, int? PageOverride)
        {      
            // get data
            if (PageOverride.HasValue && PageOverride.Value > 0)
            {
                request.Page = PageOverride.Value;
            } ......​

Thanks, Mark

0
Daniel
Telerik team
answered on 04 Sep 2015, 09:42 AM
Hello Mark,

I am not sure if I understand this statement:
rather than re-load the grid with the required page after the first load as currently
Using the autoBind option should prevent the first load?

As for setting the page in the pager - this is not currently supported. If the dataSource is configured for server operations than any method that updates the pager will also trigger a new request to the server. it is possible to achieve this by changing the internal _skip field  in the requestEvent:
function onRequestEnd(e) {
    this._skip = (PageOverride - 1) * this.pageSize();
}
but note that using internal methods or fields is not recommended because  they could be changed in a future version.

Regards,
Daniel
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
0
Mark
Top achievements
Rank 1
answered on 08 Sep 2015, 09:55 AM

Hi Daniel,

I tried adding the autoBind(false) option, but nothing rendered at all then.

I'll try and explain the situation - in my URL I have an ID which represents an item on the grid, but not necessarily on page 1.  The system needs to jump to the correct page for that ID.  Currently I handle this in the databound event - so the page loads the grid via the controller on page 1 by default initially, and then the databound event tests to see if the requested ID is found on the page, if not then it calculates the index of the page that it is on and then makes a second call to the controller to load the data for the correct page of the grid.  Because my loads can be quite slow because of how the data is retrieved, it would be better to only hit the controller once, somehow passing the ID so that the returned object to render the grid renders it on the page of the requested ID first time around.

It's not essential behaviour, but desirable to improve the user experience.  Any help would be appreciated.

Here is my code currently :-

==== VIEW ====

 @( Html.Kendo().Grid<Models.SurveyViewModel>()
    .Name("grid")
    .DataSource(datasource => datasource
        .Ajax()
        .PageSize(15)
        .Read(read => read.Action("GetSurveys", "Survey")) //.Data("{ PageOverride: 2}"))
    )
    .Columns(columns =>
    {
        columns.Template(Survey => { }).ClientTemplate(" ").Title("Documents");
        columns.Bound(Survey => Survey.JobNoFormatted).Title("Job Number");
        columns.Bound(Survey => Survey.Address).Title("Site");
        columns.Bound(Survey => Survey.surveyType).Title("Report Type");
        columns.Bound(Survey => Survey.ApprovedFormatted).Title("Approved");
    })
    .ClientRowTemplate(Html.Partial("~/Views/Survey/SurveyTemplate.cshtml").ToHtmlString())
    .Sortable()
    .ToolBar(tools =>
    {
        tools.Template(@<text>
            <div class="toolbar">
                <a class="k-button k-button-icontext k-grid-excel" href="#"><span class="k-icon k-i-excel"></span>Export to Excel</a>
                <a href="#" onclick="toggleFilter();"><img src="~/Content/images/icons/filter.png" class="floatright" style="padding: 3px;" title="Show/Hide Filters" /></a>
                <div class="toolbar_filter" style="display:none;">
                    @*initially hidden*@
                    @Html.Partial("~/Views/Survey/SurveyFilterPanel.cshtml")
                </div>
            </div>
        </text>);
    })
    .Pageable(pageable => pageable
                .Refresh(true)
                .PageSizes(new[] { 15, 30, 60 })
                .ButtonCount(5))
    .Events(e => e
                .ExcelExport("excelExport")
                .DataBound("surveysDatabound"))
    .Excel(excel => excel
                .FileName("Surveys.xlsx")
                .AllPages(true))
)

=== DATABOUND JS ====

 // change the page to the one with the matched row highlighted (written generically for all Kendo grids, assuming that :-
                // a) Grid's name is 'grid'
                // b) The controller is named the same as the current URL section (e.g. Quote) - case insenstive btw
                // c) The controller has an action called GetRecordPage returning a JSON-ed integer of the required page
                var id = getQueryVariable('id');
                if (id > 0) {
                    $(function () {
                        var grid = $("#grid").data("kendoGrid");
                        var dataSource = grid.dataSource;
                        var requestData = dataSource.transport.parameterMap({
                            sort: dataSource.sort(),
                            filter: dataSource.filter(),
                            pageSize: dataSource.pageSize(),
                            id: id
                        });
                        $.ajax({
                            url: '@Url.Action("GetRecordPage", selectedTab)',
                            type: "POST",
                            dataType: "json",
                            data: requestData,
                            success: function (page) {
                                var grid2 = $("#grid").data("kendoGrid");
                                // we have to test the returned page against the current page, otherwise it would keep reloading forever in a loop
                                if (grid2.dataSource.page() != page) {
                                    dataSource.query({
                                        page: page,
                                        pageSize: dataSource.pageSize(),
                                        sort: dataSource.sort(),
                                        filter: dataSource.filter(),
                                    });
                                }
                            }
                        });
                    });
                }

=== CONTROLLER ====

// Get Surveys
        public ActionResult GetSurveys([DataSourceRequest]DataSourceRequest request)
        {      
          .... INTERNAL LOGIC.....
            // return JSON to UI
            return Json(ret.ToDataSourceResult(request));
        }​

// get record page of a survey if an ID has been passed in
        public ActionResult GetRecordPage([DataSourceRequest] DataSourceRequest request, int id)
        {
            // get the current page size beign used
            var pageSize = request.PageSize;
            request.PageSize = 0;

            // get the collection out of session from initial data call (GetSurveys)
            SurveyCollection col = (SurveyCollection)Session["SurveyDataCollection"];
           
            // work out which page it's on
            int page = 1;  // default in case no ID match
            Survey match = col.FirstOrDefault(s => s.Job.JobID == id);
            if (match != null)
            {
                page = (col.IndexOf(match) / pageSize) + 1;
            }

            // clear out the session variable
            Session.Remove("SurveyDataCollection");

            // returned JSONed page number
            return Json(page);
        }​

0
Nikolay Rusev
Telerik team
answered on 10 Sep 2015, 02:37 PM

Hello Mark,

 

Here is a prototype of how you should implement the scenario: - based in the ID value (parsed from query string) find the record in all items, calculate the page index on which the item should appear and position the Grid to that page.

 

In order to implement this scenario you have two options:

 1. Page the grid on client - the one which you are currently implementing. In order to implement this you have to load all data items in the data source, by setting .ServerOperation(false). Thus all data items will be served on client and you will be able to calculate the page index and page the Grid.

 

The cons of this is impementation:

 - all data items will be served on client

 - the widget will be initially bound to first page

 

 2. Page the grid on server - parse the query string and calculate the page index on server. Here is how you could implement this:

 - inside the controller - parse the query string and find the page index in all items. Then set the calculated value to a property in the ViewBag. Example: 

 

        public ActionResult Remote_Data_Binding()
        {
            ViewBag.Page = 5; //5 is result of the calculations

            return View();
        }

 

 - configure the Grid for server operations - thus only page size items will be loaded on client

 - set  .AutoBind(false) to the Grid. Thus initial binding will be prevented

 - add the following script which will bind the widget to the page calculated on server:

 

<script>
    $(function() {        
        var dataSource = $("#grid").data("kendoGrid").dataSource;
        var request = {
            page: @ViewBag.Page,
            pageSize: dataSource.pageSize(),
            sort: dataSource.sort(),
            filter: dataSource.filter(),
            group: dataSource.group()
        };

        dataSource.query(request);
    });
</script>

 

 

Regards,

Nikolay Rusev

Telerik

 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
Tags
Grid
Asked by
Mark
Top achievements
Rank 1
Answers by
Daniel
Telerik team
Mark
Top achievements
Rank 1
Nikolay Rusev
Telerik team
Share this question
or