fix(tabs): pagination state not updated when tab content changes (#12911)

Fixes the displayed state of the tabs pagination not being updated when the content of the tabs changes. The issue comes from the fact that the content change callback runs outside the `NgZone`.

Fixes #12901.
This commit is contained in:
Kristiyan Kostadinov
2018-09-12 00:26:24 +02:00
committed by Jeremy Elbourn
parent 0c8efa54d4
commit a7de64a4cd
2 changed files with 52 additions and 6 deletions
+38 -2
View File
@@ -19,6 +19,7 @@ import {MatInkBar} from './ink-bar';
import {MatTabHeader} from './tab-header';
import {MatTabLabelWrapper} from './tab-label-wrapper';
import {Subject} from 'rxjs';
import {ObserversModule, MutationObserverFactory} from '@angular/cdk/observers';
describe('MatTabHeader', () => {
@@ -30,7 +31,7 @@ describe('MatTabHeader', () => {
beforeEach(async(() => {
dir = 'ltr';
TestBed.configureTestingModule({
imports: [CommonModule, PortalModule, MatRippleModule, ScrollingModule],
imports: [CommonModule, PortalModule, MatRippleModule, ScrollingModule, ObserversModule],
declarations: [
MatTabHeader,
MatInkBar,
@@ -345,6 +346,41 @@ describe('MatTabHeader', () => {
discardPeriodicTasks();
}));
it('should update the pagination state if the content of the labels changes', () => {
const mutationCallbacks: Function[] = [];
TestBed.overrideProvider(MutationObserverFactory, {
useValue: {
// Stub out the MutationObserver since the native one is async.
create: function(callback: Function) {
mutationCallbacks.push(callback);
return {observe: () => {}, disconnect: () => {}};
}
}
});
fixture = TestBed.createComponent(SimpleTabHeaderApp);
fixture.detectChanges();
const tabHeaderElement: HTMLElement =
fixture.nativeElement.querySelector('.mat-tab-header');
const labels =
Array.from<HTMLElement>(fixture.nativeElement.querySelectorAll('.label-content'));
const extraText = new Array(100).fill('w').join();
const enabledClass = 'mat-tab-header-pagination-controls-enabled';
expect(tabHeaderElement.classList).not.toContain(enabledClass);
labels.forEach(label => {
label.style.width = '';
label.textContent += extraText;
});
mutationCallbacks.forEach(callback => callback());
fixture.detectChanges();
expect(tabHeaderElement.classList).toContain(enabledClass);
});
});
});
@@ -359,7 +395,7 @@ interface Tab {
<mat-tab-header [selectedIndex]="selectedIndex" [disableRipple]="disableRipple"
(indexFocused)="focusedIndex = $event"
(selectFocusedIndex)="selectedIndex = $event">
<div matTabLabelWrapper style="min-width: 30px; width: 30px"
<div matTabLabelWrapper class="label-content" style="min-width: 30px; width: 30px"
*ngFor="let tab of tabs; let i = index"
[disabled]="!!tab.disabled"
(click)="selectedIndex = i">
+14 -4
View File
@@ -20,6 +20,7 @@ import {
ElementRef,
EventEmitter,
Input,
NgZone,
OnDestroy,
Optional,
Output,
@@ -137,7 +138,9 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
constructor(private _elementRef: ElementRef,
private _changeDetectorRef: ChangeDetectorRef,
private _viewportRuler: ViewportRuler,
@Optional() private _dir: Directionality) {
@Optional() private _dir: Directionality,
// @breaking-change 8.0.0 `_ngZone` parameter to be made required.
private _ngZone?: NgZone) {
super();
}
@@ -234,9 +237,16 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
* Callback for when the MutationObserver detects that the content has changed.
*/
_onContentChanges() {
this._updatePagination();
this._alignInkBarToSelectedTab();
this._changeDetectorRef.markForCheck();
const zoneCallback = () => {
this._updatePagination();
this._alignInkBarToSelectedTab();
this._changeDetectorRef.markForCheck();
};
// The content observer runs outside the `NgZone` by default, which
// means that we need to bring the callback back in ourselves.
// @breaking-change 8.0.0 Remove null check for `_ngZone` once it's a required parameter.
this._ngZone ? this._ngZone.run(zoneCallback) : zoneCallback();
}
/**