I have had some trouble reproducing this one reliably (it seems to occur more often on my app than in reproductions) but there seems to be a race condition related to the rendering of tab content when it is lazy-loaded.
Tab content should only be displayed once.
Toggling tabs very quickly (using arrow keys + space bar) can cause tab content to be drawn multiple times.
StackBlitz: https://stackblitz.com/edit/angular-material2-issue-h6pgut?file=app%2Fapp.component.ts
I am intentionally trying to be awkward in terms of timing (ie. setting the selectedTabIndex in a timeout), but this behaviour still should not happen.
I have recorded a reproduction in this video: https://gfycat.com/ReadyFairBasil
Note that at the start there is only one instance of each tab's content but by the end the second tab's content is duplicated.
Bug
Latest StackBlitz (Angular 6-rc2, Material 6-rc1)
I could only reproduce by using lazy-loaded tabs + NgForTrackBy + OnPush + window.setTimeout, but these may not be necessary. I also had to use keyboard selection to toggle fast enough to trigger the issue.
@andrewseguin Here is a much clearer repro: https://stackblitz.com/edit/angular-material2-issue-p51bcx
I tried to look into it myself and it seems the issue is quite deep into the Portal/animation/ViewContainerRef logic.
The older Tab 1 content ViewRef is marked as destroyed but still in the DOM.
It looks like anything less than 500ms will reproduce this bug? anything can be done using a delay trigger?
@benelliott The problem is the animation. If you disable it, the problem disappears.
<mat-tab-group [@.disabled]="true">
<mat-tab *ngFor="let tab of tabs" [label]="tab">
<div *matTabContent class="content">
{{tab}}
</div>
</mat-tab>
</mat-tab-group>
This would also fit the ~500ms observation from @xyz247.
Possibly related to https://github.com/angular/angular/issues/26133
@andrewseguin I'm seeing this in my real app. How come issues like this keep going unfixed after months and months? There are several duplicate reports of this one - so it's affecting real people.
It's easily duplicated with the lazy load demo for tabs.
It's clearly some race condition with the detachment / attachment of portals - it shouldn't be too hard to fix. (and I only see this only on lazy load)
This only works if you have only one base element inside each tabContent template
For Example
<mat-tab-group>
<mat-tab>
<ng-template matTabContent>
<div class="tab-container">
<!-- Content goes here -->
</div>
</ng-template>
</mat-tab>
<mat-tab>
<ng-template matTabContent>
<div class="tab-container">
<!-- Content goes here -->
</div>
</ng-template>
</mat-tab>
</mat-tab-group>
Listen to the output on the <mat-tab-group> for (animationDone)
<mat-tab-group (animationDone)="onTabAnimationDone()">
On Animation done, we want to look for any additional .tab-containers that have been rendered inside the active tab.
export class MyComponent {
constructor(private elementRef: ElementRef) {}
public onTabAnimationDone(): void {
const inactiveTabs = this.elementRef.nativeElement.querySelectorAll(
'.mat-tab-body-active .mat-tab-body-content > .tab-container:not(:first-child)'
);
inactiveTabs.forEach(tab => tab.remove());
}
}
Is there any update on this issue? I'm still experiencing this even now
I had experienced same issue. My solution is: Do not use lazy load tab content. Hope this could help someone.
Most helpful comment
@benelliott The problem is the animation. If you disable it, the problem disappears.
This would also fit the ~500ms observation from @xyz247.
Possibly related to https://github.com/angular/angular/issues/26133