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

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

6 Answers 309 Views
TreeList
This is a migrated thread and some comments may be shown as answers.
binta
Top achievements
Rank 1
binta asked on 31 Mar 2017, 08:59 AM
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

6 Answers, 1 is accepted

Sort by
0
Tsvetina
Telerik team
answered on 04 Apr 2017, 09:22 AM
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.
0
Tsvetina
Telerik team
answered on 06 Apr 2017, 11:22 AM
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.
0
James
Top achievements
Rank 1
answered on 24 Oct 2017, 08:22 AM

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.

 

 

0
Tsvetina
Telerik team
answered on 25 Oct 2017, 02:11 PM
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.
0
James
Top achievements
Rank 1
answered on 29 Oct 2017, 05:28 AM

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

0
Tsvetina
Telerik team
answered on 31 Oct 2017, 03:00 PM
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.
Tags
TreeList
Asked by
binta
Top achievements
Rank 1
Answers by
Tsvetina
Telerik team
James
Top achievements
Rank 1
Share this question
or