@@ -18,11 +18,12 @@ import {
1818 NgZone ,
1919 OnDestroy ,
2020 OnInit ,
21+ Output ,
2122 ViewChild ,
2223 ViewEncapsulation ,
2324} from '@angular/core' ;
2425import { animationFrameScheduler , fromEvent , Observable , Subject } from 'rxjs' ;
25- import { sampleTime , takeUntil } from 'rxjs/operators' ;
26+ import { sample , sampleTime , takeUntil } from 'rxjs/operators' ;
2627import { CdkVirtualForOf } from './virtual-for-of' ;
2728import { VIRTUAL_SCROLL_STRATEGY , VirtualScrollStrategy } from './virtual-scroll-strategy' ;
2829
@@ -54,9 +55,22 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
5455 /** Emits when the rendered range changes. */
5556 private _renderedRangeSubject = new Subject < ListRange > ( ) ;
5657
58+ /** Emits when a change detection cycle completes. */
59+ private _changeDetectionComplete = new Subject < void > ( ) ;
60+
5761 /** The direction the viewport scrolls. */
5862 @Input ( ) orientation : 'horizontal' | 'vertical' = 'vertical' ;
5963
64+ // Note: we don't use the typical EventEmitter here because we need to subscribe to the scroll
65+ // strategy lazily (i.e. only if the user is actually listening to the events). We do this because
66+ // depending on how the strategy calculates the scrolled index, it may come at a cost to
67+ // performance.
68+ /** Emits when the index of the first element visible in the viewport changes. */
69+ @Output ( ) scrolledIndexChange : Observable < number > =
70+ Observable . create ( observer => this . _scrollStrategy . scrolledIndexChange
71+ . pipe ( sample ( this . _changeDetectionComplete ) )
72+ . subscribe ( observer ) ) ;
73+
6074 /** The element that wraps the rendered content. */
6175 @ViewChild ( 'contentWrapper' ) _contentWrapper : ElementRef < HTMLElement > ;
6276
@@ -139,6 +153,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
139153 // Complete all subjects
140154 this . _renderedRangeSubject . complete ( ) ;
141155 this . _detachedSubject . complete ( ) ;
156+ this . _changeDetectionComplete . complete ( ) ;
142157 this . _destroyed . complete ( ) ;
143158 }
144159
@@ -363,5 +378,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
363378 for ( const fn of runAfterChangeDetection ) {
364379 fn ( ) ;
365380 }
381+
382+ this . _ngZone . run ( ( ) => this . _changeDetectionComplete . next ( ) ) ;
366383 }
367384}
0 commit comments