Skip to content

Commit 51206ee

Browse files
crisbetotinayuangao
authored andcommitted
fix(tabs): hide mat-tab-nav-bar ink bar when no link is active (#9701)
Currently the `mat-tab-nav-bar` updates the ink bar location whenever a link becomes active, however this doesn't account for the case where none of the links are active. These changes switch to checking for active links whenever the active state of any of the tabs changes and hiding the ink bar if none of them are active. Fixes #9671.
1 parent 2aba8ab commit 51206ee

File tree

2 files changed

+33
-14
lines changed

2 files changed

+33
-14
lines changed

src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
2-
import {Component, ViewChild} from '@angular/core';
2+
import {Component, ViewChild, ViewChildren, QueryList} from '@angular/core';
33
import {By} from '@angular/platform-browser';
44
import {dispatchFakeEvent, dispatchMouseEvent, createMouseEvent} from '@angular/cdk/testing';
55
import {Direction, Directionality} from '@angular/cdk/bidi';
@@ -213,6 +213,17 @@ describe('MatTabNavBar', () => {
213213

214214
expect(inkBar.alignToElement).toHaveBeenCalled();
215215
}));
216+
217+
it('should hide the ink bar when all the links are inactive', () => {
218+
const inkBar = fixture.componentInstance.tabNavBar._inkBar;
219+
220+
spyOn(inkBar, 'hide');
221+
222+
fixture.componentInstance.links.forEach(link => link.active = false);
223+
fixture.detectChanges();
224+
225+
expect(inkBar.hide).toHaveBeenCalled();
226+
});
216227
});
217228

218229
it('should clean up the ripple event handlers on destroy', () => {
@@ -271,6 +282,7 @@ describe('MatTabNavBar', () => {
271282
})
272283
class SimpleTabNavBarTestApp {
273284
@ViewChild(MatTabNav) tabNavBar: MatTabNav;
285+
@ViewChildren(MatTabLink) links: QueryList<MatTabLink>;
274286

275287
label = '';
276288
disabled: boolean = false;

src/lib/tabs/tab-nav-bar/tab-nav-bar.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ export class MatTabNav extends _MatTabNavMixinBase implements AfterContentInit,
7676
/** Subject that emits when the component has been destroyed. */
7777
private readonly _onDestroy = new Subject<void>();
7878

79-
_activeLinkChanged: boolean;
80-
_activeLinkElement: ElementRef;
79+
private _activeLinkChanged: boolean;
80+
private _activeLinkElement: ElementRef | null;
8181

8282
@ViewChild(MatInkBar) _inkBar: MatInkBar;
8383

@@ -118,21 +118,22 @@ export class MatTabNav extends _MatTabNavMixinBase implements AfterContentInit,
118118
super(elementRef);
119119
}
120120

121-
/** Notifies the component that the active link has been changed. */
121+
/**
122+
* Notifies the component that the active link has been changed.
123+
* @deletion-target 7.0.0 `element` parameter to be removed.
124+
*/
122125
updateActiveLink(element: ElementRef) {
123-
this._activeLinkChanged = this._activeLinkElement != element;
124-
this._activeLinkElement = element;
125-
126-
if (this._activeLinkChanged) {
127-
this._changeDetectorRef.markForCheck();
128-
}
126+
// Note: keeping the `element` for backwards-compat, but isn't being used for anything.
127+
this._activeLinkChanged = !!element;
128+
this._changeDetectorRef.markForCheck();
129129
}
130130

131131
ngAfterContentInit(): void {
132132
this._ngZone.runOutsideAngular(() => {
133133
const dirChange = this._dir ? this._dir.change : observableOf(null);
134134

135-
return merge(dirChange, this._viewportRuler.change(10)).pipe(takeUntil(this._onDestroy))
135+
return merge(dirChange, this._viewportRuler.change(10))
136+
.pipe(takeUntil(this._onDestroy))
136137
.subscribe(() => this._alignInkBar());
137138
});
138139

@@ -142,6 +143,9 @@ export class MatTabNav extends _MatTabNavMixinBase implements AfterContentInit,
142143
/** Checks if the active link has been changed and, if so, will update the ink bar. */
143144
ngAfterContentChecked(): void {
144145
if (this._activeLinkChanged) {
146+
const activeTab = this._tabLinks.find(tab => tab.active);
147+
148+
this._activeLinkElement = activeTab ? activeTab._elementRef : null;
145149
this._alignInkBar();
146150
this._activeLinkChanged = false;
147151
}
@@ -155,7 +159,10 @@ export class MatTabNav extends _MatTabNavMixinBase implements AfterContentInit,
155159
/** Aligns the ink bar to the active link. */
156160
_alignInkBar(): void {
157161
if (this._activeLinkElement) {
162+
this._inkBar.show();
158163
this._inkBar.alignToElement(this._activeLinkElement.nativeElement);
164+
} else {
165+
this._inkBar.hide();
159166
}
160167
}
161168

@@ -202,8 +209,8 @@ export class MatTabLink extends _MatTabLinkMixinBase
202209
@Input()
203210
get active(): boolean { return this._isActive; }
204211
set active(value: boolean) {
205-
this._isActive = value;
206-
if (value) {
212+
if (value !== this._isActive) {
213+
this._isActive = value;
207214
this._tabNavBar.updateActiveLink(this._elementRef);
208215
}
209216
}
@@ -223,7 +230,7 @@ export class MatTabLink extends _MatTabLinkMixinBase
223230
}
224231

225232
constructor(private _tabNavBar: MatTabNav,
226-
private _elementRef: ElementRef,
233+
public _elementRef: ElementRef,
227234
ngZone: NgZone,
228235
platform: Platform,
229236
@Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalOptions: RippleGlobalOptions,

0 commit comments

Comments
 (0)