Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

  • 12.0p ListGrid recordComponent white area (no drawAhead?) when mousewheel scrolling

    Hi Isomorphic,

    please see this modified online testcase (v12.0p_2019-08-14).

    Every once in a while when scrolling the ListGrid with the mouse wheel, there is an empty space that takes ~0.5 seconds to fill. Empty space is normally ~3 rows, but can be ~12 rows (FYI: I do see 22 rows at once in the sample on my FullHD monitor).

    I made the sample that way that all data is loaded upfront, so this is no network issue.

    A minimal mouse-wheel advance scrolls 2 rows for me. A "full finger" mouse-wheel turn is ~8 minimal advances = ~16 records.
    Basically at the end of every "full finger" mouse-wheel I do see a white empty space (now Win10 Chromium 76.0.3809.100, but browser independent).
    That white space is filled correctly after some time, but it is noticeable enough before this, so that customers complain.
    This does not happen at all with showRecordComponents: false.

    Compared to the testcase I also have more columns and horizontal scrolling in my application.

    It seems that either drawAheadRatio is not working in this case or something else is wrong. virtualScrolling does not seem to make a difference here. Same for recordComponentPoolingMode.

    Do you have any idea what is causing this delay? I can see CPU usage go up when scrolling, but is this really related?

    Thank you & Best regards
    Blama

    Code:
    isc.ListGrid.create({
        ID:"dsListGrid", 
        width: "100%",
        height: "100%",
        autoFetchData: true,
        dataSource: "supplyItem",
        dataFetchMode: "local",
        virtualScrolling: false,
        showRecordComponents: true,
        showRecordComponentsByCell: true,
        recordComponentPoolingMode: "recycle",
        canRemoveRecords: true,
        showRowNumbers: true,
    
        // scrollRedrawDelay: 1,
        // scrollWheelRedrawDelay: 1,
        // drawAheadRatio: 1.0,
    
      fields:[
           {name: "itemID"},
           {name: "itemName"},
           {name: "SKU"},
           {name: "category"},
           {name: "unitCost"},
           {name: "units"},
           {name: "inStock"},
           {name: "nextShipment"},
           {name: "description", },
           //{name: "buttonField", title: "Info", align: "center" },
           {name: "iconField", title: "Comments/Stats", width: 110 }
        ],
    
        createRecordComponent : function (record, colNum) {  
            var fieldName = this.getFieldName(colNum);  
    
            if (fieldName == "iconField") {  
                var recordCanvas = isc.HLayout.create({
                    height: 22,
                    width: "100%",
                    align: "center"
                });
    
                var editImg = isc.ImgButton.create({
                    showDown: false,
                    showRollOver: false,
                    layoutAlign: "center",
                    src: "icons/16/comment_edit.png",
                    prompt: "Edit Comments",
                    height: 16,
                    width: 16,
                    grid: this,
                    click : function () {
                        isc.say("Edit Comment Icon Clicked for country : " + record["SKU"]);  
                    }
                });
    
                var chartImg = isc.ImgButton.create({
                    showDown: false,
                    showRollOver: false,
                    layoutAlign: "center",
                    src: "icons/16/chart_bar.png",
                    prompt: "View Chart",
                    height: 16,
                    width: 16,
                    grid: this,
                    click : function () {
                        isc.say("Chart Icon Clicked for country : " + record["SKU"]);  
                    }
                });
    
                recordCanvas.addMember(editImg);  
                recordCanvas.addMember(chartImg);  
                return recordCanvas;  
            } else {  
                return null;  
            }  
        },
    
        updateRecordComponent: function (record, colNum, component, recordChanged) {
            var fieldName = this.getFieldName(colNum);  
            if (fieldName == "iconField") {
                var membersArray = component.getMembers();
                for (i=0;i<membersArray.size;i++) {
                    if (i == 0) {
                        membersArray[i].addProperties({
                            click : function () {
                                isc.say("Edit Comment Icon Clicked for country : " + record["SKU"]);
                            }
                        });        
                    } else {
                        membersArray[i].addProperties({
                            click : function () {
                                isc.say("Chart Icon Clicked for country : " + record["SKU"]);
                            }
                        });
                    }
                }    
            } else if (fieldName == "buttonField") {      
                component.addProperties({
                    icon: "flags/16/" + record["itemName"] + ".png",
                    click : function () {
                        isc.say(record["SKU"] + " info button clicked.");
                    }
                }); 
            } else {  
                return null;  
            }  
            return component;
        }
    
    });

  • #2
    Click image for larger version

Name:	ListGrid scrolling white space.gif.gif
Views:	1
Size:	904.9 KB
ID:	259031

    Comment


    • #3
      drawAheadRatio is working fine, but you may be expecting that every time you stop scrolling, new rendering takes place up to the drawAheadRatio. This isn't how it works: new rendering doesn't take place until whitespace is shown.

      The reason the redraw is slow here is most likely because of your recordComponents. The brief delay is probably redrawing and/or creation of more recordComponents. You can easily confirm this by using the Developer Console and/or logging when your createRecordComponent / updateRecordComponent methods are being called.

      Note that in the docs for showRecordComponents we recommend against using them for simple clickable icons like you have here - see those docs for alternatives.

      Note that you also do not have any apparent reason to turn off virtualScrolling, which will also have a negative impact here.

      Comment


      • #4
        Hi Isomorphic,

        you are right about recordComponents and potential alternatives here. With the only downside of multiple columns, I could also use valueIcons with click handlers here.
        I assume I started with recordComponents because of this sample, as in my application I'm doing the same thing (multiple icons in one field). Perhaps the sample should also advise that it is there only to the technique and that this case is better solved with valueIcons. Also here the sample includes virtualScrolling: false, which is most likely why I started with it.

        W.r.t. the calling of the createRecordComponent / updateRecordComponent methods you are right. I did not expect them to be called like they are for a whole bunch of displayedRows*drawAheadRatio at start and then 20 rows per updateRecordComponent, but instead displayedRows*drawAheadRatio at start and then little amounts of updateRecordComponent for every scroll.
        Also I expected the amount of updateRecordComponent to correlate with drawAheadRatio, which is not happening.
        It seems drawAhead is not triggered per viewport change, which IMHO is what the docs describe:
        Code:
        How far should we render rows ahead of the currently visible area? This is expressed as a ratio from viewport size to rendered area size.
        
        Tweaking drawAheadRatio allows you to make tradeoffs between continuous scrolling speed vs initial render time and render time when scrolling by large amounts.
        It does not seem that a high drawAheadRatio has advantages w.r.t. to recordComponents, actually they seem to be disconnected, as even a drawAheadRatio of 1.2 does not result in more calls of updateRecordComponent with less rows.

        With the changed sample one can see recordComponent getting triggered. Is it also possible to log the moving window of drawAhead?

        Thank you & Best regards
        Blama

        Code:
        isc.ListGrid.create({
            ID:"dsListGrid",
            width: "100%",
            height: "100%",
            autoFetchData: true,
            dataSource: "supplyItem",
            dataFetchMode: "local",
            virtualScrolling: false,
            showRecordComponents: true,
            showRecordComponentsByCell: true,
            recordComponentPoolingMode: "recycle",
            canRemoveRecords: true,
            showRowNumbers: true,
        
            // scrollRedrawDelay: 1,
            // scrollWheelRedrawDelay: 1,
            drawAheadRatio: 1.2,
        
          fields:[
               {name: "itemID"},
               {name: "itemName"},
               {name: "SKU"},
               {name: "category"},
               {name: "unitCost"},
               {name: "units"},
               {name: "inStock"},
               {name: "nextShipment"},
               {name: "description", },
               //{name: "buttonField", title: "Info", align: "center" },
               {name: "iconField", title: "Comments/Stats", width: 110 }
            ],
        
            createRecordComponent : function (record, colNum) {  
                var fieldName = this.getFieldName(colNum);  
        
                if (fieldName == "iconField") {
                    isc.logWarn("createRecordComponent called for row " + (dsListGrid.getRecordIndex(record)+1));
                    var recordCanvas = isc.HLayout.create({
                        height: 22,
                        width: "100%",
                        align: "center"
                    });
        
                    var editImg = isc.ImgButton.create({
                        showDown: false,
                        showRollOver: false,
                        layoutAlign: "center",
                        src: "icons/16/comment_edit.png",
                        prompt: "Edit Comments",
                        height: 16,
                        width: 16,
                        grid: this,
                        click : function () {
                            isc.say("Edit Comment Icon Clicked for country : " + record["SKU"]);  
                        }
                    });
        
                    var chartImg = isc.ImgButton.create({
                        showDown: false,
                        showRollOver: false,
                        layoutAlign: "center",
                        src: "icons/16/chart_bar.png",
                        prompt: "View Chart",
                        height: 16,
                        width: 16,
                        grid: this,
                        click : function () {
                            isc.say("Chart Icon Clicked for country : " + record["SKU"]);  
                        }
                    });
        
                    recordCanvas.addMember(editImg);  
                    recordCanvas.addMember(chartImg);  
                    return recordCanvas;  
                } else {  
                    return null;  
                }  
            },
        
            updateRecordComponent: function (record, colNum, component, recordChanged) {
                var fieldName = this.getFieldName(colNum);  
                if (fieldName == "iconField") {
                    isc.logWarn("updateRecordComponent called for row " + (dsListGrid.getRecordIndex(record)+1));
                    var membersArray = component.getMembers();
                    for (i=0;i<membersArray.size;i++) {
                        if (i == 0) {
                            membersArray[i].addProperties({
                                click : function () {
                                    isc.say("Edit Comment Icon Clicked for country : " + record["SKU"]);
                                }
                            });        
                        } else {
                            membersArray[i].addProperties({
                                click : function () {
                                    isc.say("Chart Icon Clicked for country : " + record["SKU"]);
                                }
                            });
                        }
                    }    
                } else {  
                    return null;  
                }  
                return component;
            }
        
        });

        Comment


        • #5
          When unrendered rows are scrolled into the viewport, a new set of rows is rendered based on the current viewport and drawAheadRatio. The docs describe this accurately.

          Yes, if you enable more logging, you can see descriptions of the HTML ranges being rendered and other details.

          Comment


          • #6
            Hi Isomorphic,

            thanks, setting log level to DEBUG for custom category gridHTML gave more insights.

            It seems that drawAhead is really looking only forward (=a sliding window with the current top row as 1st row) and is not a sliding window with the current viewport in the middle. Therefore at certain points scrolling up an down by one row will trigger getTableHTML every time. Is this on purpose / by design?

            Click image for larger version

Name:	ListGrid getTableHTML triggered too often.gif
Views:	1
Size:	885.6 KB
ID:	259044



            Code:
            isc.ListGrid.create({
                ID:"dsListGrid",
                width: "100%",
                height: 680,
                autoFetchData: true,
                dataSource: "supplyItem",
                dataFetchMode: "local",
                virtualScrolling: false,
                showRecordComponents: true,
                showRecordComponentsByCell: true,
                recordComponentPoolingMode: "recycle",
                canRemoveRecords: true,
                showRowNumbers: true,
                showSelectedRollOverCanvas: false,
            
                // scrollRedrawDelay: 1,
                // scrollWheelRedrawDelay: 1,
                drawAheadRatio: 2.00,
            
              fields:[
                   {name: "itemID", hidden: true},
                   {name: "itemName"},
                   {name: "SKU", hidden: true},
                   {name: "category", hidden: true},
                   {name: "unitCost", hidden: true},
                   {name: "units", hidden: true},
                   {name: "inStock", hidden: true},
                   {name: "nextShipment", hidden: true},
                   {name: "description", hidden: true },
                   //{name: "buttonField", title: "Info", align: "center" },
                   {name: "iconField", title: "Comments/Stats", width: 110 }
                ],
            
                createRecordComponent : function (record, colNum) {  
                    var fieldName = this.getFieldName(colNum);  
            
                    if (fieldName == "iconField") {
                        isc.logWarn("createRecordComponent called for row " + dsListGrid.getRecordIndex(record));
                        var recordCanvas = isc.HLayout.create({
                            height: 22,
                            width: "100%",
                            align: "center"
                        });
            
                        var editImg = isc.ImgButton.create({
                            showDown: false,
                            showRollOver: false,
                            layoutAlign: "center",
                            src: "icons/16/comment_edit.png",
                            prompt: "Edit Comments",
                            height: 16,
                            width: 16,
                            grid: this,
                            click : function () {
                                isc.say("Edit Comment Icon Clicked for country : " + record["SKU"]);  
                            }
                        });
            
                        var chartImg = isc.ImgButton.create({
                            showDown: false,
                            showRollOver: false,
                            layoutAlign: "center",
                            src: "icons/16/chart_bar.png",
                            prompt: "View Chart",
                            height: 16,
                            width: 16,
                            grid: this,
                            click : function () {
                                isc.say("Chart Icon Clicked for country : " + record["SKU"]);  
                            }
                        });
            
                        recordCanvas.addMember(editImg);  
                        recordCanvas.addMember(chartImg);  
                        return recordCanvas;  
                    } else {  
                        return null;  
                    }  
                },
            
                updateRecordComponent: function (record, colNum, component, recordChanged) {
                    var fieldName = this.getFieldName(colNum);  
                    if (fieldName == "iconField") {
                        isc.logWarn("updateRecordComponent called for row " + dsListGrid.getRecordIndex(record));
                        var membersArray = component.getMembers();
                        for (i=0;i<membersArray.size;i++) {
                            if (i == 0) {
                                membersArray[i].addProperties({
                                    click : function () {
                                        isc.say("Edit Comment Icon Clicked for country : " + record["SKU"]);
                                    }
                                });        
                            } else {
                                membersArray[i].addProperties({
                                    click : function () {
                                        isc.say("Chart Icon Clicked for country : " + record["SKU"]);
                                    }
                                });
                            }
                        }    
                    } else {  
                        return null;  
                    }  
                    return component;
                }
            
            });

            Additionally it seems that drawAhead is only applied at start. I'll create a more easy testcase for this.

            Best regards
            Blama

            Comment


            • #7
              drawAhead goes in the direction of the most recent scrolling, and yes this is by design.

              Comment

              Working...
              X