Components: mat-expansion-panel "opens" with angular enter-leave animations, although current state is closed.

Created on 12 Jun 2018  ·  24Comments  ·  Source: angular/components

Bug, feature request, or proposal:

Bug

What is the expected behavior?

Mat-expansion-panel, when animating in or out, will stay in current state of opened or closed and render as such.

What is the current behavior?

mat-expansion-panel, when animating out, will open and display all hidden panel content, while still showing the current state as closed.

What are the steps to reproduce?

https://stackblitz.com/edit/angular-ykxmcr?file=src%2Fapp%2Fhero-list-multistep.component.ts

What is the use-case or motivation for changing an existing behavior?

Keeping the panel closed when animating out provides a much cleaner experience for the UI by preventing jumpiness and inconsistencies.

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

@angular/core 6.0.4
@angular/material 6.2.1
@angular/cdk 6.2.1
Chrome 67.0.3396.79
Firefox 60.0.2
Safari 11.1

Is there anything else we should know?

This happens with all animations I have tested, not just the one provided in the stackblitz example.

P3 materiaexpansion has pr

Most helpful comment

Piercing selector helped a lot:

::ng-deep .ng-animating div mat-accordion mat-expansion-panel mat-expansion-panel-header {
    height: 48px;
  }
::ng-deep .ng-animating div mat-accordion mat-expansion-panel div.mat-expansion-panel-content {
    height: 0px;
    visibility: hidden;
}

To say the truth, I totally forgot that styles are encapsulated by default in components not only from child to parent but also in opposite way. Thanks)

All 24 comments

Possibly related... I'm seeing something similar where the expansion panel is visible in a dialog as the dialog animates open. So it may appear that the animation does not have to applied directly to the expansion panel to see this behavior.

I have created an example for a router transition which illustrates the same problem:

https://stackblitz.com/edit/angular-expansion-panel-with-route-transition-animation?file=app%2Fapp%2Fapp.component.html

checkin-eurowings

Any updates on this? I thought maybe I had to specify something in my transition animations to cause everything to trigger in the right order, but if that's the case, I can't figure out what. I'd like to see an example of transitions that don't cause this behavior, if anybody has it working.

@aeslinger0

Possibly related... I'm seeing something similar where the expansion panel is visible in a dialog as the dialog animates open. So it may appear that the animation does not have to applied directly to the expansion panel to see this behavior.

See #13870. I am not sure they are related either, but what you describe seems similar to that issue.

That sounds likely, yeah. For now I have a workaround in my global styles. I'll keep an eye out for the linked patch landing and see if that fixes things.

That sounds likely, yeah. For now I have a workaround in my global styles. I'll keep an eye out for the linked patch landing and see if that fixes things.

Can you please tell how you fixed it? I partially fixed it in my code by to putting the content of expansion-panel to ng-template and fortunately it helped with collapsing it during transition animation, but instead that behavior, now initial size of expansion-panel is smaller then it should be (i believe because it contains totally nothing), but after animation it became normal.

I happen to only have this problem when a custom component, that contains a mat-card with a few mat-expansion-panels on it, slides into view with an enter-animation. So, I made these rules:

.ng-animating mat-card mat-expansion-panel-header { height: 48px; } .ng-animating mat-card div.mat-expansion-panel-content { height: 0px; visibility: hidden; }

The expansion panel header, in its closed state, is normally 48px. If you don't force it to this height during the animation, but try to remove the expansion panel content DIV by settings its height to 0, it renders at the height of its content instead (the panel-title element, probably) then jumps to the 48px height after the animation completes.

Hope this helps!

.ng-animating mat-card mat-expansion-panel-header {
height: 48px;
}
.ng-animating mat-card div.mat-expansion-panel-content {
height: 0px;
visibility: hidden;
}

With little changes it works, thanks)
And you are right, I have the same situation, with a little bit different HTML structure: custom component with expansion-panel inside and during enter-animation it is opened by default. If I put content of each panel in ng-content (to make it lazy-loaded) then content become invisible but height of the panel become too small.
Only sad thing in my case, to make it work it requires to put these styles rules in the the global styles.css. When I put them in the specific component style sheet, the first rule (for header) applies perfectly but the second (for content) just doesn't work.
Also I found that all these behavior worked fine even without hacks before I updated ng-material and angular libraries from version 6 to 7.

I think for your second rule you'd need a piercing selector, e.g. .ng-animating mat-card ::ng-deep div.mat-expansion-panel-content -- the mat-expansion-panel-content is part of the mat-expansion-panel component so you can't style it without the ::ng-deep. I find it easier to make the fix globally.

Piercing selector helped a lot:

::ng-deep .ng-animating div mat-accordion mat-expansion-panel mat-expansion-panel-header {
    height: 48px;
  }
::ng-deep .ng-animating div mat-accordion mat-expansion-panel div.mat-expansion-panel-content {
    height: 0px;
    visibility: hidden;
}

To say the truth, I totally forgot that styles are encapsulated by default in components not only from child to parent but also in opposite way. Thanks)

Hi every body, have you got some news about this issue ? Do you know if this fix will be integrated soon ? Thanks a lot!

I solved it temporarily until a fix arrives via a global style in style.scss:

// temporary fix for expansion panels until Angular Material Issue is fixed,
// see https://github.com/angular/material2/issues/13870
mat-accordion mat-expansion-panel {
  mat-expansion-panel-header {
    height: 40px; // height may be different for you
  }
  .mat-expansion-panel-content {
    height: 0;
  }
  &.mat-expanded {
    mat-expansion-panel-header {
      height: 64px; // height may be different for you
    }
    .mat-expansion-panel-content {
      height: auto;
    }
  }
}

So are Angular animations fundamentally broken if we get issues like this?

To visualize at least what's going on I found it helpful to add the following to global styles:

.ng-animating
{
    outline: 1px solid red;
}

During a simple slide-in animation, the view of my accordion is like this:

image

So the .ng-animating class is being inherited by the children of accordion and anything that is possible to be animatable is being highlighted as such. This happens even if my accordion is in a completely separate component of its own.

I've concluded the only way to get on with my day right now - is to just disable animations completely on the accordion.

<mat-accordion [@.disabled]="true">

None of the css solutions worked for me. The problem being they won't work for animating an accordion offscreen when you quite likely do want the panel to remain open in its current state. In addition if I click on an accordion that initially animates on screen (with a 30s animation) then I can't open any panels once the animation is complete. That seems like a much more serious issue, which I don't even understand.

In any case the problem seems to be that the .ng-animate class is being inherited by the accordion control.

If there's no way to prevent this then surely animations are broken, and the fix needs to be of broader scope than a nasty css hack?

I solved it temporarily until a fix arrives via a global style in style.scss:

// temporary fix for expansion panels until Angular Material Issue is fixed,
// see https://github.com/angular/material2/issues/13870
mat-accordion mat-expansion-panel {
  mat-expansion-panel-header {
    height: 40px; // height may be different for you
  }
  .mat-expansion-panel-content {
    height: 0;
  }
  &.mat-expanded {
    mat-expansion-panel-header {
      height: 64px; // height may be different for you
    }
    .mat-expansion-panel-content {
      height: auto;
    }
  }
}

it solves the problem but it causes mat-panel content to jump for a while :(

Я решил это временно, пока не пришло исправление с помощью глобального стиля в style.scss:

// temporary fix for expansion panels until Angular Material Issue is fixed,
// see https://github.com/angular/material2/issues/13870
mat-accordion mat-expansion-panel {
  mat-expansion-panel-header {
    height: 40px; // height may be different for you
  }
  .mat-expansion-panel-content {
    height: 0;
  }
  &.mat-expanded {
    mat-expansion-panel-header {
      height: 64px; // height may be different for you
    }
    .mat-expansion-panel-content {
      height: auto;
    }
  }
}

это решает проблему, но некоторое время заставляет содержимое панели мата прыгать :(

I think that if you set these options to mat-expansion-panel-header,
the jump in the content of the mat panel will be fixed. Replace with your height.

I wanted to add another repro that may be illustrative

https://stackblitz.com/edit/sidenav-accordion-expansion

Here an accordion is in a component which is factoried at runtime into a sidenav; you can see the accordion is expanded during sidenav enter.

I applied my fixes from upthread and it resolves your issue. In styles.scss, just add

mat-sidenav.ng-animating {
  mat-expansion-panel-header {
    height: 48px;
  }
  div.mat-expansion-panel-content {
    height: 0px;
    visibility: hidden;
  }
}

Of course this should work out of the box, but you can use it as an interim fix.

Finally solved this by using these attributes
<mat-expansion-panel-header expandedHeight="46px" collapsedHeight="46px"> your text </mat-expansion-panel-header>

you can change the height as per requirement

TY me LATER :) ;) ;p

So are Angular animations fundamentally broken if we get issues like this?

To visualize at least what's going on I found it helpful to add the following to global styles:

.ng-animating
{
    outline: 1px solid red;
}

During a simple slide-in animation, the view of my accordion is like this:

image

So the .ng-animating class is being inherited by the children of accordion and anything that is possible to be animatable is being highlighted as such. This happens even if my accordion is in a completely separate component of its own.

I've concluded the only way to get on with my day right now - is to just disable animations completely on the accordion.

<mat-accordion [@.disabled]="true">

None of the css solutions worked for me. The problem being they won't work for animating an accordion offscreen when you quite likely _do_ want the panel to remain open in its current state. In addition if I click on an accordion that initially animates on screen (with a 30s animation) then I can't open any panels once the animation is complete. That seems like a much more serious issue, which I don't even understand.

In any case the problem seems to be that the .ng-animate class is being inherited by the accordion control.

If there's no way to prevent this then surely animations are broken, and the fix needs to be of broader scope than a nasty css hack?

I was able to implement it with your approach of disabling animations instead this css's workarounds, but added a little twist , I instead setting the animations fully off I binded that field [@.disabled] with a variable from my component that would be true by default but on click that that opens my ng-template via a Mat-Dialog I change the variable value after a timeout fixing the visual bug of the animation and allowing animations for the mat-expansion-panel like so:

this.dialog.open(accountRef).afterClosed().subscribe(() => { this.disableAnimations = true; }); setTimeout(() => { this.disableAnimations = false; });

And on my html
<mat-accordion [multi]="true" [@.disabled]="disableAnimations">

Just make sure to turn disableAnimations to true when this accordion leaves the screen so it fixes the next time the mat-expansion-panel shows on the screen, for me this accordion is inside a mat dialog so i achieved this with the afterClosed event

Having this issue when expansion panel is used in a mat-dialog. Expands for a second when dialog is opened.

any updates?

The only workaround that worked for me is:

Thanks @chipicow

So are Angular animations fundamentally broken if we get issues like this?
To visualize at least what's going on I found it helpful to add the following to global styles:

.ng-animating
{
    outline: 1px solid red;
}

During a simple slide-in animation, the view of my accordion is like this:
image
So the .ng-animating class is being inherited by the children of accordion and anything that is possible to be animatable is being highlighted as such. This happens even if my accordion is in a completely separate component of its own.
I've concluded the only way to get on with my day right now - is to just disable animations completely on the accordion.

<mat-accordion [@.disabled]="true">

None of the css solutions worked for me. The problem being they won't work for animating an accordion offscreen when you quite likely _do_ want the panel to remain open in its current state. In addition if I click on an accordion that initially animates on screen (with a 30s animation) then I can't open any panels once the animation is complete. That seems like a much more serious issue, which I don't even understand.
In any case the problem seems to be that the .ng-animate class is being inherited by the accordion control.
If there's no way to prevent this then surely animations are broken, and the fix needs to be of broader scope than a nasty css hack?

I was able to implement it with your approach of disabling animations instead this css's workarounds, but added a little twist , I instead setting the animations fully off I binded that field [@.disabled] with a variable from my component that would be true by default but on click that that opens my ng-template via a Mat-Dialog I change the variable value after a timeout fixing the visual bug of the animation and allowing animations for the mat-expansion-panel like so:

this.dialog.open(accountRef).afterClosed().subscribe(() => { this.disableAnimations = true; }); setTimeout(() => { this.disableAnimations = false; });

And on my html
<mat-accordion [multi]="true" [@.disabled]="disableAnimations">

Just make sure to turn disableAnimations to true when this accordion leaves the screen so it fixes the next time the mat-expansion-panel shows on the screen, for me this accordion is inside a mat dialog so i achieved this with the afterClosed event

this worked for me, but I insted of the timeout I used

 ngAfterViewChecked(): void {
    this.disableAnimations = false;
  }

@ALGDB Angular will throw you the error NG0100: ExpressionChangedAfterItHasBeenCheckedError if you perform changes directly after a check.

image

A possible solution is to queue the operation to the next change detection check like this:

  ngAfterViewChecked(): void {
    setTimeout(() => {
      this.disableAnimations = false;
    }, 0)
  }

A more detailed explanation and different solutions can be found here in this video from the Angular docs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

LoganDupont picture LoganDupont  ·  3Comments

jelbourn picture jelbourn  ·  3Comments

savaryt picture savaryt  ·  3Comments

Miiekeee picture Miiekeee  ·  3Comments

vanor89 picture vanor89  ·  3Comments