Skip to content

Commit 41c43cc

Browse files
authored
fix(navbar): update ink bar when links change (#4897)
* fix(navbar): update ink bar when links change * remove add link * fix test * change strategy - use mutation observer * cleanup
1 parent 2e3910c commit 41c43cc

File tree

6 files changed

+74
-29
lines changed

6 files changed

+74
-29
lines changed

src/demo-app/tabs/tabs-demo.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
<h1>Tab Nav Bar</h1>
22

3+
<button md-button (click)="tabLinks.shift()">Remove tab</button>
4+
<button md-button (click)="swapTabLinks()">Swap first two</button>
5+
<button md-button (click)="addToLabel()">Add to labels</button>
6+
37
<div class="demo-nav-bar">
48
<nav md-tab-nav-bar aria-label="weather navigation links">
59
<a md-tab-link

src/demo-app/tabs/tabs-demo.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ export class TabsDemo {
8383
deleteTab(tab: any) {
8484
this.dynamicTabs.splice(this.dynamicTabs.indexOf(tab), 1);
8585
}
86+
87+
swapTabLinks() {
88+
const temp = this.tabLinks[0];
89+
this.tabLinks[0] = this.tabLinks[1];
90+
this.tabLinks[1] = temp;
91+
}
92+
93+
addToLabel() {
94+
this.tabLinks.forEach(link => link.label += 'extracontent');
95+
}
8696
}
8797

8898

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div class="mat-tab-links">
1+
<div class="mat-tab-links" (cdkObserveContent)="_alignInkBar()">
22
<ng-content></ng-content>
33
<md-ink-bar></md-ink-bar>
44
</div>

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

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
1+
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
22
import {MdTabsModule} from '../index';
33
import {MdTabNavBar} from './tab-nav-bar';
44
import {Component, ViewChild} from '@angular/core';
55
import {By} from '@angular/platform-browser';
66
import {ViewportRuler} from '../../core/overlay/position/viewport-ruler';
77
import {FakeViewportRuler} from '../../core/overlay/position/fake-viewport-ruler';
8-
import {dispatchMouseEvent, dispatchFakeEvent} from '../../core/testing/dispatch-events';
9-
import {LayoutDirection, Dir} from '../../core/rtl/dir';
8+
import {dispatchFakeEvent, dispatchMouseEvent} from '../../core/testing/dispatch-events';
9+
import {Dir, LayoutDirection} from '../../core/rtl/dir';
1010
import {Subject} from 'rxjs/Subject';
1111

1212

@@ -37,20 +37,19 @@ describe('MdTabNavBar', () => {
3737

3838
beforeEach(() => {
3939
fixture = TestBed.createComponent(SimpleTabNavBarTestApp);
40+
fixture.detectChanges();
4041
});
4142

4243
it('should change active index on click', () => {
43-
let component = fixture.debugElement.componentInstance;
44-
4544
// select the second link
4645
let tabLink = fixture.debugElement.queryAll(By.css('a'))[1];
4746
tabLink.nativeElement.click();
48-
expect(component.activeIndex).toBe(1);
47+
expect(fixture.componentInstance.activeIndex).toBe(1);
4948

5049
// select the third link
5150
tabLink = fixture.debugElement.queryAll(By.css('a'))[2];
5251
tabLink.nativeElement.click();
53-
expect(component.activeIndex).toBe(2);
52+
expect(fixture.componentInstance.activeIndex).toBe(2);
5453
});
5554

5655
it('should re-align the ink bar when the direction changes', () => {
@@ -64,6 +63,31 @@ describe('MdTabNavBar', () => {
6463
expect(inkBar.alignToElement).toHaveBeenCalled();
6564
});
6665

66+
it('should re-align the ink bar when the tabs list change', () => {
67+
const inkBar = fixture.componentInstance.tabNavBar._inkBar;
68+
69+
spyOn(inkBar, 'alignToElement');
70+
71+
fixture.componentInstance.tabs = [1, 2, 3, 4];
72+
fixture.detectChanges();
73+
74+
expect(inkBar.alignToElement).toHaveBeenCalled();
75+
});
76+
77+
it('should re-align the ink bar when the tab labels change the width', done => {
78+
const inkBar = fixture.componentInstance.tabNavBar._inkBar;
79+
80+
const spy = spyOn(inkBar, 'alignToElement').and.callFake(() => {
81+
expect(spy.calls.any()).toBe(true);
82+
done();
83+
});
84+
85+
fixture.componentInstance.label = 'label change';
86+
fixture.detectChanges();
87+
88+
expect(spy.calls.any()).toBe(false);
89+
});
90+
6791
it('should re-align the ink bar when the window is resized', fakeAsync(() => {
6892
const inkBar = fixture.componentInstance.tabNavBar._inkBar;
6993

@@ -97,15 +121,21 @@ describe('MdTabNavBar', () => {
97121
selector: 'test-app',
98122
template: `
99123
<nav md-tab-nav-bar>
100-
<a md-tab-link [active]="activeIndex === 0" (click)="activeIndex = 0">Tab One</a>
101-
<a md-tab-link [active]="activeIndex === 1" (click)="activeIndex = 1">Tab Two</a>
102-
<a md-tab-link [active]="activeIndex === 2" (click)="activeIndex = 2">Tab Three</a>
124+
<a md-tab-link
125+
*ngFor="let tab of tabs; let index = index"
126+
[active]="activeIndex === index"
127+
(click)="activeIndex = index">
128+
Tab link {{label}}
129+
</a>
103130
</nav>
104131
`
105132
})
106133
class SimpleTabNavBarTestApp {
107134
@ViewChild(MdTabNavBar) tabNavBar: MdTabNavBar;
108135

136+
label = '';
137+
tabs = [0, 1, 2];
138+
109139
activeIndex = 0;
110140
}
111141

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

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
import {
2+
AfterContentInit,
23
Component,
3-
Input,
4-
ViewChild,
5-
ElementRef,
6-
ViewEncapsulation,
74
Directive,
8-
NgZone,
5+
ElementRef,
96
Inject,
10-
Optional,
7+
Input,
8+
NgZone,
119
OnDestroy,
12-
AfterContentInit,
10+
Optional,
11+
ViewChild,
12+
ViewEncapsulation
1313
} from '@angular/core';
1414
import {MdInkBar} from '../ink-bar';
1515
import {MdRipple} from '../../core/ripple/index';
1616
import {ViewportRuler} from '../../core/overlay/position/viewport-ruler';
17-
import {MD_RIPPLE_GLOBAL_OPTIONS, RippleGlobalOptions, Dir, Platform} from '../../core';
17+
import {Dir, MD_RIPPLE_GLOBAL_OPTIONS, Platform, RippleGlobalOptions} from '../../core';
1818
import {Observable} from 'rxjs/Observable';
19-
import {Subscription} from 'rxjs/Subscription';
2019
import 'rxjs/add/operator/auditTime';
20+
import 'rxjs/add/operator/takeUntil';
2121
import 'rxjs/add/observable/of';
2222
import 'rxjs/add/observable/merge';
23+
import {Subject} from 'rxjs/Subject';
2324

2425
/**
2526
* Navigation component matching the styles of the tab group header.
@@ -34,8 +35,8 @@ import 'rxjs/add/observable/merge';
3435
encapsulation: ViewEncapsulation.None,
3536
})
3637
export class MdTabNavBar implements AfterContentInit, OnDestroy {
37-
/** Combines listeners that will re-align the ink bar whenever they're invoked. */
38-
private _realignInkBar: Subscription = null;
38+
/** Subject that emits when the component has been destroyed. */
39+
private _onDestroy = new Subject<void>();
3940

4041
_activeLinkChanged: boolean;
4142
_activeLinkElement: ElementRef;
@@ -51,13 +52,15 @@ export class MdTabNavBar implements AfterContentInit, OnDestroy {
5152
}
5253

5354
ngAfterContentInit(): void {
54-
this._realignInkBar = this._ngZone.runOutsideAngular(() => {
55+
this._ngZone.runOutsideAngular(() => {
5556
let dirChange = this._dir ? this._dir.dirChange : Observable.of(null);
5657
let resize = typeof window !== 'undefined' ?
5758
Observable.fromEvent(window, 'resize').auditTime(10) :
5859
Observable.of(null);
5960

60-
return Observable.merge(dirChange, resize).subscribe(() => this._alignInkBar());
61+
return Observable.merge(dirChange, resize)
62+
.takeUntil(this._onDestroy)
63+
.subscribe(() => this._alignInkBar());
6164
});
6265
}
6366

@@ -70,14 +73,11 @@ export class MdTabNavBar implements AfterContentInit, OnDestroy {
7073
}
7174

7275
ngOnDestroy() {
73-
if (this._realignInkBar) {
74-
this._realignInkBar.unsubscribe();
75-
this._realignInkBar = null;
76-
}
76+
this._onDestroy.next();
7777
}
7878

7979
/** Aligns the ink bar to the active link. */
80-
private _alignInkBar(): void {
80+
_alignInkBar(): void {
8181
if (this._activeLinkElement) {
8282
this._inkBar.alignToElement(this._activeLinkElement.nativeElement);
8383
}

tools/gulp/packaging/rollup-helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const ROLLUP_GLOBALS = {
4242
'rxjs/add/operator/share': 'Rx.Observable.prototype',
4343
'rxjs/add/operator/startWith': 'Rx.Observable.prototype',
4444
'rxjs/add/operator/switchMap': 'Rx.Observable.prototype',
45+
'rxjs/add/operator/takeUntil': 'Rx.Observable.prototype',
4546
'rxjs/add/operator/toPromise': 'Rx.Observable.prototype',
4647
'rxjs/BehaviorSubject': 'Rx',
4748
'rxjs/Observable': 'Rx',

0 commit comments

Comments
 (0)