TreeGrid: Knockout + lazy loading

Posted by: j.puetz on 31 July 2019, 1:05 am EST

  • Posted 31 July 2019, 1:05 am EST

    Is it possible to use Wijmo with knockout and load child-elements for the TreeFlexGrid lazily?

    I tried to define this:

    <div id="myGridgridgrid"
         class="custom-flex-grid"
         data-bind="wjFlexGrid: {
            itemsSource: treeData,
            selectionMode: 'ListBox',
            headersVisibility: 'Column',
            lazyLoadFunction: function (node, callback) {
                console.log('This never gets run.')
            },
            childItemsPath: 'items',
         }"
    >
    

    Then I use sample data:

    var myViewModel = {
                        treeData: [ // start with three lazy-loaded nodes
                            { name: 'Lazy Node 1', items: [] },
                            { name: 'Lazy Node 2', items: [] },
                            { name: 'Lazy Node 3', items: [] }
                        ]
                    };
                    ko.applyBindings(myViewModel);
    

    Now Wijmo does not allow to expand the items, they are only useless leaves now. No button to expand them.

    Is this lazy loading not possible using wijmo.grid with knockout?

  • Posted 31 July 2019, 6:29 pm EST

    Hi Janis,

    The lazyLoadFunction is not a property of FlexGrid that is why it is never called. In order to lazy load the child items, you will have to add at least one dummy data inside the items array otherwise the grid will not show the expand/collapse button. You will also need to handle the groupCollapsedChanged event and remove the dummy item and add the actual items to the current row. Please refer to the sample attached for the same.

    FlexGrid_LazyLoadTree.zip

    Regards,

    Ashwin

  • Posted 31 July 2019, 10:54 pm EST

    Thank you for helping me realize that there is also an individual component TreeView. With Knockout there examples is no TreeView example, so I am wondering whether the TreeView is compatible with knockout?

    I only noticed the FlexGrid beeing able to show a TreeView and must have overseen the TreeView. Then I found examples for the lazy loading using lazyLoadFunction, but failed to realize that a different component was used there.

    What are the differences between the FlexGrid Tree and the TreeView tree? When would I use which?

  • Posted 1 August 2019, 3:13 pm EST

    Hey Janis,

    All of our wijmo controls are compatible with knockoutjs. They all can be easily used with knockout either by plain Javascript or you can use the knockout interop module.

    The FlexGrid is a grid control used to display data in tabular form. The hierarchical FlexGrid or a FlexGrid Tree is used show data in hierarchical form. The FlexGrid Tree is typically used to show data that contains child items that span to multiple columns.

    The TreeView is a navigation control which displays a hierarchical list of data which may contain text, checkboxes, images, or arbitrary HTML content. A TreeView is typically used to display the headings in a document, the entries in an index, the files and directories on a disk, or any other kind of information that might usefully be displayed as a hierarchy.

    Let us know if you need any more clarification.

    ~regards

  • Posted 12 August 2019, 2:18 am EST

    Okay I found a way to get the selected item.

    
    data-bind="wjFlexGrid: {
                    itemsSource: treeData,
                    childItemsPath: 'childItems',
                    selectionMode: 'Row',
                    headersVisibility: 'Column',
                    selectionChanged: onSelect,
                 }
    
    
    
    onSelect = (s, e, m) => {
        var selectedItem = e.rows[m.range.row].dataItem;
        ...
    }
    
    

    Its a bit cumbersome, but it works as desired :slight_smile:

  • Posted 12 August 2019, 3:51 pm EST

    Hi Janis,

    We are really sorry for the inconvenience caused.

    Since the knockout binding does not support selection property, the selectionChanged event can be used to get the current item. In the onSelect method, you can simply get the currentItem from CollectionView instead of using the dataItem property:

    var selectedItem = e.collectionView.currentItem;
    

    Please refer to the sample below:

    https://jsfiddle.net/3au4m8on/

    ~regards

  • Posted 15 August 2019, 11:04 pm EST

    Thank you very much for both samples. Together they helped me a lot!

    Some questions did come up though:

    1.) About the last example:

    What is the point of the grid observable?

    this.grid = ko.observable();
    

    And what function has the control databind?

    <div data-bind="wjFlexGrid: { itemsSource: data, autoGenerateColumns: false, selectionChanged: selChange, control: grid}">
    

    Both are probably related. The example works if I remove both, but I am still curious about their function.

    2.) How can I access the FlexGrid object instance, when using knockout?

    Currently I use the initialized function and then save the instance (for later use):

    initGrid = (data, s) => {
           this.treeGrid = s;
       }
    
    ```
    

    data-bind=“wjFlexGrid: {

    itemsSource: treeData,

    childItemsPath: ‘childItems’,

    selectionMode: ‘Row’,

    headersVisibility: ‘Column’,

    selectionChanged: onSelect,

    treeIndent: 20,

    initialized: initGrid

    }”

    Is there another way to access the FlexGrid instance (when using knockout)?
    
    3.)  What parameters does the selectionChanged function get?
    I think the first is the supplied knockout viewmodel.
    Second and third both seem to keep some of the data.
    The names in the examples are usually just letters, so I would like to know just a little bit more ;-)
  • Posted 18 August 2019, 3:07 pm EST

    Hi Janis,

    Regarding 1 and 2:

    The control property is used to create a reference to the FlexGrid which can be used in the JS.

    In the sample, the control property is bound to the grid variable which is a knockout observable.

    Regarding selectionChanged event:

    The first parameter, as you said, is the knockout view model. The second parameter is the instance of the FlexGrid which raised the selectionChanged event. And the third parameter is an instance of CellRangeEventArgs class and it contains information about the row, column, etc that were affected by the event.

    API Reference:

    ~regards

  • Posted 19 August 2019, 2:13 am EST

    1.)

    Thanks for the info about the control observable. It works nicely!

    I had to ask, since I couldnt find it in the api docs.

    The same goes for the initialized binding. I couldnt find either in the api docs, but got both presented in samples and both work.

    2.)

    I had also tried your solution to get the current item. (var selectedItem = e.collectionView.currentItem;)

    While this works with the example, it does not work in my project. It always yields undefined, so I am assuming it does not work with the hierarchical version of the FlexGrid.

    3.)

    Is it possible to configure the FlexGrid to start collapsed?

    I found the method grid.collapseGroupsToLevel(0);

    If I call that before adding items to the FlexGrid, it wont work. The added items are expanded anyways.

    If I call that after adding items to the FlexGrid (and call this.collectionView.refresh();), it will emit groupCollapsedChanged events for all items. Since I am still setting up the initial state (lazily), this is not desired. I know I could manually get by this and trash all the extra events, but I wonder if I missed another possible configuration option?

    Maybe I should have used the TreeView instead, since it seems to have a better api for this. Do you think I should switch to that component? (Both seemed to be fit for my requirements.)

  • Posted 19 August 2019, 4:51 pm EST

    Hi Janis,

    I am glad that I was able to help you regarding the control and initialized binding.

    Regarding collectionView.currentItem:

    In a hierarchical grid, only the top-level items are present in the CollectionView. That is why the currentItem is null. For a tree grid, you may use the dataItem property of the current Row to get the current item.

    var currentItem = grid.rows[grid.selection.row].dataItem;
    

    Regarding group collapse:

    You may handle the itemsSourceChanged event of the FlexGrid and call the collapseGroupsToLevel method inside this event handler. By doing this, whenever you will assign a new itemsSource to the FlexGrid, the grid will automatically collapse to level 0.

    In HTML:

    <div data-bind="wjFlexGrid: { initialized: initGrid, groupCollapsedChanged: addNode, childItemsPath: 'children', control: grid, itemsSourceChanged: sourceChanged }"></div>
    

    In JS:

    this.sourceChanged = function(data, s, e) {
    	s.collapseGroupsToLevel(0);
    }
    

    Regarding using TreeView:

    You could use TreeView instead of a hierarchical FlexGrid since it supports many of the functionality out of the box like lazy loading, etc. But, the TreeView is a navigation control and cannot show multiple columns. If you do not have multiple columns, you may easily use TreeView.

    Let me know if you have any further queries.

    ~regards

  • Posted 22 August 2019, 11:24 pm EST - Updated 3 October 2022, 10:11 am EST

    Thank you for your help. I could not have done it without you!

    Now my next question :smiley:

    The hierarchical FlexGrid shows Buttons to expand, but when an item has no children it does not look like it is on the same level.

    I am assuming I have to configure some option on the wjFlexGridColumn level. I couldnt find anything regarding this in the Column class though. https://www.grapecity.com/wijmo/api/classes/wijmo_grid.column.html#wordwrap

    Here is an example of what I mean:

    The selected iteam is a child of the item above it, but both seem to be on the same level.

    The last item would be on a similar depth level, but looks like it is a level deeper. This is because it has not been lazy loaded yet. It does not know, that it contains no children, other than the dummy child, which gets removed when expanding. This causes the system to remove the expand button, which causes the text to move to the left.

    How can I solve this?

  • Posted 25 August 2019, 9:50 pm EST

    Hey Janis,

    You may use the itemFormatter callback to add padding to the rows that do not have any child. Refer to the code snippet and the sample:

    this.formatItem = function(panel, row, col, cell) {
    	if(panel === panel.grid.cells && col === 0) {
    		var item = panel.rows[row].dataItem;
    		if(!item.children) {
    			var padding = ((panel.grid.treeIndent * panel.rows[row].level) + 20);
    			cell.style.paddingLeft = padding + 'px';
    		}
    	}
    }
    

    https://jsfiddle.net/zes6qoL0/

    ~regards

  • Posted 29 August 2019, 2:04 am EST

    Thanks that works! For me the value of 20 had to be 28 though.

    Another question:

    I would like one column to be a custom item. A button, that does something with the Id of my data.

    I tried from using this example https://www.grapecity.com/wijmo/api/classes/wijmo_knockout_grid.wjflexgridcolumn.html

    I never shows my buttons though. It always shows the dafault template.

    This is what I did:

    
    <div class="custom-tree"
         data-bind="wjFlexGrid: {
            itemsSource: collectionView,
            childItemsPath: 'childItems',
            selectionMode: 'Row',
            headersVisibility: 'Column',
            selectionChanged: onSelect,
            treeIndent: 20,
            control: treeGrid,
            groupCollapsedChanged: ongroupCollapsedChanged,
            itemFormatter: formatItem,
            allowSorting: false
         }">
            <a data-bind="attr: {
                href: 'https://finance.yahoo.com/q?s=' + $item().id() },
                text: $item().id">
            </a>
        </div>
    </div>
    
    
  • Posted 29 August 2019, 5:38 pm EST

    Hi Janis,

    Currently, cell templates are not supported in KnockoutJS with a hierarchical FlexGrid. But, we have added an enhancement request for the same with internal tracking id 396135. We will give you an update as soon as this feature is implemented. Until then, you may use the itemFormatter callback to add buttons the FlexGrid. Please refer to the sample below:

    https://jsfiddle.net/nkwerm4L/

    ~regards

  • Posted 29 August 2019, 11:32 pm EST

    Thanks, Ashwin!

    That enhancement would we awesome!

    Is it possible to give a nonbinding estimate when this feature will be done?

    Currently I do use the itemFormatter as suggested. Now I am wondering how to create an element with a knockout click-binding in there.

    Right now I am not sure whether this is possible at all?

    Regards

  • Posted 1 September 2019, 4:27 pm EST

    Hi Janis,

    Regarding the enhancement:

    I have asked the dev team to provide an ETA on the case. I will update you as soon as I have further information.

    Regarding creating ko binding:

    You may add a button to the cell element in the itemFormatter and use the ko.applyBindings method to apply the binding again. Please refer to the sample below:

    https://jsfiddle.net/xczv6hpm/

    ~regards

Need extra support?

Upgrade your support plan and get personal unlimited phone support with our customer engagement team

Learn More

Forum Channels