@@ -300,7 +300,12 @@ export class CdkTable<T>
300300 protected _viewRepeater : _ViewRepeater < T , RenderRow < T > , RowContext < T > > ;
301301 private readonly _viewportRuler = inject ( ViewportRuler ) ;
302302 private _injector = inject ( Injector ) ;
303- private _virtualScrollViewport = inject ( CDK_VIRTUAL_SCROLL_VIEWPORT , { optional : true } ) ;
303+ private _virtualScrollViewport = inject ( CDK_VIRTUAL_SCROLL_VIEWPORT , {
304+ optional : true ,
305+ // Virtual scrolling can only be enabled by a viewport in
306+ // the same host, don't try to resolve in parent components.
307+ host : true ,
308+ } ) ;
304309 private _positionListener =
305310 inject ( STICKY_POSITIONING_LISTENER , { optional : true } ) ||
306311 inject ( STICKY_POSITIONING_LISTENER , { optional : true , skipSelf : true } ) ;
@@ -466,6 +471,14 @@ export class CdkTable<T>
466471 /** Emits when the footer rows sticky state changes. */
467472 private readonly _footerRowStickyUpdates = new Subject < StickyUpdate > ( ) ;
468473
474+ /**
475+ * Whether to explicitly disable virtual scrolling even if there is a virtual scroll viewport
476+ * parent. This can't be changed externally, whereas internally it is turned into an input that
477+ * we use to opt out existing apps that were implementing virtual scroll before we added support
478+ * for it.
479+ */
480+ private readonly _disableVirtualScrolling = false ;
481+
469482 /** Aria role to apply to the table's cells based on the table's own role. */
470483 _getCellRole ( ) : string | null {
471484 // Perform this lazily in case the table's role was updated by a directive after construction.
@@ -564,7 +577,7 @@ export class CdkTable<T>
564577 get fixedLayout ( ) : boolean {
565578 // Require a fixed layout when virtual scrolling is enabled, otherwise
566579 // the element the header can jump around as the user is scrolling.
567- return this . _virtualScrollViewport ? true : this . _fixedLayout ;
580+ return this . _virtualScrollEnabled ( ) ? true : this . _fixedLayout ;
568581 }
569582 set fixedLayout ( value : boolean ) {
570583 this . _fixedLayout = value ;
@@ -594,7 +607,10 @@ export class CdkTable<T>
594607 *
595608 * @docs -private
596609 */
597- readonly viewChange : BehaviorSubject < ListRange > ;
610+ readonly viewChange : BehaviorSubject < ListRange > = new BehaviorSubject ( {
611+ start : 0 ,
612+ end : Number . MAX_VALUE ,
613+ } ) ;
598614
599615 // Outlets in the table's template where the header, data rows, and footer will be inserted.
600616 _rowOutlet : DataRowOutlet ;
@@ -637,21 +653,13 @@ export class CdkTable<T>
637653
638654 this . _isServer = ! this . _platform . isBrowser ;
639655 this . _isNativeHtmlTable = this . _elementRef . nativeElement . nodeName === 'TABLE' ;
640- this . viewChange = new BehaviorSubject < ListRange > ( {
641- start : 0 ,
642- end : this . _virtualScrollViewport ? 0 : Number . MAX_VALUE ,
643- } ) ;
644656
645657 // Set up the trackBy function so that it uses the `RenderRow` as its identity by default. If
646658 // the user has provided a custom trackBy, return the result of that function as evaluated
647659 // with the values of the `RenderRow`'s data and index.
648660 this . _dataDiffer = this . _differs . find ( [ ] ) . create ( ( _i : number , dataRow : RenderRow < T > ) => {
649661 return this . trackBy ? this . trackBy ( dataRow . dataIndex , dataRow . data ) : dataRow ;
650662 } ) ;
651-
652- if ( this . _virtualScrollViewport ) {
653- this . _setupVirtualScrolling ( this . _virtualScrollViewport ) ;
654- }
655663 }
656664
657665 ngOnInit ( ) {
@@ -667,9 +675,14 @@ export class CdkTable<T>
667675
668676 ngAfterContentInit ( ) {
669677 this . _viewRepeater =
670- this . recycleRows || this . _virtualScrollViewport
678+ this . recycleRows || this . _virtualScrollEnabled ( )
671679 ? new _RecycleViewRepeaterStrategy ( )
672680 : new _DisposeViewRepeaterStrategy ( ) ;
681+
682+ if ( this . _virtualScrollEnabled ( ) ) {
683+ this . _setupVirtualScrolling ( this . _virtualScrollViewport ! ) ;
684+ }
685+
673686 this . _hasInitialized = true ;
674687 }
675688
@@ -1038,24 +1051,23 @@ export class CdkTable<T>
10381051 * so that the differ equates their references.
10391052 */
10401053 private _getAllRenderRows ( ) : RenderRow < T > [ ] {
1041- const dataWithinRange = this . _renderedRange
1042- ? ( this . _data || [ ] ) . slice ( this . _renderedRange . start , this . _renderedRange . end )
1043- : [ ] ;
1054+ // Note: the `_data` is typed as an array, but some internal apps end up passing diffrent types.
1055+ if ( ! Array . isArray ( this . _data ) || ! this . _renderedRange ) {
1056+ return [ ] ;
1057+ }
1058+
10441059 const renderRows : RenderRow < T > [ ] = [ ] ;
1060+ const end = Math . min ( this . _data . length , this . _renderedRange . end ) ;
10451061
10461062 // Store the cache and create a new one. Any re-used RenderRow objects will be moved into the
10471063 // new cache while unused ones can be picked up by garbage collection.
10481064 const prevCachedRenderRows = this . _cachedRenderRowsMap ;
10491065 this . _cachedRenderRowsMap = new Map ( ) ;
10501066
1051- if ( ! this . _data ) {
1052- return renderRows ;
1053- }
1054-
10551067 // For each data object, get the list of rows that should be rendered, represented by the
10561068 // respective `RenderRow` object which is the pair of `data` and `CdkRowDef`.
1057- for ( let i = 0 ; i < dataWithinRange . length ; i ++ ) {
1058- let data = dataWithinRange [ i ] ;
1069+ for ( let i = this . _renderedRange . start ; i < end ; i ++ ) {
1070+ const data = this . _data [ i ] ;
10591071 const renderRowsForData = this . _getRenderRowsForData ( data , i , prevCachedRenderRows . get ( data ) ) ;
10601072
10611073 if ( ! this . _cachedRenderRowsMap . has ( data ) ) {
@@ -1479,6 +1491,9 @@ export class CdkTable<T>
14791491 const virtualScrollScheduler =
14801492 typeof requestAnimationFrame !== 'undefined' ? animationFrameScheduler : asapScheduler ;
14811493
1494+ // Render nothing since the virtual scroll viewport will take over.
1495+ this . viewChange . next ( { start : 0 , end : 0 } ) ;
1496+
14821497 // Forward the rendered range computed by the virtual scroll viewport to the table.
14831498 viewport . renderedRangeStream
14841499 // We need the scheduler here, because the virtual scrolling module uses an identical
@@ -1628,6 +1643,10 @@ export class CdkTable<T>
16281643 const endRect = lastNode ?. getBoundingClientRect ?.( ) ;
16291644 return startRect && endRect ? endRect . bottom - startRect . top : 0 ;
16301645 }
1646+
1647+ private _virtualScrollEnabled ( ) : boolean {
1648+ return ! this . _disableVirtualScrolling && this . _virtualScrollViewport != null ;
1649+ }
16311650}
16321651
16331652/** Utility function that gets a merged list of the entries in an array and values of a Set. */
0 commit comments