Skip to content

Commit fd1827a

Browse files
committed
fix(tabs): maintain selected tab when new tabs are added or removed
Maintains the `selectedIndex` of the current tab when a new tab is added or removed. Previously, changing the amount of tabs would shift the array, causing a different tab to be selected. Fixes #7738.
1 parent c3d7cd9 commit fd1827a

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

src/lib/tabs/tab-group.spec.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,12 @@ describe('MatTabGroup', () => {
278278
expect(tabs[3].origin).toBeGreaterThanOrEqual(0);
279279

280280
// Add a new tab in the beginning and select it, expect an origin < than 0 (animate left)
281-
fixture.componentInstance.tabs.push({label: 'New tab', content: 'to left of index'});
282281
fixture.componentInstance.selectedIndex = 0;
283282
fixture.detectChanges();
284283

284+
fixture.componentInstance.tabs.push({label: 'New tab', content: 'to left of index'});
285+
fixture.detectChanges();
286+
285287
tabs = component._tabs.toArray();
286288
expect(tabs[0].origin).toBeLessThan(0);
287289
});
@@ -303,6 +305,47 @@ describe('MatTabGroup', () => {
303305
expect(component.selectedIndex).toBe(numberOfTabs - 2);
304306
});
305307

308+
309+
it('should maintain the selected tab if a new tab is added', () => {
310+
fixture.detectChanges();
311+
const component: MatTabGroup =
312+
fixture.debugElement.query(By.css('mat-tab-group')).componentInstance;
313+
314+
fixture.componentInstance.selectedIndex = 1;
315+
fixture.detectChanges();
316+
317+
// Add a new tab at the beginning.
318+
fixture.componentInstance.tabs.unshift({label: 'New tab', content: 'at the start'});
319+
fixture.detectChanges();
320+
321+
expect(component.selectedIndex).toBe(2);
322+
expect(component._tabs.toArray()[2].isActive).toBe(true);
323+
});
324+
325+
326+
it('should maintain the selected tab if a tab is removed', () => {
327+
// Add a couple of tabs so we have more to work with.
328+
fixture.componentInstance.tabs.push(
329+
{label: 'New tab', content: 'with new content'},
330+
{label: 'Another new tab', content: 'with newer content'}
331+
);
332+
333+
// Select the second-to-last tab.
334+
fixture.componentInstance.selectedIndex = 3;
335+
fixture.detectChanges();
336+
337+
const component: MatTabGroup =
338+
fixture.debugElement.query(By.css('mat-tab-group')).componentInstance;
339+
340+
// Remove a tab right before the selected one.
341+
fixture.componentInstance.tabs.splice(2, 1);
342+
fixture.detectChanges();
343+
344+
expect(component.selectedIndex).toBe(1);
345+
expect(component._tabs.toArray()[1].isActive).toBe(true);
346+
});
347+
348+
306349
});
307350

308351
describe('async tabs', () => {

src/lib/tabs/tab-group.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,16 @@ export class MatTabGroup extends _MatTabGroupMixinBase implements AfterContentIn
210210
// Subscribe to changes in the amount of tabs, in order to be
211211
// able to re-render the content as new tabs are added or removed.
212212
this._tabsSubscription = this._tabs.changes.subscribe(() => {
213+
const tabs = this._tabs.toArray();
214+
215+
// Maintain the previously-selected tab if a new tab is added or removed.
216+
for (let i = 0; i < tabs.length; i++) {
217+
if (tabs[i].isActive) {
218+
this._indexToSelect = i;
219+
break;
220+
}
221+
}
222+
213223
this._subscribeToTabLabels();
214224
this._changeDetectorRef.markForCheck();
215225
});

src/lib/tabs/tab.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
ViewChild,
2121
ViewContainerRef,
2222
ViewEncapsulation,
23+
ElementRef,
2324
} from '@angular/core';
2425
import {CanDisable, mixinDisabled} from '@angular/material/core';
2526
import {Subject} from 'rxjs/Subject';

0 commit comments

Comments
 (0)