components: [Tabs] Hidden tabs don't render expansion panels correctly

Bug, feature request, or proposal:

Bug.

What is the expected behavior?

A <md-expansion-panel [expanded]="false"> should not be expanded until explicitally opened. When inside a hidden tab, .mat-expansion-panel-content should have height : 0px

What is the current behavior?

Panel is collapsed but panetl’s content is visible. Problem occurs only if the accordion is in a hidden tab of a tab-group.

What are the steps to reproduce?

http://plnkr.co/edit/q6kXPH?p=preview

  <md-tab label="Working">
    <md-accordion>
        <md-expansion-panel [expanded]="false">
        <md-expansion-panel-header>
          You should see me
        </md-expansion-panel-header>
        <p>Yous should not see me</p>
      </md-expansion-panel>
    </md-accordion>
  </md-tab>
  <md-tab label="Not working">
    <md-accordion>
        <md-expansion-panel [expanded]="false">
        <md-expansion-panel-header>
          You should see me
        </md-expansion-panel-header>
        <p>Yous should not see me</p>
      </md-expansion-panel>
    </md-accordion>
  </md-tab>
</md-tab-group>

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Material2 master Angular 4.2.3

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 59
  • Comments: 109 (18 by maintainers)

Commits related to this issue

Most upvoted comments

Hello! @julianobrasil, following @lebeker advice that’s working for me as a workaround:

<md-tab-group [(selectedIndex)]="selectedTabIndex">
  <md-tab label="First">
    <div>...</div>
  </md-tab>
  <md-tab label="Second">
    <div *ngIf="selectedTabIndex == 1">...</div>
  <md-tab>
  <md-tab label="Third">
    <div *ngIf="selectedTabIndex == 2">...</div>
  <md-tab>
</md-tab-group>

Or use css ? It worked for me ! 😃

div.mat-expansion-panel-content:not(.mat-expanded) {
    height: 0px !important;
    visibility: hidden !important;
}

.mat-expansion-panel-header  {
    height: 48px;
}

Update Angular 6

Since Angular 6 mat-tabs can be loaded lazily. Example: (from StackOverflow)

<mat-tab-group>
  <mat-tab>
    <ng-template matTabContent>
      <p>Content goes here</p>
    </ng-template>
  </mat-tab>
</mat-tab-group>

This should solve the issue.

Hey, guys! Good news. This is fixed in 6.0.0-beta.0: https://stackblitz.com/edit/angular-material2-beta-6-table-mat-tab

To use the lazy loading feature, just put mat-tab content inside <ng-template matTabContent>

<mat-tab label="Lazy loading content">
  <ng-template matTabContent>
        <!--here goes tab content-->
  </ng-template>
</mat-tab>

image

@rayabhagis, did you use this into your component ?

encapsulation: ViewEncapsulation.None

Updated CSS:

div.mat-expansion-panel-content:not(.mat-expanded) {
    height: 0px;
    visibility: hidden;
}

.mat-expansion-panel-header:not(.mat-expanded)  {
    height: 48px;
}

.mat-expansion-panel-header  {
    height: 64px;
    .mat-content {
        font-weight: 100;
        font-size: 18px;
    }
    &.mat-expanded {        
        .mat-expansion-indicator {
            transform: rotate(180deg);
        }
    }
}

This actually happens to anything that contains an animation when set inside a hidden md-tab.

@ClemensSchneider sad but true. That trick stops working with @angular/material@5.0.0-rc.1.

@julianobrasil just playing along with your solution but it didn’t work in my case with more than two tabs. I believe that’s because the showExpansion property is not actually changed after the first tab change. Changing it just before setTimeout had no effect for me. Perhaps I did something wrong?

To have my test working with more than two tabs I basically just changed the type and the name of the property selectedTabIndex (of course ❤️ ). Here it is:

export class AppComponent {
  selectedTabIndex = 0;
  tabChange(event) {
    Promise.resolve().then(() => this.selectedTabIndex = event.index);
  }
}
<mat-tab-group (selectedTabChange)="tabChange($event)">
  <mat-tab label="Tab 1">
    <mat-expansion-panel>...</mat-expansion-panel>
  </mat-tab>
  <mat-tab label="Tab 2">
    <mat-expansion-panel *ngIf="selectedTabIndex == 1">...</mat-expansion-panel>
  </mat-tab>
  <mat-tab label="Tab 3">
    <mat-expansion-panel *ngIf="selectedTabIndex == 2">...</mat-expansion-panel>
  </mat-tab>
</mat-tab-group>

Cheers!

If it’s not it’s amazing that it’s still not fixed.

Just a little tweak of @lula’s solution - you don’t need to actually store the selectedIndex. Instead you could just reference the md-tab-group:

<md-tab-group #myTabGroup>
    <md-tab label="First">
        <div>First</div>
    </md-tab>
    <md-tab label="Second">
        <md-accordion *ngIf="myTabGroup.selectedIndex == 1">
            <md-expansion-panel *ngFor="let i of [1,2,3];">
                <md-expansion-panel-header>
                    <md-panel-title>Test {{ i }}</md-panel-title>
                </md-expansion-panel-header>

                <p>Testing content {{ i }}</p>
            </md-expansion-panel>
        </md-accordion>
    </md-tab>
    <md-tab label="Third">
        <div>Third</div>
    </md-tab>
</md-tab-group>

Ok, i edit the issue : the problem occurs only if the element is in a hidden tab. Thanks @julianobrasil

As a working workaround for lazy initialized panels i propose following CSS

.mat-expansion-panel-header {
  height: 48px;
}
.mat-expansion-panel-content:not(.mat-expanded):not(.ng-animating){
  display: none;
}

For some reasons bastienlemaitre’s solution was breaking animiation in my case. After trial and errors this isw what I came up with. Animation is still not that smooth on panel collapse, but its better then whole content poping upon expansion.

@TobiasMalikowski probably this will help you.

The CSS solution by @bastienlemaitre is not working for me in the content of hidden tabs.

Tried the solution of mdTab.isActive based on @julianobrasil suggestion with ngIf but resulting in ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'.

I have to generate tabs dynamically, so storing selected index won’t work. Here is what I am trying.

<mat-tab-group #tabGroup>
    <mat-tab #currTab *ngFor="let label of labels">
        <ng-template mat-tab-label>
          <span>{{label}}</span>
        </ng-template>
        <div *ngIf="currTab.isActive" >
          <mat-expansion-panel [expanded]="false" *ngFor="let panel of panels">
            <mat-expansion-panel-header>
              <mat-panel-title>{{panel.title}}</mat-panel-title>
              <mat-panel-description>{{panel.desc}}</mat-panel-description>
            </mat-expansion-panel-header>
            <div>{{panel.content}}</div>
          </mat-expansion-panel>
        </div>
    </mat-tab>
</mat-tab-group>

Is there a better way to operate on this one?

It seems to be working correctly here: http://plnkr.co/edit/q6kXPH?p=preview

Hey, i just temporarily fixed this error for me by adding click event on mat-tab, and checking for right index. Like this: (selectedTabChange)="showAccordion($event)" and in function switch right bool:

showAccordion(event) {`
        if (event.index === 1) {
            setTimeout(() => {
                this.showAccordionBool = true;
            }, 100);
        } else {
            setTimeout(() => {
                this.showAccordionBool = false;
            }, 100);
        }
    }

And lastly added in my template that if statement: <mat-accordion *ngIf="showAccordionBool">

How about MAT team fix the bug and we will not have to look for not-working-anyway workarounds ?😃

@lebeker, I’ve been using something like that in a project: http://plnkr.co/edit/YAi6Xb?p=preview

I tried isActive (*ngIf="mdTab.isActive" where mdTab is a reference template variable), but it causes a “changed after checked” error.

Issue still exists in version 8.

@lula, your fix almost work perfectly. In my case I was just missing the selectedIndex <mat-tab-group [(selectedIndex)]="selectedTabIndex" (selectedTabChange)="tabChange($event)"> which seems to make difference for some reason. Anyway thanks for saving my day !

@lula worked for me, thanks!

😉 A very unacceptable workaround (but useful if you can’t wait for the next release, possibly without this bug) is using a *ngIf and just show the accordion/expansion panel when it’s needed (if it’s needed when the page is loaded, put a variable in a setTimeout to trigger *ngIf in about 4 to 5 seconds after the tab-group has been rendered, or use the tab change event). In this case the expansion is shown in its expected state (collapsed or expanded).

OK. Updated the plunker to reproduce it: http://plnkr.co/edit/q6kXPH?p=preview

Put it in the issue.

When will this library be comparable to the material-ui that react has?

React currently has a better material component library than Google’s own material library

@Sieabah instead of making pointless comments, why not simply submit a pr with a fix? or maybe dig into the issue and try to found out what is exactly causing it?

This really needs to be fixed.

The following scss worked for me as a workaround:

.mat-expansion-panel-header {
  height: 48px;
}
.mat-expansion-panel-content:not(.mat-expanded) {
  .mat-expansion-panel-body {
    padding-bottom: 0;
  }
}

in component: selectedTab: MatTab;

in template:

<mat-tab-group (selectedTabChange)="selectedTab=$event.tab">
...
 <mat-tab #mdTab label="Super Label">
    <div *ngIf="mdTab==selectedTab">
...

Angular 5 + latest material: still able to reproduce the bug 😦 But @lula workaround helped to solve the issue. https://github.com/angular/material2/issues/5269#issuecomment-326692430

Hopefully we will have a real fix soon

Unfortunately, the solution with *ngIf="selectedIndex === 1" doesn’t work if you need forms inside your tabs as if the user change tabs, it’ll destroy your components with your FormControl’s values.

Any update on this? Still broken in angular 5.0.0 with material 5.0.0-rc0 for me 😦 I have an expansion panel inside of a tab. When I navigate to the tab, I can see the contents of the expansion panel, instead of the contents being hidden.

Confirmed that this work in v15 https://stackblitz.com/edit/angular-pmsgwg

@DamienFlury - This doesn’t appear to work when dynamicHeight="true" on mat-tab-group because:

  1. Tab initially renders with no content.
  2. The content is fetched (lazily) as expected.
  3. The tab expands (dynamically) to fit the lazily loaded content.

This causes an unsightly flicker each time the tab is loaded.

The CSS solution is working for me, but there is an ugly jump in the animation. Is there any way to fix this jump within CSS? Tried a few things, but it looks like that the height change gets not animated properly.

Anyways can’t wait to see a true fix for this annoying bug.

@jonyadamit, yeah, this is drawback of this way. I really don’t know about where this is in material project timeline, but my guess is that this is actually an angular issue instead of a material one and matTabContent is a feature that can be used to fix this issue in some specific cases (when you don’t need to keep the state).

@jackwootton I have changed your code a bit. its working properly.

https://stackblitz.com/edit/deferred-expansion-panel-broken-b2vurz?file=app%2Fside-menu%2Fside-menu.component.ts

The problem is all about *ngIf. First time its not able to make it true. that’s why I am setting it true using setTimeout().

If you still have issue do let me know. I will try to help.

The docs site will be updated for sure when the final release is out. I don’t think the team is updating the site for beta/rc versions.

Seems that it’s not an issue with the material expansion-panels (as @rhino5oh already noted), but a general issue with angular animations that have a target height of * and are triggered while the element to be animated is still detached from DOM (and thus, the target height cannot be known). I can confirm that my own components that include such animations are rendered incorrectly (height = 0) when used inside tab-content.

Maybe somebody from the material-team can confirm this assumption?

I can confirm that neither mat-accordion nor mat-expansion-panel are causing the issue. This is because I’m having the same exact problem using PrimeNG’s p-accordion and p-accordionTab. Hopefully that helps save some debug time for the devs.

Is there any news on whether this issue will be addressed in a forthcoming release?

Did something change here? The solution that was working earlier seems to have broken in our app now.

@michalmw there’s nothing to show in my component.ts concerning with my workaround. Variable selectedTabIndex is directly used in the view template. Angular should take care of instantiating it. Should you need to start with another tab than the first, then you can also add selectedTabIndex to component.ts and set the tab index you prefer. Let me know if this is what you wanted to know or you needed something else 😃

Hey! that bug is really annoying, is there any hope to see it’s fix?) Maybe applying @julianobrasil solution with *ngIf=isActive on tabs content?