@@ -199,8 +199,7 @@ export class NoDataRowOutlet implements RowOutlet {
199199 * @docs -private
200200 */
201201export interface RowContext < T >
202- extends CdkCellOutletMultiRowContext < T > ,
203- CdkCellOutletRowContext < T > { }
202+ extends CdkCellOutletMultiRowContext < T > , CdkCellOutletRowContext < T > { }
204203
205204/**
206205 * Class used to conveniently type the embedded view ref for rows with a context.
@@ -301,7 +300,12 @@ export class CdkTable<T>
301300 protected _viewRepeater : _ViewRepeater < T , RenderRow < T > , RowContext < T > > ;
302301 private readonly _viewportRuler = inject ( ViewportRuler ) ;
303302 private _injector = inject ( Injector ) ;
304- 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+ } ) ;
305309 private _positionListener =
306310 inject ( STICKY_POSITIONING_LISTENER , { optional : true } ) ||
307311 inject ( STICKY_POSITIONING_LISTENER , { optional : true , skipSelf : true } ) ;
@@ -467,6 +471,14 @@ export class CdkTable<T>
467471 /** Emits when the footer rows sticky state changes. */
468472 private readonly _footerRowStickyUpdates = new Subject < StickyUpdate > ( ) ;
469473
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+
470482 /** Aria role to apply to the table's cells based on the table's own role. */
471483 _getCellRole ( ) : string | null {
472484 // Perform this lazily in case the table's role was updated by a directive after construction.
@@ -565,7 +577,7 @@ export class CdkTable<T>
565577 get fixedLayout ( ) : boolean {
566578 // Require a fixed layout when virtual scrolling is enabled, otherwise
567579 // the element the header can jump around as the user is scrolling.
568- return this . _virtualScrollViewport ? true : this . _fixedLayout ;
580+ return this . _virtualScrollEnabled ( ) ? true : this . _fixedLayout ;
569581 }
570582 set fixedLayout ( value : boolean ) {
571583 this . _fixedLayout = value ;
@@ -595,7 +607,10 @@ export class CdkTable<T>
595607 *
596608 * @docs -private
597609 */
598- readonly viewChange : BehaviorSubject < ListRange > ;
610+ readonly viewChange : BehaviorSubject < ListRange > = new BehaviorSubject ( {
611+ start : 0 ,
612+ end : Number . MAX_VALUE ,
613+ } ) ;
599614
600615 // Outlets in the table's template where the header, data rows, and footer will be inserted.
601616 _rowOutlet : DataRowOutlet ;
@@ -638,21 +653,13 @@ export class CdkTable<T>
638653
639654 this . _isServer = ! this . _platform . isBrowser ;
640655 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- } ) ;
645656
646657 // Set up the trackBy function so that it uses the `RenderRow` as its identity by default. If
647658 // the user has provided a custom trackBy, return the result of that function as evaluated
648659 // with the values of the `RenderRow`'s data and index.
649660 this . _dataDiffer = this . _differs . find ( [ ] ) . create ( ( _i : number , dataRow : RenderRow < T > ) => {
650661 return this . trackBy ? this . trackBy ( dataRow . dataIndex , dataRow . data ) : dataRow ;
651662 } ) ;
652-
653- if ( this . _virtualScrollViewport ) {
654- this . _setupVirtualScrolling ( this . _virtualScrollViewport ) ;
655- }
656663 }
657664
658665 ngOnInit ( ) {
@@ -668,9 +675,14 @@ export class CdkTable<T>
668675
669676 ngAfterContentInit ( ) {
670677 this . _viewRepeater =
671- this . recycleRows || this . _virtualScrollViewport
678+ this . recycleRows || this . _virtualScrollEnabled ( )
672679 ? new _RecycleViewRepeaterStrategy ( )
673680 : new _DisposeViewRepeaterStrategy ( ) ;
681+
682+ if ( this . _virtualScrollEnabled ( ) ) {
683+ this . _setupVirtualScrolling ( this . _virtualScrollViewport ! ) ;
684+ }
685+
674686 this . _hasInitialized = true ;
675687 }
676688
@@ -1039,24 +1051,23 @@ export class CdkTable<T>
10391051 * so that the differ equates their references.
10401052 */
10411053 private _getAllRenderRows ( ) : RenderRow < T > [ ] {
1042- const dataWithinRange = this . _renderedRange
1043- ? ( this . _data || [ ] ) . slice ( this . _renderedRange . start , this . _renderedRange . end )
1044- : [ ] ;
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+
10451059 const renderRows : RenderRow < T > [ ] = [ ] ;
1060+ const end = Math . min ( this . _data . length , this . _renderedRange . end ) ;
10461061
10471062 // Store the cache and create a new one. Any re-used RenderRow objects will be moved into the
10481063 // new cache while unused ones can be picked up by garbage collection.
10491064 const prevCachedRenderRows = this . _cachedRenderRowsMap ;
10501065 this . _cachedRenderRowsMap = new Map ( ) ;
10511066
1052- if ( ! this . _data ) {
1053- return renderRows ;
1054- }
1055-
10561067 // For each data object, get the list of rows that should be rendered, represented by the
10571068 // 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 ] ;
1069+ for ( let i = this . _renderedRange . start ; i < end ; i ++ ) {
1070+ const data = this . _data [ i ] ;
10601071 const renderRowsForData = this . _getRenderRowsForData ( data , i , prevCachedRenderRows . get ( data ) ) ;
10611072
10621073 if ( ! this . _cachedRenderRowsMap . has ( data ) ) {
@@ -1480,6 +1491,9 @@ export class CdkTable<T>
14801491 const virtualScrollScheduler =
14811492 typeof requestAnimationFrame !== 'undefined' ? animationFrameScheduler : asapScheduler ;
14821493
1494+ // Render nothing since the virtual scroll viewport will take over.
1495+ this . viewChange . next ( { start : 0 , end : 0 } ) ;
1496+
14831497 // Forward the rendered range computed by the virtual scroll viewport to the table.
14841498 viewport . renderedRangeStream
14851499 // We need the scheduler here, because the virtual scrolling module uses an identical
@@ -1629,6 +1643,10 @@ export class CdkTable<T>
16291643 const endRect = lastNode ?. getBoundingClientRect ?.( ) ;
16301644 return startRect && endRect ? endRect . bottom - startRect . top : 0 ;
16311645 }
1646+
1647+ private _virtualScrollEnabled ( ) : boolean {
1648+ return ! this . _disableVirtualScrolling && this . _virtualScrollViewport != null ;
1649+ }
16321650}
16331651
16341652/** Utility function that gets a merged list of the entries in an array and values of a Set. */
0 commit comments