Kendo MVC Treelist load on demand not showing expand icon while loading parent nodes

7 posts, 0 answers
  1. binta
    binta avatar
    3 posts
    Member since:
    Mar 2017

    Posted 31 Mar 2017 Link to this post

    Hi 
    Please help me out. I am breaking my head to achieve the load on demand treelist in kendo. I have followed the instructions as per the url http://demos.telerik.com/aspnet-mvc/treelist/remote-data-binding. But nothing worked out. My scenario is like that I need to load the parent nodes when loading the page first. Then on clicking the parent node, need to fire a call to get the child nodes. For me, it load the parent nodes first, but I can't see the expand icon coming up for nodes having children. Just see the attached image. I don't know what I'm doing wrong. Attaching the view page code and also the controller methods. Please let me know what is the issue? Well appreciated, if you could share some code for sample other than the demo sample.
    View code
    ----------------------
     <div>

                    @(Html.Kendo().TreeList<TelerikMvcApp1.Models.AssetModel>()
                    .Name("assetTreeList")
                    .Columns(columns =>
                    {
                        columns.Add().Field(a => a.AssetName).Width(70).Expandable(true);
                    })
                    .Sortable()
                    .DataSource(dataSource => dataSource
                        .Read(read => read.Action("LoadTree", "Home"))
                        .ServerOperation(false)
                        .Model(m =>
                        {
                            m.Id(e => e.AssetId);
                            m.ParentId(e => e.ParentAssetId);
                            m.Field(e => e.AssetName);
                            m.Field(e => e.ParentAssetId);
                        })
                        )
                    .Scrollable(true)
                    .Height(540)
                   //.Events(e => e
                   ////.DataBound("onAssetTreeListDataBound")
                   //// ////.Change("treelist_change")
                   //)
                    )

                </div>
    Controller Methods
    -------------------------------------------------
     public IEnumerable<AssetModel> GetAssetTreeListBasedOnPermissions()
            {
                try
                {
                    List<AssetModel> list = new List<AssetModel>();
                    list.Add(new AssetModel { AssetId = 1, ParentAssetId = null, AssetName = "asset1" });
                    list.Add(new AssetModel { AssetId = 2, ParentAssetId = 1, AssetName = "asset2" });
                    list.Add(new AssetModel { AssetId = 3, ParentAssetId = null, AssetName = "asset3" });
                    list.Add(new AssetModel { AssetId = 4, ParentAssetId = 2, AssetName = "asset4" });

                    IEnumerable<AssetModel> enumlist = list
                        .Select(x => new AssetModel
                        {
                            AssetId = x.AssetId,
                            ParentAssetId = x.ParentAssetId,
                            AssetName = x.AssetName
                        });
                    return enumlist;

                  
                }
                catch (Exception ex)
                {
                    return null;
                }
            }

            public JsonResult LoadTree([DataSourceRequest] DataSourceRequest request, int? id)
            {
                var result = GetAssetTreeListBasedOnPermissions().ToTreeDataSourceResult(request,
                    e => e.AssetId,
                    e => e.ParentAssetId,
                    e => id.HasValue ? e.ParentAssetId == id : e.ParentAssetId == null,
                    e=>e
                );

                return Json(result, JsonRequestBehavior.AllowGet);
            }
    Model class
    ------------------------
     public class AssetModel
        {
            public long AssetId { get; set; }
            public long? ParentAssetId { get; set; }
            public string AssetName { get; set; }
            public bool HasChildren { get; set; }
        }
    Thanks in advance
    Binta Prasad
  2. Tsvetina
    Admin
    Tsvetina avatar
    2481 posts

    Posted 04 Apr 2017 Link to this post

    Hi Binta,

    The current version of the Remote binding demo of the TreeList does not use load on demand. You can see this if you inspect the network traffic. All TreeList items are loaded with a single request. This is a recent change in the demo, related to another fix in the demo behavior. We will further investigate this and I will post again when I have more information about this scenario and a recommendation for fixing your own TreeList.

    Regards,
    Tsvetina
    Telerik by Progress
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
  3. Tsvetina
    Admin
    Tsvetina avatar
    2481 posts

    Posted 06 Apr 2017 Link to this post

    Hi Binta,

    I created an app with your code and saw that the hasChildren field is not correctly populated. It needs to return true for each item that has children. You need to check on the server whether a certain item has children items and return true or false, so the TreeList can determine whether an expand button is needed for the current item.

    If the data on the server cannot be easily processed to check for children, you can consider returning always true, which will display an expand button for all items on load and hide it if an item is expanded and no children are found.

    Anyway, here is sample implementation of the LoadTree method following your code, which results in a correctly bound TreeList:
    public JsonResult LoadTree([DataSourceRequest] DataSourceRequest request, int? id)
    {
        var assets = GetAssetTreeListBasedOnPermissions();
     
     
        var result = assets.Where(e => e.ParentAssetId == id)
        .Select(e => new AssetModel{
            AssetId = e.AssetId,
            ParentAssetId = e.ParentAssetId,
            AssetName = e.AssetName,
            hasChildren = assets.Where(s => s.ParentAssetId == e.AssetId).Count() > 0
        }).ToTreeDataSourceResult(request);
             
         
        return Json(result, JsonRequestBehavior.AllowGet);
    }

    Note that I made hasChildren lower-case to fit the TreeList client-side name of the field used internally.

    You can find a sample project containing the full implementation attached to this message.

    Regards,
    Tsvetina
    Telerik by Progress
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
  4. James
    James  avatar
    4 posts
    Member since:
    Apr 2017

    Posted 24 Oct 2017 Link to this post

    Hi Tsvetina,

    Thank you so much for the sample app which you provided. I made the mentioned changes in our  application and the load on demand treelist worked like a charm!!!. But now, something else has started breaking off my head. I have applied server side filtering for my treelist. When I try to apply filter to my columns, the result shows me duplicate records. Posting my code below:

    Controller method

    ------------------------------------------------

    public async Task<JsonResult> GetAssetTreeListBasedOnPermissions([DataSourceRequest] DataSourceRequest request, AssetColumnFilters assetcolumnFilters,int? id)
            {
                try
                {
                    string searchExpression = string.Empty;
                    AssetColumnFilters columnFilters = new AssetColumnFilters();

                    if (!string.IsNullOrEmpty(assetcolumnFilters.AssetId))
                    {
                        var assetIds = assetcolumnFilters.AssetId.ToString().Split(',');
                        var i = 0;
                        var assetString = "";
                        foreach (var assetId in assetIds)
                        {
                            if (i == 0)
                            {
                                assetString += " (A.AssetId=" + assetId + ") ";
                            }
                            else if (i > 0)
                            {
                                assetString += " OR (A.AssetId=" + assetId + ") ";
                            }
                            i++;
                        }

                        searchExpression = assetString;

                    }

                 
                   string searchExp = searchExpression;
                 
                    AssetFiltersModel filters = new AssetFiltersModel();

                    long? companyId = SiteSession.CurrentSession.CompanyId;
                    long userId = SiteSession.CurrentSession.UserId;

                    //FIRST TIME LOAD
                    if (id==null && request.Filters.Count==0)
                    searchExp = string.IsNullOrEmpty(searchExp) ? " (SA.ParentAssetId=" + 0 + " AND A.CompanyId=" + SiteSession.CurrentSession.CompanyId + ")" : "(SA.ParentAssetId=" + 0 + " AND A.CompanyId=" + SiteSession.CurrentSession.CompanyId + ") and " + searchExp;
                    
    else if(id==null&& request.Filters.Count>0)
                        searchExp = string.IsNullOrEmpty(searchExp) ? " (A.CompanyId=" + SiteSession.CurrentSession.CompanyId + ")" : "(A.CompanyId=" + SiteSession.CurrentSession.CompanyId + ") and " + searchExp;
                    else
                    searchExp = string.IsNullOrEmpty(searchExp) ? " (SA.ParentAssetId=" + id + " AND A.CompanyId=" + SiteSession.CurrentSession.CompanyId + ")" : "(SA.ParentAssetId=" + id + " AND A.CompanyId=" + SiteSession.CurrentSession.CompanyId + ") and " + searchExp;
                    
                
                   

                    filters.SearchExpression = searchExp;
                    filters.SortExpression = (request.Sorts.Count > 0) ? request.Sorts.FirstOrDefault().Member : string.Empty;
                    filters.SortDirection = (request.Sorts.Count > 0) ? (request.Sorts.FirstOrDefault().SortDirection == ListSortDirection.Ascending ? " asc" : " Desc") : string.Empty;
                    filters.StartIndex = Helper.GetCurrentPage(request.Page, request.PageSize);
                    filters.PageSize = request.PageSize;
                    filters.UserId = userId;
                    var assetGridList = await aladdinRestClient.PostAsync<AssetFiltersModel, AssetListModel>(Constant.GETASSETLISTBASEDONUSER, filters, false);
                 
                    var list= assetGridList.AssetList.AsQueryable();

                    var result = list
                   .Select(e => new AssetModel
                      {
                          AssetId = e.AssetId,
                          ParentAssetId = e.ParentAssetId,
                       AssetName = e.AssetName,
                       AssetIdEncrypt = e.AssetIdEncrypt,
                       hasChildren = e.IsParent == 1 ? true : false,
                       AssetNo = e.AssetNo,
                       Model = e.Model,
                       SerialNumber = e.SerialNumber,
                       Category1Name = e.Category1Name,
                       Category2Name = e.Category2Name,
                       Category3Name = e.Category3Name,
                       Category4Name = e.Category4Name,
                       LocationLevel1Name = e.LocationLevel1Name,
                       LocationLevel2Name = e.LocationLevel2Name,
                       LocationLevel3Name = e.LocationLevel3Name,
                       LocationLevel4Name = e.LocationLevel4Name,                  
                       StatusName = e.StatusName,
                       IsAttachmentsExist = e.IsAttachmentsExist
                   }).ToTreeDataSourceResult(request,
                       e => e.AssetId,
                       e => e.ParentAssetId
                      
                        
                     );
                

                    var jsonResult = Json(result, JsonRequestBehavior.AllowGet);
                    jsonResult.MaxJsonLength = Int32.MaxValue;
                    return jsonResult;
                   


                

                }
                catch (Exception ex)
                {
                    throw ex;
                }


            }

    View 

    ---------------------------------------------------------------------------

     @(Html.Kendo().TreeList<AssetModel>()
                   .Name("assetTreeList")
                   .Columns(columns =>
                   {
                       columns.Add().Field(a => a.AssetId).Hidden(true);
                       columns.Add().Field(a => a.AssetIdEncrypt).Hidden(true);
                       //.Template("#if(IsAttachmentsExist == true) {#" + "<a class='k-button' href='javascript:void(0);' onclick='EditAsset(#:AssetId#,true);'><span class='k-i-attachment-45 k-i-clip-45'></span>#= AssetName# </a>" + "#}else {#" + "<a class='inline-link' href='javascript:void(0);' onclick='EditAsset(#:AssetId#,true);' style='margin-right:5px;'>#= AssetName# </a>" + "#} #").Width(290);
                       columns.Add().Field(a => a.AssetName).TemplateId("attachment-template").Width(290)/*.HeaderTemplateId("expandAll-template")*/;
                       columns.Add().Field(a => a.AssetNo).Title(AssetDetailResource.AssetNumber).Width(250).Hidden((bool)ViewData["AssetNumber"]);
                       columns.Add().Field(a => a.Model).Title(AssetDetailResource.Model).Width(120).Hidden((bool)ViewData["Model"]);
                       columns.Add().Field(a => a.SerialNumber).Title(AssetDetailResource.SerialNumber).Width(150).Hidden((bool)ViewData["SerialNumber"]);
                       columns.Add().Field(a => a.LocationLevel1Name).Title(AssetDetailResource.LocationLevel1Name).Width(180).Hidden((bool)ViewData["LocationLevel1"]);
                       columns.Add().Field(a => a.LocationLevel2Name).Title(AssetDetailResource.LocationLevel2Name).Width(180).Hidden((bool)ViewData["LocationLevel2"]);
                       columns.Add().Field(a => a.LocationLevel3Name).Title(AssetDetailResource.LocationLevel3Name).Width(180).Hidden((bool)ViewData["LocationLevel3"]);
                       columns.Add().Field(a => a.LocationLevel4Name).Title(AssetDetailResource.LocationLevel4Name).Width(180).Hidden((bool)ViewData["LocationLevel4"]);
                       columns.Add().Field(a => a.Category1Name).Title(AssetDetailResource.Category1).Width(180).Hidden((bool)ViewData["CategoryLevel1"]);
                       columns.Add().Field(a => a.Category2Name).Title(AssetDetailResource.Category2).Width(180).Hidden((bool)ViewData["CategoryLevel2"]);
                       columns.Add().Field(a => a.Category3Name).Title(AssetDetailResource.Category3).Width(180).Hidden((bool)ViewData["CategoryLevel3"]);
                       columns.Add().Field(a => a.Category4Name).Title(AssetDetailResource.Category4).Width(180).Hidden((bool)ViewData["CategoryLevel4"]);
                       columns.Add().Field(a => a.StatusName).Title(@SGE.Aladdin.Resources.SGEAdmin.AdminResources.Status).Width(120).Hidden((bool)ViewData["Status"]);
                       //if (IsAdd)
                       //{
                       //    columns.Add().Template("<a href='javascript:void(0);' onclick='CloneAsset(#:AssetId#);' class='duplicate-ico' title='" + string.Format(CommonResources.CloneTooltip, CommonResources.Assets) + "'>" + "</a>").Title(CommonResources.Clone).Width(50);
                       //}
                       if (IsDelete)
                       {
                           columns.Add().Template("<a href='javascript:void(0);' onclick='DeleteAsset(#:AssetId#);' class='delete-ico' title='" + string.Format(CommonResources.DeleteTooltip, CommonResources.Assets) + "'>" + "</a>").Title(CommonResources.Delete).Width(50);
                       }

                   })
                   .Sortable()
                   .Events(ev => ev.DataBound("onTreeListDataBound"))
                   .Events(ev => ev.FilterMenuInit("filterMenuInit"))
                   .DataSource(dataSource => dataSource
                    .Read(read => read.Action("GetAssetTreeListBasedOnPermissions", "Asset"))
                    .ServerOperation(true)                
                    .Model(m =>
                    {
                        m.Id(e => e.AssetId);
                        m.ParentId(e => e.ParentAssetId);
                        m.Field(e => e.AssetName);
                    })
                    )              
                    .Filterable(true)
                    .Reorderable(true)
                    .Resizable(true)
                    .Scrollable(true)
                    .Sortable(true)

                    )

     

     In the above code, when a column filter is applied, I am getting the entire data in 'list' variable. When this list of data is converted to treedatasourceresult, the filter will get applied to the data. In the result variable, I can see the filtered results with duplicates. Please see the attached image. Based on the filter, the result should return  asset 05, ASSET 01=>ASSET 008. But now its showing as in the attached image, which is incorrect. Could you please help me? Your help will be much appreciated.

     

     

  5. Tsvetina
    Admin
    Tsvetina avatar
    2481 posts

    Posted 25 Oct 2017 Link to this post

    Hi James,

    If I understand your code correctly, it seems like you actually do the filtering, paging and sorting manually and the data that you have in the result variable before calling ToTreeDataSourceResult is the data that needs to be shown in the TreeList.
    When you call ToTreeDataSourceResult method, it applied paging, sorting and filtering itself by using the request params.

    To prevent this, you can try creating a new TreeDataSourceResult object and assign it your list of data:
    var result = list
        .Select(e => new AssetModel
           {
               AssetId = e.AssetId,
               ParentAssetId = e.ParentAssetId,
            AssetName = e.AssetName,
            AssetIdEncrypt = e.AssetIdEncrypt,
            hasChildren = e.IsParent == 1 ? true : false,
            AssetNo = e.AssetNo,
            Model = e.Model,
            SerialNumber = e.SerialNumber,
            Category1Name = e.Category1Name,
            Category2Name = e.Category2Name,
            Category3Name = e.Category3Name,
            Category4Name = e.Category4Name,
            LocationLevel1Name = e.LocationLevel1Name,
            LocationLevel2Name = e.LocationLevel2Name,
            LocationLevel3Name = e.LocationLevel3Name,
            LocationLevel4Name = e.LocationLevel4Name,                 
            StatusName = e.StatusName,
            IsAttachmentsExist = e.IsAttachmentsExist
        });
        var dsResult = new TreeDataSourceResult() { Data = result };
     
         var jsonResult = Json(dsResult, JsonRequestBehavior.AllowGet);
         jsonResult.MaxJsonLength = Int32.MaxValue;
         return jsonResult;


    Regards,
    Tsvetina
    Progress Telerik
    Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
  6. James
    James  avatar
    4 posts
    Member since:
    Apr 2017

    Posted 29 Oct 2017 in reply to Tsvetina Link to this post

    Hi Tsvetina,

    Thanks for the reply. Actually, I am not performing any filtering manually. In the 'list' variable I am getting the entire data from the database without any filtering. Then in the result variable, I am converting the entire data to ToTreeDataSourceResult in which the request params will apply filtering to the data. The result will have my filtered data with duplicates. In the conversion, actually the issue happens. Please help me with a solution. Thanks in advance

     

    Regards 

    Binta

  7. Tsvetina
    Admin
    Tsvetina avatar
    2481 posts

    Posted 31 Oct 2017 Link to this post

    Hi Binta,

    Have you checked my colleague's reply from the other forum thread that you opened:
    Kendo Load On Demand Treelist showing duplicate records on server side filtering

    Did the upgrade to the latest version (2017.3.1026) help? If it doesn't it would be best if you could open a support ticket and send us a project where the issue can be reproduced and debugged.

    Regards,
    Tsvetina
    Progress Telerik
    Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
Back to Top