11import {
2+ ApplicationRef ,
3+ DestroyRef ,
24 afterRenderEffect ,
35 computed ,
6+ inject ,
47 linkedSignal ,
58 untracked ,
69} from '@angular/core'
@@ -21,25 +24,62 @@ import type { AngularVirtualizer } from './types'
2124export * from '@tanstack/virtual-core'
2225export * from './types'
2326
24- function createVirtualizerBase <
27+ export type AngularVirtualizerOptions <
28+ TScrollElement extends Element | Window ,
29+ TItemElement extends Element ,
30+ > = VirtualizerOptions < TScrollElement , TItemElement > & {
31+ /**
32+ * Whether to flush the DOM using `ApplicationRef.tick()`
33+ * @default true
34+ * */
35+ useApplicationRefTick ?: boolean
36+ }
37+
38+ // Flush CD after virtual-core updates so template bindings hit the DOM
39+ // before the next frame's scroll reconciliation reads `scrollHeight`.
40+ function injectScheduleDomFlushViaAppRefTick ( ) {
41+ const appRef = inject ( ApplicationRef )
42+ const destroyRef = inject ( DestroyRef )
43+ let hostDestroyed = false
44+ destroyRef . onDestroy ( ( ) => {
45+ hostDestroyed = true
46+ } )
47+ let domFlushQueued = false
48+
49+ return ( ) => {
50+ if ( domFlushQueued ) return
51+ domFlushQueued = true
52+ queueMicrotask ( ( ) => {
53+ domFlushQueued = false
54+ if ( hostDestroyed ) return
55+ appRef . tick ( )
56+ } )
57+ }
58+ }
59+
60+ function injectVirtualizerBase <
2561 TScrollElement extends Element | Window ,
2662 TItemElement extends Element ,
2763> (
28- options : ( ) => VirtualizerOptions < TScrollElement , TItemElement > ,
64+ options : ( ) => AngularVirtualizerOptions < TScrollElement , TItemElement > ,
2965) {
66+ const scheduleDomFlush = injectScheduleDomFlushViaAppRefTick ( )
67+
3068 const resolvedOptions = computed < VirtualizerOptions < TScrollElement , TItemElement > > ( ( ) => {
31- const _options = options ( )
69+ const { useApplicationRefTick = true , ... _options } = options ( )
3270 return {
3371 ..._options ,
3472 onChange : ( instance , sync ) => {
35- // Update the main signal to trigger a re-render
3673 reactiveVirtualizer . set ( instance )
74+ if ( useApplicationRefTick ) {
75+ scheduleDomFlush ( )
76+ }
3777 _options . onChange ?.( instance , sync )
3878 } ,
3979 }
4080 } )
4181
42- const lazyVirtualizer = computed ( ( ) => new Virtualizer ( untracked ( options ) ) )
82+ const lazyVirtualizer = computed ( ( ) => new Virtualizer ( untracked ( resolvedOptions ) ) )
4383
4484 const reactiveVirtualizer = linkedSignal ( ( ) => {
4585 const virtualizer = lazyVirtualizer ( )
@@ -104,13 +144,13 @@ export function injectVirtualizer<
104144 TItemElement extends Element ,
105145> (
106146 options : ( ) => PartialKeys <
107- Omit < VirtualizerOptions < TScrollElement , TItemElement > , 'getScrollElement' > ,
147+ Omit < AngularVirtualizerOptions < TScrollElement , TItemElement > , 'getScrollElement' > ,
108148 'observeElementRect' | 'observeElementOffset' | 'scrollToFn'
109149 > & {
110150 scrollElement : ElementRef < TScrollElement > | TScrollElement | undefined
111151 } ,
112152) : AngularVirtualizer < TScrollElement , TItemElement > {
113- return createVirtualizerBase < TScrollElement , TItemElement > ( ( ) => {
153+ return injectVirtualizerBase < TScrollElement , TItemElement > ( ( ) => {
114154 const _options = options ( )
115155 return {
116156 observeElementRect : observeElementRect ,
@@ -137,14 +177,14 @@ function isElementRef<T extends Element>(
137177
138178export function injectWindowVirtualizer < TItemElement extends Element > (
139179 options : ( ) => PartialKeys <
140- VirtualizerOptions < Window , TItemElement > ,
180+ AngularVirtualizerOptions < Window , TItemElement > ,
141181 | 'getScrollElement'
142182 | 'observeElementRect'
143183 | 'observeElementOffset'
144184 | 'scrollToFn'
145185 > ,
146186) : AngularVirtualizer < Window , TItemElement > {
147- return createVirtualizerBase < Window , TItemElement > ( ( ) => ( {
187+ return injectVirtualizerBase < Window , TItemElement > ( ( ) => ( {
148188 getScrollElement : ( ) => ( typeof document !== 'undefined' ? window : null ) ,
149189 observeElementRect : observeWindowRect ,
150190 observeElementOffset : observeWindowOffset ,
0 commit comments