@@ -23,6 +23,8 @@ import {
2323 ViewContainerRef ,
2424 forwardRef ,
2525 ViewChild ,
26+ OnChanges ,
27+ SimpleChanges ,
2628} from '@angular/core' ;
2729import { AnimationEvent } from '@angular/animations' ;
2830import { TemplatePortal , CdkPortalOutlet , PortalHostDirective } from '@angular/cdk/portal' ;
@@ -81,6 +83,7 @@ export class MatTabBodyPortal extends CdkPortalOutlet implements OnInit, OnDestr
8183 . subscribe ( ( isCentering : boolean ) => {
8284 if ( isCentering && ! this . hasAttached ( ) ) {
8385 this . attach ( this . _host . _content ) ;
86+ this . _host . _restoreScrollPosition ( ) ;
8487 }
8588 } ) ;
8689
@@ -111,9 +114,13 @@ export class MatTabBodyPortal extends CdkPortalOutlet implements OnInit, OnDestr
111114 animations : [ matTabsAnimations . translateTab ] ,
112115 host : {
113116 'class' : 'mat-tab-body' ,
117+ '[class.mat-tab-body-active]' : 'active' ,
114118 } ,
115119} )
116- export class MatTabBody implements OnInit {
120+ export class MatTabBody implements OnInit , OnChanges {
121+ /** Element wrapping the tab's content. */
122+ @ViewChild ( 'content' ) _contentElement : ElementRef ;
123+
117124 /** Event emitted when the tab begins to animate towards the center as the active tab. */
118125 @Output ( ) readonly _onCentering : EventEmitter < number > = new EventEmitter < number > ( ) ;
119126
@@ -132,6 +139,12 @@ export class MatTabBody implements OnInit {
132139 /** The tab body content to display. */
133140 @Input ( 'content' ) _content : TemplatePortal ;
134141
142+ /** Whether the tab is currently active. */
143+ @Input ( ) active : boolean ;
144+
145+ /** Scroll position of the tab before the user switched away. */
146+ private _lastScrollPosition = 0 ;
147+
135148 /** The shifted index position of the tab body, where zero represents the active center tab. */
136149 @Input ( )
137150 set position ( position : number ) {
@@ -180,6 +193,16 @@ export class MatTabBody implements OnInit {
180193 }
181194 }
182195
196+ ngOnChanges ( changes : SimpleChanges ) {
197+ // Cache the scroll position before moving away from the tab. Note that this has to be done
198+ // through change detection and as early as possible, because some browsers (namely Safari)
199+ // will reset the scroll position when we switch from an absolute to a relative position.
200+ if ( changes . active && changes . active . previousValue ) {
201+ this . _lastScrollPosition = this . _elementRef . nativeElement . scrollTop ||
202+ this . _contentElement . nativeElement . scrollTop ;
203+ }
204+ }
205+
183206 _onTranslateTabComplete ( e : AnimationEvent ) : void {
184207 // If the transition to the center is complete, emit an event.
185208 if ( this . _isCenterPosition ( e . toState ) && this . _isCenterPosition ( this . _position ) ) {
@@ -197,9 +220,19 @@ export class MatTabBody implements OnInit {
197220 }
198221
199222 /** Whether the provided position state is considered center, regardless of origin. */
200- _isCenterPosition ( position : MatTabBodyPositionState | string ) : boolean {
223+ _isCenterPosition ( position : MatTabBodyPositionState | string ) : boolean {
201224 return position == 'center' ||
202- position == 'left-origin-center' ||
203- position == 'right-origin-center' ;
225+ position == 'left-origin-center' ||
226+ position == 'right-origin-center' ;
227+ }
228+
229+ _restoreScrollPosition ( ) {
230+ if ( this . _lastScrollPosition ) {
231+ // Depending on the browser, the scrollable element can end up being
232+ // either the host element or the element with all the content.
233+ this . _contentElement . nativeElement . scrollTop =
234+ this . _elementRef . nativeElement . scrollTop =
235+ this . _lastScrollPosition ;
236+ }
204237 }
205238}
0 commit comments