Search code examples
paginationkendo-asp.net-mvcpagerkendo-datasourcekendo-treelist

Use Kendo Pager Control with Kendo TreeList to Paginate by Root Nodes


I'm tiring to use the kendo pager with the Kendo TreeList to paginate the Trees (Root Nodes). Remote data binding is enabled to load child nodes (on-demand data loaded at row expand). At the initial load, the TreeList is in Collapsed view.

A Kendo Pager and a kendo DataSource is been used. The server handles the dynamic data loading and paging data.

Sample Tree Hierarchy:-

T1 = A -> B

T2 = C -> D -> E

T3 = X -> Y

Let's say the Page Size is 2. T1 and T2 will show on the first page and the T3 will be on the 2nd page.

Page 1

Page 1

Page 2 Collapsed view

enter image description here

The problem I'm facing is that when the user selects the page from pager control that data is not sent to the backend.

In the below scenario, when the user selects page 2 pager controller sends page 1 instead of 2. It seems the pager act as the TreeList client pager !?

enter image description here

TreeListView.cshtml

@(Html.Kendo().DataSource<TreeListViewModel>()
    .Name("TreeListDataSource")
    .TreeList(source =>
    {
        source.Model(m =>
        {
            m.Expanded(false);
            m.Id(f => f.ObjectID);
            m.ParentId(f => f.ParentID).Nullable(true);
            m.Field(f => f.Code);                                
        });
        source.Read(read => read.Action("ReadTreeListdData", "TreeList").Data("BuildTreeListReadRequestData")).PageSize(2);

    })

)

@(Html.Kendo().TreeList<TreeListViewModel>()
        .Name("TreeList")
        .Columns(columns =>
        {
            columns.Add().Field(f => f.Code);

        })                            
        .DataSource("TreeListDataSource")                           
)    

@(Html.Kendo().Pager()
    .Name("TreeListPager")
    .DataSource("TreeListDataSource")                           
)

TreeListController.cs

public JsonResult ReadTreeListData([DataSourceRequest] DataSourceRequest request, int? id)
{
    int TotalTreeCount = 0;

    int pageSize = request.PageSize;
    int page = request.Page;

    var Result = GetTreeListDirectory(id, pageSize, page, out TotalTreeCount).ToTreeDataSourceResult(request,
        e => e.ObjectID,
        e => e.ParentID,
        e => id.HasValue ? e.ParentID == id : e.ParentID == null,                    
        e => e
    );

    Result.Total = TotalTreeCount; //3

    return Json(Result, JsonRequestBehavior.AllowGet);
}

References:


Solution

  • Currently, the Kendo Tree view is not supported for server pagination. but you can do it manually as below.

    TreeListView.cshtml

    @(Html.Kendo().TreeList<TreeListViewModel>()
        .Name("TreeList")
        .Columns(columns =>
        {
            columns.Add().Field(f => f.Code);
        })
        .DataSource(source =>
        {
            source.Model(m =>
            {
                m.Expanded(false);
                m.Id(f => f.ObjectID);
                m.ParentId(f => f.ParentID).Nullable(true);
                m.Field(f => f.Code);
            });
            source.Read(read => read.Action("ReadTreeListdData", "TreeList").Data("BuildTreeListReadRequestData"));
            source.ServerOperation(true).Events(e => e.RequestEnd("TreeListDataSource_Sync"));
        })
        .Events(events => { events.DataBound("onDataBound");})
     )
    
    <div class="container-fluid" style="border-style: solid; padding-top: 5px; padding-bottom: 5px; border-color: #E2E2E3; border-width: 0px 1px 1px 1px;">
        <div class="row">
            <div class="col-2 col-lg-3 col-md-2" style="display: inline-block;">
                @(Html.Kendo().DropDownList()
                    .Name("PageSize")
                    .DataTextField("Text")
                    .DataValueField("Value")
                    .Events(e => e.Change("OnpageSizeChange"))
                    .BindTo(new List<SelectListItem>() {
                        new SelectListItem() { Text = "10", Value = "10" },
                        new SelectListItem() { Text = "20", Value = "20" }
                    })
                    .Value("10")
                    .HtmlAttributes(new { style = "width: 5em" })
                )
                <span id="itemPerPageMsg" class="k-label">items per page</span>  |
                <span id="totRecordCount" class="k-label"></span>
            </div>
            <div class="col-6 col-lg-6 col-md-6" style="display: inline-block; text-align:right;">
                <div id="paggerContent">
                </div>
            </div>
            <div class="col-2 col-lg-2 col-md-2" style="display: inline-block; text-align: right;">
                <span class="k-label">Go to page</span>
                @(Html.Kendo().NumericTextBox<int>()
                    .Name("GoToPageNumber")
                    .Format("numeric")
                    .Min(0)
                    .Placeholder("0")
                    .Spinners(false)
                    .Events(e => e.Change("onChangeGoToPageTextBox"))
                    .HtmlAttributes(new { @class = "k-textbox", style = "width: 3em", title = "Go to page", autocomplete = "off" })
                )
                <span class="k-label"> of <span id="totalPageCount"></span></span>
            </div>
        </div>
    </div>
    
    
    <script>
        function onDataBound(arg) {
            var treeList = $('#TreeList').data('kendoTreeList').dataSource.total();
            var pageSizeDropDown = $("#PageSize").val();
            var currentPageNumber = localStorage["PageIndex"] != 0 ? localStorage["PageIndex"] : 1;
            buildPageNumbers(currentPageNumber, treeList, pageSizeDropDown);
            localStorage["PageIndex"] = 1;
            localStorage["PageSize"] = 10;
        }
    
        function BuildTreeListReadRequestData() {
            var data = {
                pageIndex: localStorage["PageIndex"],
                pageSize: localStorage["PageSize"]
            };
            return data;
        }
    
        function onClickPagerNumber(newPageNumber) {
            localStorage["PageIndex"] = newPageNumber;
            $("#TreeList").data("kendoTreeList").dataSource.read();
        }
    
        function OnpageSizeChange() {
            var selectedPageSize = $("#PageSize").val();
            localStorage["PageSize"] = selectedPageSize;
            $("#TreeList").data("kendoTreeList").dataSource.read();
        }
    
        function onChangeGoToPageTextBox() {
            onClickPagerNumber(this.value());
        }
    
        function buildPageNumbers(selectedPage, totalItems, pageSize) {
            var ulStart = '<ul class="k-pager-numbers">';
            var ulend = '</ul > ';
            var pagerList = '';
            var pagerCount = Math.ceil(totalItems / pageSize);
            var maxPage = pagerCount;
            var allPageSets = 1;
            var startPage = 1;
            var selectedPagePageSet = 1;
            var isThereMorePages = false;
            var buttonCount = 7;
    
            if (pagerCount > buttonCount) {
                selectedPagePageSet = Math.ceil(selectedPage / buttonCount);
                maxPage = buttonCount * selectedPagePageSet;
                if (maxPage > pagerCount) {
                    maxPage = pagerCount;
                }
                startPage = (buttonCount * (parseInt(selectedPagePageSet) - 1)) + 1;
    
                allPageSets = Math.ceil(pagerCount / buttonCount);
                if (allPageSets > selectedPagePageSet) {
                    isThereMorePages = true;
                }
            }
    
            if (totalItems == 0) {
                pagerList = pagerList + '<li><span aria-label="Page 0" class="k-link k-state-selected">0</span></li>';
            } else {
                for (let i = startPage; i <= maxPage; i++) {
                    var newPager = '';
                    if (i == selectedPage) {
                        newPager = '<li><span aria-label="Page ' + i + '" class="k-link k-state-selected">' + i + '</span></li>'
                    } else {
                        newPager = '<li><a onclick="onClickPagerNumber(' + i + ');" href="#" class="k-link" title="Page ' + i + '" style="border-style: none;">' + i + '</a></li>'
                    }
                    pagerList = pagerList + newPager;
                }
            }
    
            var startGoToPageClass = 'class="k-link"';
            var endGoToPageClass = 'class="k-link"';
    
            if (selectedPage == 1) {
                startGoToPageClass = 'class="k-link k-state-disabled"';
            }
    
            if (selectedPage == pagerCount) {
                endGoToPageClass = 'class="k-link k-state-disabled"';
            }
    
            const nextPage = parseInt(selectedPage) + 1;
            const previousPage = parseInt(selectedPage) - 1;
            var goToTheFirstPage = '<li style="vertical-align: middle;"><a onclick="onClickPagerNumber(1);" href="#" ' + startGoToPageClass + ' title="Go to the first page" style="border-style: none;"><span class="k-icon k-i-arrow-end-left"></span></a></li>'
            var goToThePreviousPage = '<li style="vertical-align: middle;"><a onclick="onClickPagerNumber(' + previousPage + ');" href="#" ' + startGoToPageClass + ' title="Go to the previous page" style="border-style: none;"><span class="k-icon k-i-arrow-60-left"></span></a></li>'
            var goToTheNextPage = '<li style="vertical-align: middle;"><a onclick="onClickPagerNumber(' + nextPage + ');" href="#" ' + endGoToPageClass + ' title="Go to the next page" style="border-style: none;"><span class="k-icon k-i-arrow-60-right"></span></a></li>'
            var goToTheLastPage = '<li style="vertical-align: middle;"><a onclick="onClickPagerNumber(' + pagerCount + ');" href="#" ' + endGoToPageClass + ' title="Go to the last page" style="border-style: none;"><span class="k-icon k-i-arrow-end-right"></span></a></li>'
    
            var finalHtml = ulStart + goToTheFirstPage + goToThePreviousPage;
    
            if (selectedPagePageSet > 1) {
                var previousMorePage = parseInt(startPage) - 1;
                finalHtml = finalHtml + '<li><a onclick="onClickPagerNumber(' + previousMorePage + ');" href="#" class="k-link" title="More pages" style="border-style: none;">...</a></li>';
            }
            finalHtml = finalHtml + pagerList;
    
            if (isThereMorePages) {
                var nextMorePage = parseInt(maxPage) + 1;
                finalHtml = finalHtml + '<li><a onclick="onClickPagerNumber(' + nextMorePage + ');" href="#" class="k-link" title="More pages" style="border-style: none;">...</a></li>';
            }
    
            finalHtml = finalHtml + goToTheNextPage + goToTheLastPage + ulend;
    
            document.getElementById('paggerContent').innerHTML = finalHtml;
            document.getElementById('totalPageCount').innerHTML = pagerCount;
    
            var startRecord = (((parseInt(pageSize) * selectedPage)) - (pageSize)) + 1;
            var endRecord = (parseInt(pageSize) * selectedPage);
            if (endRecord > totalItems) {
                endRecord = totalItems;
            }
            var showingRecordText = '';
            if (totalItems == 0) {
                showingRecordText = 'No items to display';
                selectedPage = 0;
            } else {
                showingRecordText = startRecord + ' - ' + endRecord + ' of ' + totalItems + ' items';
            }
            document.getElementById('totRecordCount').innerHTML = showingRecordText;
    
            var kendoNumericTextBox = $("#GoToPageNumber").data("kendoNumericTextBox");
            kendoNumericTextBox.value(selectedPage);
        }
    </script>
    

    Now you can handle pagination by your database. for example, if you are getting data from MSSQL SP you can do as below.

    CREATE OR ALTER PROCEDURE [GetTreeListData]
    (
        @PageSize INT,
        @PageIndex INT
    )
    AS
    BEGIN
        SELECT table_name
        WHERE ROWNUM IN BETWEEN (((@PageIndex - 1) * @PageSize) + 1) AND ((@PageIndex) * @PageSize))
    END