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
angular grid lithium-angular ngx rxjs scroll typescript virtual virtual-scroll

@lithiumjs/ngx-virtual-scroll

A fast and lightweight virtual scrolling solution for Angular that supports single column lists, grid lists and view caching.

Live demo (Code)

Installing

@lithiumjs/ngx-virtual-scroll requires @lithiumjs/angular. Both can be installed from npm:

npm install @lithiumjs/angular @lithiumjs/ngx-virtual-scroll

Usage guide

Import NgxVirtualScrollModule. Use the li-virtual-scroll component to declare a list with virtual scrolling:

<li-virtual-scroll [items]="items">
    <!-- liVirtualItem defines a list item element for each item -->
    <div *liVirtualItem="let item">
        {{ item.name }}
    </div>
</li-virtual-scroll>

Note: The li-virtual-scroll element is a scroll container and needs to have a defined maximum size to ensure scrolling occurs.

Item element size

The size of the list item elements can either be explicitly passed in, or calculated automatically. Width is only required for grid lists.

[!IMPORTANT] All item elements in the list must be the same size. For best results, make sure that the elements have a constant size that does not change. If the element size is not constant and changes based on the number of elements in the container (i.e. flexbox), then the item size must be declared explicitly.

Automatic item size

If itemWidth or itemHeight are not explicitly passed, they will be automatically calculated by rendering the first item in the list and recording its size. This method only works for list items that have a constant size.

Explicit item size

Use the itemWidth and itemHeight inputs to explicitly declare the width/height (in pixels) of the list item elements:

<li-virtual-scroll [items]="items" [itemWidth]="128" [itemHeight]="128">
    <div *liVirtualItem="let item">
        {{ item.name }}
    </div>
</li-virtual-scroll>

[!NOTE] Include the element's margins as part of the width/height.

Grid lists (multiple items per row)

If the list being rendered is a grid list with multiple items per row, make sure to set gridList to true:

<li-virtual-scroll [items]="items" [gridList]="true">
    <div *liVirtualItem="let item">
        {{ item.name }}
    </div>
</li-virtual-scroll>

Asynchronous view rendering

Asynchronous view rendering can be used to improve scrolling responsiveness for items with complex views. When enabled, a placeholder element will be temporarily be shown while the item is rendered asynchronously. The placeholder element can be customized using the liVirtualPlaceholder directive:

<li-virtual-scroll [items]="items" [asyncRendering]="true">
     <!-- Items contain complex components: -->
    <div *liVirtualItem="let item">
        {{ item.name }}

        <app-complex-component>...</app-complex-component>
    </div>

    <!-- Placeholder only prints the item name: -->
    <div *liVirtualPlaceholder="let item">
        {{ item.name }}
    </div>
</li-virtual-scroll>

Note that the placeholder should be lightweight so that it can be rendered quickly.

View caching

View caching can be enabled to improve scrolling performance by caching views that are not being rendered for later re-use. View caching can be toggled on or off, or enabled with a maximum cache size (recommended for very large lists or complex views, as these could cause high memory usage):

<!-- Up to 256 views will be cached -->
<li-virtual-scroll [items]="items" [viewCache]="256">
    <div *liVirtualItem="let item">
        {{ item.name }}
    </div>
</li-virtual-scroll>

Scroll debounce

The scroll debounce controls how often the virtual scroll should respond to scroll position changes. The default is 50 milliseconds.

<!-- Virtual scroll will render on scroll changes every 100ms -->
<li-virtual-scroll [items]="items" [scrollDebounceMs]="100">
    <div *liVirtualItem="let item">
        {{ item.name }}
    </div>
</li-virtual-scroll>

Scroll buffer

Increasing the scroll buffer length will render more items outside of the viewport, which can reduce view pop-in when scrolling quickly. The buffer length is expressed in multiples of the list container's client height:

<li-virtual-scroll [items]="items" [bufferLength]="3">
    <div *liVirtualItem="let item">
        {{ item.name }}
    </div>
</li-virtual-scroll>

Providing a custom virtual scroll strategy

The default virtual scroll strategy can be overriden by providing a custom VirtualScrollStrategy service implementation using the LI_VIRTUAL_SCROLL_STRATEGY injection token:

@NgModule({
    providers: [
        {
            provide: LI_VIRTUAL_SCROLL_STRATEGY,
            useClass: AppFooVirtualScrollStrategy
        }
    ]
})
export class AppFooModule {}

The default virtual scroll strategy can also be overriden using NgxVirtualScrollModule.withOptions:

@NgModule({
    imports: [
        NgxVirtualScrollModule.withOptions({
            scrollStrategy: AppFooVirtualScrollStrategy
        })
    ]
})
export class AppModule {}

Troubleshooting

All items are rendered

This can happen when your list element has an unlimited height. The li-virtual-scroll component is a scroll container that needs a maximum height for virtual scrolling to work. You must apply a maximum height to the list element for virtual scrolling to work.

Only one item is rendered

This usually happens when your list item elements do not have a constant size and you have not explicitly declared itemWidth/itemHeight.

If your list container uses a layout that dynamically resizes elements (i.e. flexbox), you must ensure that the items have a constant size for the automatic width/height calculation to work correctly. If this is not possible, you must instead explicitly declare the item size using the itemWidth/itemHeight inputs. See this section for more information.

Not enough items are rendered

This can happen when incorrect itemWidth/itemHeight values are given. Make sure that you have calculated the size of your elements correctly (this should also include margins). See this section for more information.

API reference

VirtualScroll (li-virtual-scroll)

Component used to create a virtual scrolling container.

Inputs

VirtualItem ([liVirtualItem])

Structural directive used to render items inside a li-virtual-scroll component.

VirtualPlaceholder ([liVirtualPlaceholder])

Structural directive used to render placeholder elements inside a li-virtual-scroll component.

NgxVirtualScrollModule

The Angular module for this library.

NgxVirtualScrollModule.Options

The options to configure the module with.

export interface Options {
    scrollStrategy?: Type<VirtualScrollStrategy<unknown>>;
}

NgxVirtualScrollModule.withOptions

Allows configuration of the module with custom options.

export class NgxVirtualScrollModule {

    public static withOptions(
        options: NgxVirtualScrollModule.Options
    ): ModuleWithProviders<NgxVirtualScrollModule>;
}

VirtualScrollStrategy

Interface for defining a custom virtual scroll strategy.

export interface VirtualScrollStrategy<T> {

    destroyViewRef(
        scrollState: VirtualScrollState<T>,
        viewRef: EmbeddedViewRef<VirtualItem.ViewContext<T>>
    ): void;

    destroyView(
        scrollState: VirtualScrollState<T>,
        view: VirtualScrollState.ViewInfo<T>
    ): void;

    cacheView(
        scrollState: VirtualScrollState<T>,
        view: VirtualScrollState.ViewInfo<T>
    ): void;

    renderViewForItem(
        scrollState: VirtualScrollState<T>,
        item: T,
        globalIndex: number,
        deferViewCreation?: boolean
    ): Observable<VirtualScrollState.ViewInfo<T>>;

    unrenderView(
        scrollState: VirtualScrollState<T>,
        view: VirtualScrollState.ViewInfo<T>
    ): void;

    purgeViewCache(scrollState: VirtualScrollState<T>): void;
}

DefaultVirtualScrollStrategy

The default VirtualScrollStrategy service implementation.

Other information