Skip to content

Commit d101293

Browse files
committed
feat(angular): add injector option like other angular build in apis
1 parent 8362f03 commit d101293

File tree

1 file changed

+88
-68
lines changed

1 file changed

+88
-68
lines changed

packages/angular-virtual/src/index.ts

Lines changed: 88 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import {
22
ApplicationRef,
33
DestroyRef,
4+
Injector,
45
afterRenderEffect,
6+
assertInInjectionContext,
57
computed,
68
inject,
79
linkedSignal,
10+
runInInjectionContext,
811
untracked,
912
} from '@angular/core'
1013
import {
@@ -35,6 +38,14 @@ export type AngularVirtualizerOptions<
3538
useApplicationRefTick?: boolean
3639
}
3740

41+
export type AngularExtensionOptions = {
42+
/**
43+
* The injector to use for the virtualizer.
44+
* @default inject(Injector)
45+
*/
46+
injector?: Injector
47+
}
48+
3849
// Flush CD after virtual-core updates so template bindings hit the DOM
3950
// before the next frame's scroll reconciliation reads `scrollHeight`.
4051
function injectScheduleDomFlushViaAppRefTick() {
@@ -62,81 +73,90 @@ function injectVirtualizerBase<
6273
TItemElement extends Element,
6374
>(
6475
options: () => AngularVirtualizerOptions<TScrollElement, TItemElement>,
76+
extensions: AngularExtensionOptions = {},
6577
) {
66-
const scheduleDomFlush = injectScheduleDomFlushViaAppRefTick()
78+
let injector = extensions.injector;
79+
if (!injector) {
80+
assertInInjectionContext(injectVirtualizerBase)
81+
injector = inject(Injector)
82+
}
6783

68-
const resolvedOptions = computed<VirtualizerOptions<TScrollElement, TItemElement>>(() => {
69-
const { useApplicationRefTick = true, ..._options } = options()
70-
return {
71-
..._options,
72-
onChange: (instance, sync) => {
73-
reactiveVirtualizer.set(instance)
74-
if (useApplicationRefTick) {
75-
scheduleDomFlush()
76-
}
77-
_options.onChange?.(instance, sync)
78-
},
79-
}
80-
})
84+
return runInInjectionContext(injector, () => {
85+
const scheduleDomFlush = injectScheduleDomFlushViaAppRefTick()
8186

82-
const lazyVirtualizer = computed(() => new Virtualizer(untracked(resolvedOptions)))
87+
const resolvedOptions = computed<VirtualizerOptions<TScrollElement, TItemElement>>(() => {
88+
const { useApplicationRefTick = true, ..._options } = options()
89+
return {
90+
..._options,
91+
onChange: (instance, sync) => {
92+
reactiveVirtualizer.set(instance)
93+
if (useApplicationRefTick) {
94+
scheduleDomFlush()
95+
}
96+
_options.onChange?.(instance, sync)
97+
},
98+
}
99+
})
83100

84-
const reactiveVirtualizer = linkedSignal(() => {
85-
const virtualizer = lazyVirtualizer()
86-
// If setOptions does not call onChange, it's safe to call it here
87-
virtualizer.setOptions(resolvedOptions())
88-
return virtualizer
89-
}, { equal: () => false })
101+
const lazyVirtualizer = computed(() => new Virtualizer(untracked(resolvedOptions)))
90102

91-
afterRenderEffect((cleanup) => {
92-
cleanup(lazyVirtualizer()._didMount())
93-
})
103+
const reactiveVirtualizer = linkedSignal(() => {
104+
const virtualizer = lazyVirtualizer()
105+
// If setOptions does not call onChange, it's safe to call it here
106+
virtualizer.setOptions(resolvedOptions())
107+
return virtualizer
108+
}, { equal: () => false })
94109

95-
afterRenderEffect(() => {
96-
reactiveVirtualizer()._willUpdate()
97-
})
110+
afterRenderEffect((cleanup) => {
111+
cleanup(lazyVirtualizer()._didMount())
112+
})
113+
114+
afterRenderEffect(() => {
115+
reactiveVirtualizer()._willUpdate()
116+
})
98117

99-
return signalProxy(
100-
reactiveVirtualizer,
101-
// Methods that pass through: call on the instance without tracking the signal read
102-
[
103-
'_didMount',
104-
'_willUpdate',
105-
'calculateRange',
106-
'getVirtualIndexes',
107-
'measure',
108-
'measureElement',
109-
'resizeItem',
110-
'scrollBy',
111-
'scrollToIndex',
112-
'scrollToOffset',
113-
'setOptions',
114-
],
115-
// Attributes that will be transformed to signals
116-
[
117-
'isScrolling',
118-
'measurementsCache',
119-
'options',
120-
'range',
121-
'scrollDirection',
122-
'scrollElement',
123-
'scrollOffset',
124-
'scrollRect',
125-
],
126-
// Methods that will be tracked to the virtualizer signal
127-
[
128-
'getOffsetForAlignment',
129-
'getOffsetForIndex',
130-
'getVirtualItemForOffset',
131-
'indexFromElement',
132-
],
133-
// Zero-arg methods exposed as computed signals
134-
[
135-
'getTotalSize',
136-
'getVirtualItems'
137-
],
138-
// The rest is passed as is, and can be accessed or called before initialization
139-
) as unknown as AngularVirtualizer<TScrollElement, TItemElement>
118+
return signalProxy(
119+
reactiveVirtualizer,
120+
// Methods that pass through: call on the instance without tracking the signal read
121+
[
122+
'_didMount',
123+
'_willUpdate',
124+
'calculateRange',
125+
'getVirtualIndexes',
126+
'measure',
127+
'measureElement',
128+
'resizeItem',
129+
'scrollBy',
130+
'scrollToIndex',
131+
'scrollToOffset',
132+
'setOptions',
133+
],
134+
// Attributes that will be transformed to signals
135+
[
136+
'isScrolling',
137+
'measurementsCache',
138+
'options',
139+
'range',
140+
'scrollDirection',
141+
'scrollElement',
142+
'scrollOffset',
143+
'scrollRect',
144+
],
145+
// Methods that will be tracked to the virtualizer signal
146+
[
147+
'getOffsetForAlignment',
148+
'getOffsetForIndex',
149+
'getVirtualItemForOffset',
150+
'indexFromElement',
151+
],
152+
// Zero-arg methods exposed as computed signals
153+
[
154+
'getTotalSize',
155+
'getVirtualItems'
156+
],
157+
// The rest is passed as is, and can be accessed or called before initialization
158+
) as unknown as AngularVirtualizer<TScrollElement, TItemElement>
159+
})
140160
}
141161

142162
export function injectVirtualizer<

0 commit comments

Comments
 (0)