-
-
Notifications
You must be signed in to change notification settings - Fork 187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(template): introduce experimental virtual-scrolling sub-package #1539
Conversation
☁️ Nx Cloud ReportCI is running/has finished running commands for commit fb5a511. As they complete they will appear below. Click to see the status, the terminal output, and the build insights. 📂 See all runs for this branch ✅ Successfully ran 6 targetsSent with 💌 from NxCloud. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! I left some initial thoughts.
standalone: true, | ||
}) | ||
// eslint-disable-next-line @angular-eslint/directive-class-suffix | ||
export class FixedSizeVirtualScrollStrategy< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like a lot of code is duplicated between strategies, would it make sense to factorize?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
autosize & dynamic size share a lot of code. would be nice to factorize, yes
this.viewport!.containerRect$.pipe( | ||
map(({ height }) => height), | ||
distinctUntilChanged() | ||
), | ||
onScroll$, | ||
this.runwayStateChanged$.pipe(startWith(void 0)), | ||
this.recalculateRange$.pipe(startWith(void 0)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to use void 0 instead of undefined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, i'm just used to it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, a subject that is typed as void
should emit void
. I guess emitting undefined
instead would result in an error when strict mode is turned on
…rolling (#1542) * test(template): setup cypress component tests * test(template): implement first test for virtual-scrolling * test(template): implement tests for DynamicSizeVirtualScrollStrategy * test(template): implement tests for AutosizeVirtualScrollStrategy * test(template): stabilize virtual scrolling component tests for CI * chore: integrate component-test into ci
Co-authored-by: Kirill <karnaukhov.kh@gmail.com>
Introducing
@rx-angular/template/experimental/virtual-scrolling
submodule 🥳 🔥 🔥 🔥Usage
Demo
Check out the Demo Application. You can play around with
all pre-packaged ScrollStrategies as well as control the majority of inputs.
layout-techniques.mp4
Performance Benchmark and CDK comparison
https://github.com/hoebbelsB/rxa-virtual-scroll#performance-benchmarks
Components & Directives
RxVirtualFor
The
*rxVirtualFor
structural directive implements theRxVirtualViewRepeater
and is responsible to create, update, move and remove viewsfrom the bound data.
As
RxFor
,RxVirtualFor
treats each child template as single renderable unit.By default the change detection of the child templates get prioritized, scheduled and executed by leveraging
RenderStrategies
under the hood.This technique enables non-blocking rendering of lists and can be referred to as concurrent mode.
Read more about the concurrent mode in the concurrent strategies section in the
RxAngular
docs.Inputs
trackBy
keyof T
or(index: number, item: T) => any
rxVirtualFor
provides a shorthand where you can name the property directly.patchZone
boolean
true
if set tofalse
, theRxVirtualForDirective
will operate out ofNgZone
. See NgZone optimizationsparent
boolean
false
if set tofalse
, theRxVirtualForDirective
won't inform its host component about changes being made to the template. More performant,@ViewChild
and@ContentChild
queries won't work. Handling view and content queriesstrategy
Observable<RxStrategyNames \ string> \ RxStrategyNames \ string>
normal
configure theRxStrategyRenderStrategy
used to detect changes. Render StrategiesrenderCallback
Subject<U>
RxVirtualForDirective
created, updated, removed its template. Useful for situations where you need to know when rendering is done.viewCacheSize
number
20
Controls the amount if views held in cache for later re-use when a user is scrolling the list If this is set to 0,rxVirtualFor
won't cache any view, thus destroying & re-creating very often on scroll events.RxVirtualScrollViewportComponent
Container component comparable to CdkVirtualScrollViewport acting as viewport for
*rxVirtualFor
to operate on.Its main purpose is to implement the
RxVirtualScrollViewport
interface as well as maintaining the scroll runways'height in order to give the provided
RxVirtualScrollStrategy
room to position items. Furthermore, it will gather and forwardall events to the consumer of
rxVirtualFor
.Outputs
viewRange
ListRange: { start: number; end: number; }
*rxVirtualFor
. This value is determined by the providedRxVirtualScrollStrategy
. It gives the user information about the range of items being actually rendered to the DOM. Note this value updates before therenderCallback
kicks in, thus it is only in sync with the DOM when the nextrenderCallback
emitted an event.scrolledIndexChange
number
RxVirtualScrollStrategy
The
RxVirtualScrollStrategy
is responsible for positioning the created views on the viewport.The three pre-packaged scroll strategies share similar concepts for layouting views.
All of them provide a twitter-like virtual-scrolling implementation, where views are positioned absolutely and transitioned by
using css
transforms
.They also share two inputs to define the amount of views to actually render on the screen.
runwayItems
number
10
The amount of items to render upfront in scroll directionrunwayItemsOpposite
number
2
The amount of items to render upfront in reverse scroll directionSee the layouting technique in action in the following video. It compares
@rx-angular/template
vs.@angular/cdk/scrolling
layout-techniques.mp4
FixedSizeVirtualScrollStrategy
The
FixedSizeVirtualScrollStrategy
positions views based on a fixed size per item. It is comparable to@angular/cdk/scrolling
FixedSizeVirtualScrollStrategy
,but with a high performant layouting technique.
Demo
The default size can be configured directly as
@Input('itemSize')
.Example
DynamicSizeVirtualScrollStrategy
The
DynamicSizeVirtualScrollStrategy
is very similar to theAutosizeVirtualScrollStrategy
. Instead of hitting the DOM, it calculates the sizebased on a user provided function of type
(item: T) => number
. Because it doesn't have to interact with the DOM in order to position views,the
DynamicSizeVirtualScrollStrategy
has a better runtime performance compared to theAutosizeVirtualScrollStrategy
.This strategy is very useful for scenarios where you display different kind of templates, but already know the dimensions of
them.
Demo
Example
AutosizeVirtualScrollStrategy
The
AutosizeVirtualScrollStrategy
is able to render and positionitems based on their individual size. It is comparable to
@angular/cdk/experimental
AutosizeVirtualScrollStrategy
, but witha high performant layout technique, better visual stability and added features.
Furthermore, the
AutosizeVirtualScrollStrategy
is leveraging theResizeObserver
in order to detect size changes for each individualview rendered to the DOM and properly re-position accordingly.
For views it doesn't know yet, the
AutosizeVirtualScrollStrategy
anticipates a certain size in order to properly size the runway.The size is determined by the
@Input('tombstoneSize')
and defaults to50
.In order to provide top runtime performance the
AutosizeVirtualScrollStrategy
builds up caches thatprevent DOM interactions whenever possible. Once a view was visited, its properties will be stored instead of re-read from the DOM
again as this can potentially lead to unwanted forced reflows.
Demo
Example
Configuration
RX_VIRTUAL_SCROLL_DEFAULT_OPTIONS
By providing a
RX_VIRTUAL_SCROLL_DEFAULT_OPTIONS
token, you can pre-configure default settings forthe directives of the
@rx-angular/template/experimental/virtual-scrolling
package.Default Values
RxVirtualScrollDefaultOptions
Missing Features (Roadmap)
The following section describes features that are currently not implemented, but planned.
Support other orientations
Right now, the
@rx-angular/template/experimental/virtual-scrolling
package only supports vertical scrolling. In the future, it should alsobe able to support horizontal scrolling.
Tombstones
Tombstones, skeletons or placeholder templates are a nice way to improve the scrolling performance, especially when the actual views being rendered
are heavy and take a long time to create. Especially for the autosized strategy this can increase the visual stability and runtime performance a lot.
The concept is described in the article Complexities of an infinite scroller
and visible in the corresponding demo.