lVlyke / lithium-ngx-virtual-scroll

A fast and lightweight virtual scrolling solution for Angular that supports single column lists, grid lists and view caching.
MIT License
24 stars 4 forks source link

Only renders 1 item when itemHeight not explicitly set and complex components in use #22

Closed ghenry22 closed 2 months ago

ghenry22 commented 1 year ago

If [itemHeight] is not set and the item is a basic DIV then the list renders as expected.

If [itemHeight] is not set and the item is a complex component then the list renders only a single item (that 1 item renders perfectly fine)

If [itemHeight] is set then both scenarios work.

Proposed Solutions 1) Ideally auto determine height of items even when complex components are in use. 2) Update the docs to explain this behaviour and make the requirement to set [itemHeight] clear.

lVlyke commented 1 year ago

Do you have a reproducible example of this behavior? The library renders the first element in the list in order to calculate the item width and height, so no matter how complex the component is it should be able to calculate its size.

However, if your component is waiting for a network request or other async event to finish before displaying its content, then the calculation will be incorrect since the virtual scroll component will record the size of the item as soon as the item is rendered. If this is your use-case, I can look into adding a itemCalcDelay property that would take in an Observable to wait for before beginning the calculation.

ghenry22 commented 1 year ago

It’s just an ionic ion-item with a thumbnail image and a few lines of text. No http requests or anything going on.

I found that a simple div would work but the ionic component would only render the first item.

when I explicitly set the itemHeight then everything renders as expected.

BernhardBehrendt commented 10 months ago

I have an issue wich is maybe related.

<li-virtual-scroll [items]="pagesToShow"
                   [itemHeight]="365"
                   [itemWidth]="244". <----    This causes that there's just one element rendered
                   [viewCache]="100"

>
  <rw-page-image
    *liVirtualItem="let page"...

When i don't set the itemWidth then all items are rendered.

Bildschirmfoto 2023-11-07 um 23 57 46

Using another config

 <li-virtual-scroll [items]="pagesToShow"
                           [itemHeight]="436"
                           [viewCache]="100"
                           [bufferLength]="30"
                           [eventCapture]="true"
                           [gridList]="true"
                           [asyncRendering]="true"

        >
 <rw-page-image
            *liVirtualItem="let page"

Renders into a representation of the list super fast (amazing) but it does not append further items when i scroll to the end

ecancil commented 2 months ago

Same issue happening for me when using the material grid list. Only the first one is being rendered

<mat-grid-list cols="4" rowHeight="1:1" gutterSize="15px">
                    <li-virtual-scroll [items]="selectedPack.stickers || []" [gridList]="true">
                        <mat-grid-tile (click)="selectSticker(sticker)" *liVirtualItem="let sticker">

                            @if(!images[sticker.name]){<mat-icon>pending</mat-icon>}
                            @if(images[sticker.name] == ImageLoadStatus.ERROR){<mat-icon>error</mat-icon>}
                            @if(images[sticker.name] != ImageLoadStatus.ERROR){
                                <img #img 
                                    [style.display]="images[sticker.name] ==  ImageLoadStatus.LOADED ? 'block' : 'none'"
                                    (load)="images[sticker.name] =  ImageLoadStatus.LOADED; img.style.opacity = '1'"  
                                    (error)="images[sticker.name] =  ImageLoadStatus.ERROR"  
                                    class="grid-sticker pointer fade-img" 
                                    [src]="uriFromSticker(sticker)"/>
                            }
                        </mat-grid-tile>
                    </li-virtual-scroll>
                  </mat-grid-list>   
ghenry22 commented 2 months ago

Same issue happening for me when using the material grid list. Only the first one is being rendered

<mat-grid-list cols="4" rowHeight="1:1" gutterSize="15px">
                    <li-virtual-scroll [items]="selectedPack.stickers || []" [gridList]="true">
                        <mat-grid-tile (click)="selectSticker(sticker)" *liVirtualItem="let sticker">

                            @if(!images[sticker.name]){<mat-icon>pending</mat-icon>}
                            @if(images[sticker.name] == ImageLoadStatus.ERROR){<mat-icon>error</mat-icon>}
                            @if(images[sticker.name] != ImageLoadStatus.ERROR){
                                <img #img 
                                    [style.display]="images[sticker.name] ==  ImageLoadStatus.LOADED ? 'block' : 'none'"
                                    (load)="images[sticker.name] =  ImageLoadStatus.LOADED; img.style.opacity = '1'"  
                                    (error)="images[sticker.name] =  ImageLoadStatus.ERROR"  
                                    class="grid-sticker pointer fade-img" 
                                    [src]="uriFromSticker(sticker)"/>
                            }
                        </mat-grid-tile>
                    </li-virtual-scroll>
                  </mat-grid-list>   

For grid, your li-virtual-scroll parent item needs to have an explicit height AND width set (ie height: 100%, width 100%)

Your items that will be rendered in the grid ALSO need to have a width and height set.

There is also a bit of CSS needed, check out the demo repo and have a look at the CSS there for the scroll-container item which has sub items for list-items with different CSS when grid is true or false on the li-virtual-scroll element.

You can see all this in the demo, but they are not automatically applied it seems when just using the library. Could probably be incorporated into the README.MD to make it clearer.

I just spent an afternoon working through these scenarios again to get list/grid virtualscrolls working.

Looks really good now that I have all the bits together and both list and grid render and scroll nicely.

Without this I found that the grid just rendered a list.

ghenry22 commented 2 months ago

I have an issue wich is maybe related.

<li-virtual-scroll [items]="pagesToShow"
                   [itemHeight]="365"
                   [itemWidth]="244". <----    This causes that there's just one element rendered
                   [viewCache]="100"

>
  <rw-page-image
    *liVirtualItem="let page"...

When i don't set the itemWidth then all items are rendered.

Bildschirmfoto 2023-11-07 um 23 57 46

Using another config

 <li-virtual-scroll [items]="pagesToShow"
                           [itemHeight]="436"
                           [viewCache]="100"
                           [bufferLength]="30"
                           [eventCapture]="true"
                           [gridList]="true"
                           [asyncRendering]="true"

        >
 <rw-page-image
            *liVirtualItem="let page"

Renders into a representation of the list super fast (amazing) but it does not append further items when i scroll to the end

When you see all the items in the DOM like that you are not actually getting virtual scrolling. Have a read of my previous comments. You need the parent element to have an explicitly set height and for grid mode also width. Otherwise the virtual list assumes that the viewport size is infinite and just renders everything resulting in no virtual scroll.

When it's working you can open the dev tools and you will see a shorter list of virtual items and you will see them flash up as they are recycled through the view while you scroll.

Check the demo repo as it has some good examples of some of the extra bits of CSS that are needed to make everything play nice.

ghenry22 commented 2 months ago

I'm going to close this because if you read through the comments you can see the cause and the fix.

The parent element must have a defined width and height (ie height: 100vh; width: 100vw; to use the entire available space).

When gridEnabled = false, you MUST specify the item height and it must be close to the actual height.

When gridEnabled = true, you MUST specify the item height and the item width and it must be close to the actual height and width.

When these set you will see both proper virtual scrolling when you check the DOM (ie elements moved in and out when you scroll) and all items displaying as expected.

There are some other bits of CSS that are useful for getting the desire behaviour and you can check these out in the demo repo and largely copy and paste then edit down to just what you need.

lVlyke commented 1 month ago

@ghenry22 Thanks for troubleshooting this. I'll update the README to make these limitations clear.

Edit: The README has been updated to clarify item sizing requirements.