@@ -159,12 +159,9 @@ export class MatTabGroup extends _MatTabGroupMixinBase implements AfterContentIn
159
159
* a new selected tab should transition in (from the left or right).
160
160
*/
161
161
ngAfterContentChecked ( ) {
162
- // Clamp the next selected index to the bounds of 0 and the tabs length.
163
- // Note the `|| 0`, which ensures that values like NaN can't get through
164
- // and which would otherwise throw the component into an infinite loop
165
- // (since Math.max(NaN, 0) === NaN).
166
- let indexToSelect = this . _indexToSelect =
167
- Math . min ( this . _tabs . length - 1 , Math . max ( this . _indexToSelect || 0 , 0 ) ) ;
162
+ // Don't clamp the `indexToSelect` immediately in the setter because it can happen that
163
+ // the amount of tabs changes before the actual change detection runs.
164
+ const indexToSelect = this . _indexToSelect = this . _clampTabIndex ( this . _indexToSelect ) ;
168
165
169
166
// If there is a change in selected index, emit a change event. Should not trigger if
170
167
// the selected index has not yet been initialized.
@@ -200,16 +197,21 @@ export class MatTabGroup extends _MatTabGroupMixinBase implements AfterContentIn
200
197
// Subscribe to changes in the amount of tabs, in order to be
201
198
// able to re-render the content as new tabs are added or removed.
202
199
this . _tabsSubscription = this . _tabs . changes . subscribe ( ( ) => {
203
- const tabs = this . _tabs . toArray ( ) ;
204
-
205
- // Maintain the previously-selected tab if a new tab is added or removed.
206
- for ( let i = 0 ; i < tabs . length ; i ++ ) {
207
- if ( tabs [ i ] . isActive ) {
208
- // Assign both to the `_indexToSelect` and `_selectedIndex` so we don't fire a changed
209
- // event, otherwise the consumer may end up in an infinite loop in some edge cases like
210
- // adding a tab within the `selectedIndexChange` event.
211
- this . _indexToSelect = this . _selectedIndex = i ;
212
- break ;
200
+ const indexToSelect = this . _clampTabIndex ( this . _indexToSelect ) ;
201
+
202
+ // Maintain the previously-selected tab if a new tab is added or removed and there is no
203
+ // explicit change that selects a different tab.
204
+ if ( indexToSelect === this . _selectedIndex ) {
205
+ const tabs = this . _tabs . toArray ( ) ;
206
+
207
+ for ( let i = 0 ; i < tabs . length ; i ++ ) {
208
+ if ( tabs [ i ] . isActive ) {
209
+ // Assign both to the `_indexToSelect` and `_selectedIndex` so we don't fire a changed
210
+ // event, otherwise the consumer may end up in an infinite loop in some edge cases like
211
+ // adding a tab within the `selectedIndexChange` event.
212
+ this . _indexToSelect = this . _selectedIndex = i ;
213
+ break ;
214
+ }
213
215
}
214
216
}
215
217
@@ -261,6 +263,14 @@ export class MatTabGroup extends _MatTabGroupMixinBase implements AfterContentIn
261
263
} ) ;
262
264
}
263
265
266
+ /** Clamps the given index to the bounds of 0 and the tabs length. */
267
+ private _clampTabIndex ( index : number | null ) : number {
268
+ // Note the `|| 0`, which ensures that values like NaN can't get through
269
+ // and which would otherwise throw the component into an infinite loop
270
+ // (since Math.max(NaN, 0) === NaN).
271
+ return Math . min ( this . _tabs . length - 1 , Math . max ( index || 0 , 0 ) ) ;
272
+ }
273
+
264
274
/** Returns a unique id for each tab label element */
265
275
_getTabLabelId ( i : number ) : string {
266
276
return `mat-tab-label-${ this . _groupId } -${ i } ` ;
0 commit comments