@@ -301,7 +301,12 @@ export class CdkTable<T>
301301 protected _viewRepeater : _ViewRepeater < T , RenderRow < T > , RowContext < T > > ;
302302 private readonly _viewportRuler = inject ( ViewportRuler ) ;
303303 private _injector = inject ( Injector ) ;
304- private _virtualScrollViewport = inject ( CDK_VIRTUAL_SCROLL_VIEWPORT , { optional : true } ) ;
304+ private _virtualScrollViewport = inject ( CDK_VIRTUAL_SCROLL_VIEWPORT , {
305+ optional : true ,
306+ // Virtual scrolling can only be enabled by a viewport in
307+ // the same host, don't try to resolve in parent components.
308+ host : true ,
309+ } ) ;
305310 private _positionListener =
306311 inject ( STICKY_POSITIONING_LISTENER , { optional : true } ) ||
307312 inject ( STICKY_POSITIONING_LISTENER , { optional : true , skipSelf : true } ) ;
@@ -467,6 +472,14 @@ export class CdkTable<T>
467472 /** Emits when the footer rows sticky state changes. */
468473 private readonly _footerRowStickyUpdates = new Subject < StickyUpdate > ( ) ;
469474
475+ /**
476+ * Whether to explicitly disable virtual scrolling even if there is a virtual scroll viewport
477+ * parent. This can't be changed externally, whereas internally it is turned into an input that
478+ * we use to opt out existing apps that were implementing virtual scroll before we added support
479+ * for it.
480+ */
481+ private readonly _disableVirtualScrolling = false ;
482+
470483 /** Aria role to apply to the table's cells based on the table's own role. */
471484 _getCellRole ( ) : string | null {
472485 // Perform this lazily in case the table's role was updated by a directive after construction.
@@ -565,7 +578,7 @@ export class CdkTable<T>
565578 get fixedLayout ( ) : boolean {
566579 // Require a fixed layout when virtual scrolling is enabled, otherwise
567580 // the element the header can jump around as the user is scrolling.
568- return this . _virtualScrollViewport ? true : this . _fixedLayout ;
581+ return this . _virtualScrollEnabled ( ) ? true : this . _fixedLayout ;
569582 }
570583 set fixedLayout ( value : boolean ) {
571584 this . _fixedLayout = value ;
@@ -595,7 +608,10 @@ export class CdkTable<T>
595608 *
596609 * @docs -private
597610 */
598- readonly viewChange : BehaviorSubject < ListRange > ;
611+ readonly viewChange : BehaviorSubject < ListRange > = new BehaviorSubject ( {
612+ start : 0 ,
613+ end : Number . MAX_VALUE ,
614+ } ) ;
599615
600616 // Outlets in the table's template where the header, data rows, and footer will be inserted.
601617 _rowOutlet : DataRowOutlet ;
@@ -638,21 +654,13 @@ export class CdkTable<T>
638654
639655 this . _isServer = ! this . _platform . isBrowser ;
640656 this . _isNativeHtmlTable = this . _elementRef . nativeElement . nodeName === 'TABLE' ;
641- this . viewChange = new BehaviorSubject < ListRange > ( {
642- start : 0 ,
643- end : this . _virtualScrollViewport ? 0 : Number . MAX_VALUE ,
644- } ) ;
645657
646658 // Set up the trackBy function so that it uses the `RenderRow` as its identity by default. If
647659 // the user has provided a custom trackBy, return the result of that function as evaluated
648660 // with the values of the `RenderRow`'s data and index.
649661 this . _dataDiffer = this . _differs . find ( [ ] ) . create ( ( _i : number , dataRow : RenderRow < T > ) => {
650662 return this . trackBy ? this . trackBy ( dataRow . dataIndex , dataRow . data ) : dataRow ;
651663 } ) ;
652-
653- if ( this . _virtualScrollViewport ) {
654- this . _setupVirtualScrolling ( this . _virtualScrollViewport ) ;
655- }
656664 }
657665
658666 ngOnInit ( ) {
@@ -668,9 +676,14 @@ export class CdkTable<T>
668676
669677 ngAfterContentInit ( ) {
670678 this . _viewRepeater =
671- this . recycleRows || this . _virtualScrollViewport
679+ this . recycleRows || this . _virtualScrollEnabled ( )
672680 ? new _RecycleViewRepeaterStrategy ( )
673681 : new _DisposeViewRepeaterStrategy ( ) ;
682+
683+ if ( this . _virtualScrollEnabled ( ) ) {
684+ this . _setupVirtualScrolling ( this . _virtualScrollViewport ! ) ;
685+ }
686+
674687 this . _hasInitialized = true ;
675688 }
676689
@@ -1039,24 +1052,23 @@ export class CdkTable<T>
10391052 * so that the differ equates their references.
10401053 */
10411054 private _getAllRenderRows ( ) : RenderRow < T > [ ] {
1042- const dataWithinRange = this . _renderedRange
1043- ? ( this . _data || [ ] ) . slice ( this . _renderedRange . start , this . _renderedRange . end )
1044- : [ ] ;
1055+ // Note: the `_data` is typed as an array, but some internal apps end up passing diffrent types.
1056+ if ( ! Array . isArray ( this . _data ) || ! this . _renderedRange ) {
1057+ return [ ] ;
1058+ }
1059+
10451060 const renderRows : RenderRow < T > [ ] = [ ] ;
1061+ const end = Math . min ( this . _data . length , this . _renderedRange . end ) ;
10461062
10471063 // Store the cache and create a new one. Any re-used RenderRow objects will be moved into the
10481064 // new cache while unused ones can be picked up by garbage collection.
10491065 const prevCachedRenderRows = this . _cachedRenderRowsMap ;
10501066 this . _cachedRenderRowsMap = new Map ( ) ;
10511067
1052- if ( ! this . _data ) {
1053- return renderRows ;
1054- }
1055-
10561068 // For each data object, get the list of rows that should be rendered, represented by the
10571069 // respective `RenderRow` object which is the pair of `data` and `CdkRowDef`.
1058- for ( let i = 0 ; i < dataWithinRange . length ; i ++ ) {
1059- let data = dataWithinRange [ i ] ;
1070+ for ( let i = this . _renderedRange . start ; i < end ; i ++ ) {
1071+ const data = this . _data [ i ] ;
10601072 const renderRowsForData = this . _getRenderRowsForData ( data , i , prevCachedRenderRows . get ( data ) ) ;
10611073
10621074 if ( ! this . _cachedRenderRowsMap . has ( data ) ) {
@@ -1480,6 +1492,9 @@ export class CdkTable<T>
14801492 const virtualScrollScheduler =
14811493 typeof requestAnimationFrame !== 'undefined' ? animationFrameScheduler : asapScheduler ;
14821494
1495+ // Render nothing since the virtual scroll viewport will take over.
1496+ this . viewChange . next ( { start : 0 , end : 0 } ) ;
1497+
14831498 // Forward the rendered range computed by the virtual scroll viewport to the table.
14841499 viewport . renderedRangeStream
14851500 // We need the scheduler here, because the virtual scrolling module uses an identical
@@ -1629,6 +1644,10 @@ export class CdkTable<T>
16291644 const endRect = lastNode ?. getBoundingClientRect ?.( ) ;
16301645 return startRect && endRect ? endRect . bottom - startRect . top : 0 ;
16311646 }
1647+
1648+ private _virtualScrollEnabled ( ) : boolean {
1649+ return ! this . _disableVirtualScrolling && this . _virtualScrollViewport != null ;
1650+ }
16321651}
16331652
16341653/** Utility function that gets a merged list of the entries in an array and values of a Set. */
0 commit comments