itemFormatter performance optimizations

Posted by: rewen on 14 September 2017, 2:27 am EST

    • Post Options:
    • Link

    Posted 14 September 2017, 2:27 am EST

    I am noticing that my FlexGrid gets very slow (to scroll, etc) when using itemFormatters. How can I optimize this in order to prevent recreating DOM elements over and over?

    I also have a related problem where a Menu component is recreated as the grid is scrolled and an error is thrown stating that there’s already a control in the cell.

    Am I missing something simple?

  • Posted 14 September 2017, 2:27 am EST

    Hi,

    As per our understanding, you are creating a custom cell in FlexGrid either by appending a component in it or performing some other modification.

    If our understanding is correct, then we would suggest you to make use of Cell Template to achieve the same.

    Here is the demo link which would help you to get the desired behavior:

    http://demos.componentone.com/wijmo/5/Angular/CellTemplateIntro/CellTemplateIntro/

    Also, please verify the behavior with latest build as well.

    Here is the download link to get the latest build:

    http://wijmo.com/almost-there?dlfile=http://wijmo.com/get/C1Wijmo-Eval_5.20153.102.zip

    Hope it helps.

    Please let us know if we misunderstood something by sharing with us a small stripped down sample application replicating the issue.

    Regards,

    Raunak Ladha

  • Posted 14 September 2017, 2:27 am EST

    I am using the latest build, too.

    It seems like the cell element is recycled but not the contents of it, when using itemFormatter.

  • Posted 14 September 2017, 2:27 am EST

    Here is something I’ve tried, which is to set an attribute on the cell stating whether or not my custom content has already been created. If it hasn’t then I create my HTML and append it.

    [js]

    // create the icon

    if (cell.getAttribute(‘fg-select’) !== ‘true’) {

    icon = document.createElement(‘i’);

    icon.setAttribute(‘title’, options.titles[state]);

    icon.setAttribute(‘data-icon’, options.icons[state]);

    icon.setAttribute(‘data-row-index’, row.index);

    icon.classList.add(‘fg-select-icon’);

    icon.classList.add(data.iconClass);

    icon.classList.add(options.classes[state]);

    cell.setAttribute('fg-select', 'true');
    
    cell.appendChild(icon);
    

    }

    [/js]

    Now if you check the attached video, you will see what I assume is a bug where the cell contents are sometimes in the cell, and sometimes not, as the grid is scrolled.

    2015/12/itemFormatterBug.wmv

  • Posted 14 September 2017, 2:27 am EST

    The performance issue I mentioned can be seen here, where I’ve added a wijmo Menu to the last column:

    http://jsfiddle.net/p56qh12x/3/

    The Menu issue I mentioned can be seen here (same as the above fiddle, but with a CSS height added):

    http://jsfiddle.net/p56qh12x/4/

    Also notice how the menus are not usable. In my application they are more usable than in the fiddles, however there’s still a z-index issue or something preventing the menu options from being clicked (unless it’s the menu in the very last row).

    Note that I don’t actually use Angular but it was easier to fork an existing fiddle that does rather than to create something new.

  • Posted 14 September 2017, 2:27 am EST

    Sorry to keep adding more to this.

    I did a little test. I picked an arbitrary cell in my grid and saved a reference to it globally (window.ryan = cell). Then I added this to itemFormatter:

    [js]

    if (cell == window.ryan) {

    console.log(‘itemFormatter called’);

    }

    [js]

    What I found is that scrolling causes a LOT of extra calls to the itemFormatter, much more than seem necessary. Especially since the cell in question was out of view for much of the time and it was still being re-rendered.

    My table in question has 120 rows and if I use the keyboard to navigate from top row down to bottom (one at a time using the downward arrow) I see that the cell is re-rendered 78 times.

    This is most likely the root cause of the performance issue I am seeing as soon as an itemFormatter is used.

    The secondary issue would be that cell contents are sometimes recycled and sometimes not, unpredictably.

  • Posted 14 September 2017, 2:27 am EST

    Now I’m even more confused - it also seems like cells are being recycled as completely different cells.

    Eg, if I have my formatter apply an HTML attribute to a cell when the column name for the cell is “select”, I am able to find that attribute applied to cells in another column later on after scrolling up and down… within a col called “enable” for example. It’s as if the cell that was originally in the “select” col was recycled for use in the “enable” col.

  • Posted 14 September 2017, 2:27 am EST

    Hi,

    Could you please explain us the exact requirement you want to achieve?

    If you just want to display a dropdown in a cell of FlexGrid and perform a selection of the item in the drop down then instead of using Menu, we would suggest you to make use of ComboBox control shipped with Wijmo5 suite.

    Here is the code snippet:

    [js]

    grid.itemFormatter = function (panel, r, c, cell) {

    if (panel.columns[c].name !== ‘expenses’) { return; };

                var comboItems = [
                  { cmd: 'open', text: 'Open Some Page' },
                  { cmd: 'sources', text: 'Current Sources' },
                ];
    
                var combo = new wijmo.collections.CollectionView(comboItems);
    
                var comboBox = new wijmo.input.ComboBox(cell, {
                    itemsSource: combo,
                    displayMemberPath: 'text'
                });
            };[/js]
    

    For complete implementation, please refer to the attached sample application.

    Hope it helps.

    Regards,

    Raunak Ladha

    2015/12/FlexGrid_ItemFormatter_Menu.zip

  • Posted 14 September 2017, 2:27 am EST

    Hi Raunak,

    Is there a way to have cell templates without using Angular? I am coding PureJS style.

    I have two formatters which are creating some HTML ( elements which have font-based icons in them) and then appending that HTML into the cell.

    Eg:

    [js]

    icon = document.createElement(‘i’);

    icon.setAttribute(‘title’, options.titles[state]);

    icon.setAttribute(‘data-icon’, options.icons[state]);

    icon.setAttribute(‘data-row-index’, row.index);

    icon.classList.add(‘fg-select-icon’);

    icon.classList.add(data.iconClass);

    icon.classList.add(options.classes[state]);

                cell.appendChild(icon);
    

    [/js]

    I also have a Menu input in another col formatter

    [js]

    // define menu options

    var menuItems = [

    { cmd: ‘sources’, text: ‘Current Sources’ },

    ];

        // create the menu
        var menuCV = new wijmo.collections.CollectionView(menuItems);
        
        var menu = new wijmo.input.Menu(cell, {
            itemsSource: menuCV,
            header: 'More',
            displayMemberPath: 'text'
        });
    

    [/js]

    When I use the above code, this is the error I get on scroll:

    [js]

    Uncaught ** Assertion failed in Wijmo: Element is already hosting a control.t @ wijmo.min.js:formatted:125t @ wijmo.min.js:formatted:1191i @ wijmo.input.min.js:13r @ wijmo.input.min.js:13i @ wijmo.input.min.js:13DeviceUI.linksGrid.formatters.more @ links_grid.js:409(anonymous function) @ flexgrid_wrapper.js:66EvertzJS.FlexGridWrapper.formatItem @ flexgrid_wrapper.js:65i.updateCell @ wijmo.grid.min.js:13i._renderCell @ wijmo.grid.min.js:13i._renderRow @ wijmo.grid.min.js:13i._updateContent @ wijmo.grid.min.js:13u._updateContent @ wijmo.grid.min.js:13(anonymous function) @ wijmo.grid.min.js:13requestAnimationFrame (async)(anonymous function) @ wijmo.grid.min.js:13

    [/js]

  • Posted 14 September 2017, 2:27 am EST

    Hi Raunak,

    Here are the 2 requirements I am trying to achieve:

    1. I need to display custom HTML in multiple columns without the grid becoming too slow. There may be tens of thousands of items populating the grid.

    2. One of the columns in my grid needs to be a menu of some sort. The menu options will be predefined and represent actions which can be performed in my application, in the context of the item populating the row.

      Eg: If a row represents a device on a network, the menu might have an option to view a web page hosted at the device IP address.

    Problems achieving requirement #1:

    Scrolling performance of the grid deteriorates very noticeably when using itemFormatter. Even appending simple elements containing only text causes noticeable slowdown when scrolling the grid. Some testing shows that the performance issue may be caused by excessive re-rendering of cells when the grid is scrolled.

    I thought that I could make re-renders faster by caching the HTML nodes that my itemFormatter appends into the cells, so that they could be recycled the next time itemFormatter is called. This did not improve performance very much (if at all) and instead caused the bug which can be seen in video I attached earlier.

    After fighting to find out what caused that bug I came to the conclusion that the way in which FlexGrid recycles cell elements while scrolling is either buggy/unreliable or just unpredictable. I came to this conclusion after my itemFormatter added an HTML attribute to cells of a specific column and I found that HTML attribute on cells in other columns after scrolling the grid. I guess FlexGrid recycles cell elements, but they may be used in different places (different rows/colss) which means that setting attributes on cells will only cause problems. I really hope this is just a bug.

    Knowing that cell elements might be recycled to different rows/cols, it could explain the appearance of excessive re-rendering (it may not be excessive, it may be that the cell element I was watching simply moved around to different rows/cols to remain always-in-view, and was being re-rendered with the contents of the new row/col). None of this is documented, so I don’t know how it works, or what is safe, not safe, or recommended.

    Problems achieving requirement #2:

    When scrolling the grid my itemFormatter is called multiple times on the same cell which means that the Menu (or ComboBox) is recreated multiple times for every cell, but wijmo thows an error stating that the cell is already hosting a control. Your example does this, too, and I’ve attached a video of it happening there.

    There’s also an issue of CSS styling menus/comboboxes in cells (they don’t look right). In my application I cannot click the menu items unless it’s a menu that is in the very last row where the items appear below the grid (it may be my own bug with z-indexing).

    2015/12/itemFormatterBug2.wmv

  • Posted 14 September 2017, 2:27 am EST

    Hi,

    Please refer to the following code snippet which is a better way of displaying custom HTML element in the cells of FlexGrid. It also improves rendering performance.

    [js]grid.itemFormatter = function (panel, r, c, cell) {

    if (panel.columns[c].name == ‘expenses’ && panel.cellType == 1) {

                    var comboItems = [
                      { cmd: 'open', text: 'Open Some Page' },
                      { cmd: 'sources', text: 'Current Sources' },
                    ];
    
                    var combo = new wijmo.collections.CollectionView(comboItems);
    
                    var comboInput = document.createElement("input");
                    comboInput.Id = "combo1";
                    cell.innerHtml = "";
                    cell.appendChild(comboInput);
    
                    var comboBox = new wijmo.input.ComboBox(comboInput, {
                        itemsSource: combo,
                        displayMemberPath: 'text'
                    });
                }
            };[/js]
    

    For complete implementation, please refer to the attached sample application.

    Hope it helps.

    Regards,

    Raunak Ladha

    2015/12/FlexGrid_ItemFormatter_Menu_Modified.zip

  • Posted 14 September 2017, 2:27 am EST

    Thanks for your efforts, making the input manually solves the 2nd issue but unfortunately performance is very very bad. Performance seems directly related to the height of the grid.

    Using your example from the post above, change

    [js]

    .wj-flexgrid {

    height: 100%;

    }

    [/js]

    And you will see that your example performs very very slowly in Chrome.

  • Posted 14 September 2017, 2:27 am EST

    Hi,

    We could observe the behaviour at our end as well. Yes, you are right, the performance is related to the height of the FlexGrid.

    Since, you have set height to 100%, therefore all the rows will be rendered initially which results in delay in loading. Also, you are using custom HTML in each row which adds to the delay.

    Thus, in order to get rid of this issue, we would suggest you to either make use of scrolling or paging feature of FlexGrid.

    Also, please let us know if there is any specific requirement due to which you have set height to 100% so that we can escalate it further.

    Regards,

    Raunak Ladha

  • Posted 14 September 2017, 2:27 am EST

    In my application the grid has CSS like so:

    [js]

    position: absolute;

    top: 50px;

    bottom: 10px;

    left: 10px;

    right: 10px;

    [/js]

    This results in a grid approximately 870px tall in my case. Please see the video I attached earlier:

    http://wijmo.com/wp-content/uploads/2015/12/itemFormatterBug.wmv

    This is the actual application of the grid.

  • Posted 14 September 2017, 2:27 am EST

    Hi,

    Thank you for sharing the video with us. We are investigating on the issue further at our end.

    As per our understanding from the css you have posted in your last response, you didn’t have set height of the Grid explicitly rather it automatically gets rendered in approximately 870px due to some styling you have performed.

    Thus, we would request you to kindly try to set the height of the grid explicitly to “870px” and please let us know if you can observe any difference in performance.

    Thank you for the patience.

    Regards,

    Raunak Ladha

  • Posted 14 September 2017, 2:27 am EST

    Hi Raunak,

    There appears to be no difference when setting it explicitly.

    Thanks

  • Posted 14 September 2017, 2:27 am EST

    Hi again,

    Has there been any update to this issue of excessive re-rendering?

  • Posted 14 September 2017, 2:27 am EST

    Hi,

    We have escalated the issue to the development team and will let you know as soon as we get any information from them.

    Regards,

    Raunak Ladha

  • Posted 9 April 2019, 2:45 pm EST

    Hi,

    I’m also experiencing performance issues when scrolling a FlexGrid table using lots of itemFormatters.

    What is the current recommendation for rendering custom elements in cells without having them re-rendered on every scroll event?

    Thanks,

  • Posted 31 May 2022, 6:55 pm EST

    same here

  • Posted 1 June 2022, 8:13 pm EST

    Hello,

    Could you please provide some more detailed information on the exact issue you have faced? also if possible please share a small sample replicating the issue so that we can investigate the issue at our end and provide you with a solution accordingly.

    Regards

  • Posted 1 June 2022, 8:38 pm EST

    I found the issue in my case, it was due to some bad code to set background colour.

    basically, cell.style.backgroundColor = ‘’ caused the performance issue

    bad code:

    itemFormatter = (

    p: wjcGrid.GridPanel,

    rowIdx: any,

    columnIdx: any,

    cell: any

    ) => {

    if (p.cellType === wjcGrid.CellType.Cell) {

    if (rowIdx > columnIdx) {

    cell.style.backgroundColor = ‘#f4f4f4’;

    } else if (rowIdx === columnIdx) {

    cell.style.backgroundColor = ‘#e5e5e5’;

    }

    }

    };

    updated which has addressed this issue

    itemFormatter = (

    p: wjcGrid.GridPanel,

    rowIdx: any,

    columnIdx: any,

    cell: any

    ) => {

    if (p.cellType === wjcGrid.CellType.Cell) {

    if (rowIdx > columnIdx) {

    if (!cell.classList.contains(‘cell-low-matrix’)) {

    this._render.addClass(cell, ‘cell-low-matrix’);

    }

    } else if (rowIdx === columnIdx) {

    if (!cell.classList.contains(‘cell-diagonal’)) {

    this._render.addClass(cell, ‘cell-diagonal’);

    }

    }

    }

    };

  • Posted 2 June 2022, 2:48 pm EST

    Hello,

    We are glad that you were able to resolve your issue on your own. Thank you for sharing the solution with us.

    Regards

Need extra support?

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

Learn More

Forum Channels