[CLOSED] TreeGrid - grouping rows

Page 1 of 2 12 LastLast
  1. #1

    [CLOSED] TreeGrid - grouping rows

    Hi Support,

    I'm using Ext.Net 4.5.1 with Asp.Net MVC. I've used grouping for the regular grid in the past by adding feature as follows,

      .Features(
                        Html.X().GroupingSummary()
                            .ID("GroupingSummary1")
                            .GroupHeaderTplString("{name}")
                            //.HideGroupedHeader(true)
                            .EnableGroupingMenu(false)                        
               )
    But this doesn't work with TreeGrid, is there anyway to make TreeGrid support grouping?

    Thanks,
    Gunvant
    Last edited by fabricio.murta; May 08, 2018 at 5:23 PM.
  2. #2
    Hello @fahd!

    The tree grid already implements an inherent grouping mechanism by the folders (non-leaf nodes) expanding and collapsing, and this would just conflict with the grouping plug in.

    I can't see a trivial way on grouping a tree grid, but I can't say it is just impossible to develop the functionality. But nevertheless, extending a client-side component (and maybe a server-side class) would most likely be the way to do that.

    If what you want is the grouping summary row alone, it may be possible to attain the summation by just using a custom handler to walk thru the displayed rows, for example showing the sum of values in just the rows that are not collapsed, or accounting only the rows marked as 'leaves' (the end points).

    If you can work on a simple test case that illustrates your page with the elements it should have and you point what's missing in the end result, maybe we could help with some more pinpointed ideas.
    Fabrício Murta
    Developer & Support Expert
  3. #3
    Thanks Fabricio,

    That's what I thought too, I'll just use node as grouping mechanism.

    I have related question, I want to have all first level node expanded initially so I'm adding child records along with root level nodes and setting Expanded property to true. And they renders expanded as I want but for some reason it still fires the ajax event to load child records for each of those root level nodes.

    Sample code: This is how I'm building root level data
     var rootNodes = new NodeCollection();
     foreach (var item in lineList)
        {
    	var treeNode = new Node();
    	treeNode.NodeID = item.ID.ToString();
    	treeNode.Leaf = false;
    	treeNode.Text = item.Description;
    	treeNode.Expanded = true;
    	
    	treeNode.CustomAttributes.Add(new ConfigItem { Name = "TypeID", Value = "Line" });	
            //Adding children for root level nodes to avoid firing ajax for each node
            // Here childNodes are another node collection 
    	treeNode.Children.AddRange(childNodes);[/B]
    	rootNodes .Add(treeNode);
         }
    Can you tell me how to avoid firing ajax since data is already loaded?

    Thanks
    Last edited by Fahd; Apr 25, 2018 at 7:03 PM.
  4. #4
    Hello @Fahd!

    It would depend on how you are setting up the loader for the store of the tree grid. You should probably mark the store records as loaded so that, when the expand event fires, it finds out the data is properly loaded (as it would when you expand a node a second time.

    To be exact about how to handle it, I would step thru the expand event of the node and check when it does or does not trigger the ajax call to pull the data off the server.

    Hope this helps!
    Fabrício Murta
    Developer & Support Expert
  5. #5
    Yes..but does Ext.Net has built in solution for marking those record as loaded or it's something I have to manually implement ?
  6. #6
    Hello @fahd!

    Sorry, I have some thoughts about this but I am just hesitating to post, because I don't really know how you are doing that and what exactly you need to achieve.

    If we could talk over an actual test case, actual code, I could for sure state there's no way to do this without some changes, suggest what to do, or point how you could benefit from Ext.NET's builtin features to dynamically load just the nodes you need.

    So, for what I know so far, I can't say Ext.NET supports or not avoiding the double load, natively.
    Fabrício Murta
    Developer & Support Expert
  7. #7
    Below is the sample code. here all the nodes at root level has their children loaded during the first ajax request. What I want to accomplish is have all of those nodes at root level as expanded by default so the already loaded sub nodes are visible.

    If I uncomment following line in the code then all root level nodes are expanded and shows child but it still fires ajax request for all nodes which I want to avoid.

    asyncNode.Expanded = true;
    Controller:
    public class MultiLevelGridTestController : Controller
        {
            public ActionResult TreeGrid()
            {
                return View();
            }
    
            public StoreResult GetNodes(string node)
            {
                NodeCollection nodes = new Ext.Net.NodeCollection();
    
                if (node == "0")
                {
                    for (int i = 1; i < 25; i++)
                    {
                        Node asyncNode = new Node();
                        asyncNode.Text = i.ToString();
                        asyncNode.NodeID = i.ToString();
                        asyncNode.Expanded = true;
                        asyncNode.CustomAttributes.Add(new ConfigItem("task", "Task_" + i));
                        asyncNode.CustomAttributes.Add(new ConfigItem("user", "User_" + i));
                        asyncNode.CustomAttributes.Add(new ConfigItem("duration", "11.6"));
                        asyncNode.CustomAttributes.Add(new ConfigItem("done", "True"));
    
                        if (i != 5)
                        {
                            asyncNode.Children.AddRange(BuildChildNodes(i.ToString()));
                        }
    
                        nodes.Add(asyncNode);
                    }
                }
                else
                {
                    nodes = BuildChildNodes(node.ToString());
                }
    
                return this.Store(nodes);
            }
    
            private static NodeCollection BuildChildNodes(string node)
            {
                NodeCollection nodes = new NodeCollection();
                for (int i = 1; i < 6; i++)
                {
                    Node treeNode = new Node();
                    treeNode.Text = node + i.ToString();
                    treeNode.NodeID = node + "-" + i.ToString();
                    treeNode.Leaf = false;
                    treeNode.CustomAttributes.Add(new ConfigItem() { Name = "task", Value = "Sub_Task_" + node.ToString() + i });
                    treeNode.CustomAttributes.Add(new ConfigItem() { Name = "user", Value = "User_" + node + i });
                    treeNode.CustomAttributes.Add(new ConfigItem() { Name = "duration", Value = "11.6" });
                    treeNode.CustomAttributes.Add(new ConfigItem() { Name = "done", Value = "True" });
                    nodes.Add(treeNode);
                }
                return nodes;
            }
        }
    View:
    @using ScriptMode = Ext.Net.ScriptMode
    @{
        ViewBag.Title = "Index";
        Layout = null;
        var X = Html.X();
    }
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <meta name="viewport" content="width=device-width"/>
        <title>TreeGrid</title>
        @Html.X().ResourceManager()
        <script>
            var formatHours = function(v) {
                if (v < 1) {
                    return Math.round(v * 60) + " mins";
                } else if (Math.floor(v) !== v) {
                    var min = v - Math.floor(v);
                    return Math.floor(v) + "h " + Math.round(min * 60) + "m";
                } else {
                    return v + " hour" + (v === 1 ? "" : "s");
                }
            };
    
            var handler = function(grid, rowIndex, colIndex, actionItem, event, record, row) {
                Ext.Msg.alert('Editing' + (record.get('done') ? ' completed task' : ''), record.get('task'));
            };
        </script>
    </head>
    <body>
    <div>
    
        <br/>
    
    
        @(
            X.TreePanel().ID("tgProjects")
                .Title("Core Team Projects")
                .Width(900)
                .Height(700)
                .RootVisible(false)
                .MultiSelect(true)
                .RowLines(true)
                .ColumnLines(true)
                .FolderSort(false)
                .Store(
                    Html.X().TreeStore().ID("treeGridStore")
                        .Proxy(
                            Html.X().AjaxProxy().Url(Url.Action("GetNodes"))
                        )
                        .Model(Html.X().Model()
                            .Fields(
                                X.ModelField().Name("task"),
                                X.ModelField().Name("user"),
                                X.ModelField().Name("duration"),
                                X.ModelField().Name("done").Type(ModelFieldType.Boolean)
                            )
                        )
                )
                .ColumnModel(
                    X.TreeColumn().Text("Task").Flex(1).DataIndex("task"),
                    X.TemplateColumn().Text("Duration").Flex(1).DataIndex("duration")
                        .Template(t =>
                        {
                            t.Html = "{duration:this.formatHours}";
                            t.Functions.Add(new JFunction
                            {
                                Name = "formatHours",
                                Fn = "formatHours"
                            });
                        }),
                    X.Column().Text("Assigned To").Flex(1).DataIndex("user").Editor(X.TextField().AllowBlank(false)),
                    X.Column().Text("Assigned BY").Flex(1).Editor(X.TextField().AllowBlank(true)),
                    X.CheckColumn().Text("Done").DataIndex("done").Flex(1).Editable(true).StopSelection(false)
                )
                .Root(
                    Html.X().Node().NodeID("0").Text("Root")
                )
               .Plugins(
                    X.CellEditing().ClicksToEdit(1).Listeners(l => { l.BeforeEdit.Handler = "return false;"; })
                )
                .View(
                    Html.X().TreeView()
                        .Plugins(
                            Html.X().TreeViewDragDrop().AllowLeafDrop(false).ContainerScroll(true)
                        )
                )
              )
    </div>
    </body>
    </html>
    Thanks

    Update:
    Updated controller code to fix the node ID duplication
    Last edited by Fahd; May 03, 2018 at 8:59 PM.
  8. #8
    Hello, @Fahd!

    Thank you very much for the code sample provided!..

    I believe you have the sample pretty much working, with (literally) one character away from the solution!

    When you first load it with Expanded = true, did you notice there are missing sub-nodes on the first couple root nodes?

    If you check the IDs being built for the IDs under those first ones, you should see something like 11, 12 and so on, as it is but a composition of node + i.ToString() where node is 1 and i varies from 1 to 2.

    In fact, when you get right after sub-node 10, you'll get another node named after 11. Not due to the composition but conflicting nevertheless.

    Is your "production candidate" code following this ID building rule as well?

    To evaluate the wrong call of the postback event, I added a 250ms sleep into BuildChildNodes().

    If I load your example "as-is", I get this incomplete load for Task_1 and Task_2. If I collapse and expand any of them, they reload and I see the 'loading' animation as expected -- now all nodes are loaded.

    But then I noticed when I collapsed and re-expanded any other root node (Task_3) onwards, all works fine.

    I'm not sure you are willing to read more, so, well, if that's the case, just make this two changes to your code:

    1. change the int node references to string (in the function header, tests like node == 0 into node =="0")
    2. add a non-integer separator char when you are building the ID, so that the eleventh (11th) element won't equal to "1" + "1". An underline character might just do it! (that was the "one character away" from the solution). That's on your controller's code line 45.

    You could perhaps just add a prefix to the root entries, but you'll still have issues, say, between nodes 1.11 and, say, 1.1.1, or 11.1 (all would translate into ID 111).

    At least here, once I added that separator character, with the 250ms hit in the node building iteration, the ajax request is not fired or, if fired it does not make it to the server-side call (no network request made at all).

    I hope this helps! Let us know if the answer is not clear, or if that still does not address the issue you're facing.
    Fabrício Murta
    Developer & Support Expert
  9. #9
    Thanks Fabricio,

    No, my production code doesn't follow that ID generation scheme, it was just sample code that I quickly wrote for other treegrid related question I had. However my current scheme had remote chance of creating duplicate node id in future so adding hyphen ("-") just to be safe is good idea.

    After following your suggestion sample works as expected. The problem is when any of the root node doesn't have data it goes to server to retrieve data which I want to stop since my data likely have lot of those and I want to avoid all these unnecessary ajax request fired.

    I've updated controller code to reflect the behavior, where I'm purposely making Task5 empty. And grid fires request to retrieve data for that Task5 node.

    Can you please take a look again to see if we can stop that from happening?

    Thanks
  10. #10
    If a node has no data, shouldn't it be leaf=true? Or do you really need it to look like a folder and "allow" expanding?

    You probably could just set its icon to folder and set it as leaf=true. If you don't want it to load anyway, and it is empty, it could not even have the expand/collapse control.

    I mean, maybe we don't really need to hack into the node logic to set it as "loaded" if it shouldn't get anything anyway.

    Is that acceptable? Or do you really need it to "think" it is already loaded, even though no data was input?
    Fabrício Murta
    Developer & Support Expert
Page 1 of 2 12 LastLast

Similar Threads

  1. [CLOSED] TreeGrid submit dirty rows
    By IMehl in forum 2.x Premium Help
    Replies: 4
    Last Post: Dec 09, 2014, 9:47 AM
  2. Replies: 0
    Last Post: Mar 06, 2014, 5:41 AM
  3. Replies: 1
    Last Post: Jan 17, 2014, 3:43 PM
  4. Grouping total rows
    By emon in forum 1.x Help
    Replies: 0
    Last Post: Mar 10, 2011, 4:24 AM
  5. [CLOSED] [1.0] GridPanel summary rows without grouping
    By danielg in forum 1.x Premium Help
    Replies: 2
    Last Post: Feb 16, 2010, 7:36 AM

Tags for this Thread

Posting Permissions