Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
bencbradshaw opened this issue Jun 12, 2018 · 29 comments · May be fixed by #11778
Open
Labels
area: material/expansion P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@bencbradshaw
Copy link

bencbradshaw commented Jun 12, 2018

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.

@crisbeto crisbeto self-assigned this Jun 13, 2018
@crisbeto crisbeto added P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent has pr labels Jun 13, 2018
crisbeto added a commit to crisbeto/material2 that referenced this issue Jun 13, 2018
Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes angular#11765.
crisbeto added a commit to crisbeto/material2 that referenced this issue Aug 20, 2018
Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes angular#11765.
jelbourn pushed a commit that referenced this issue Aug 23, 2018
Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes #11765.
jelbourn pushed a commit that referenced this issue Aug 25, 2018
Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes #11765.
crisbeto added a commit to crisbeto/material2 that referenced this issue Sep 22, 2018
Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes angular#11765.
crisbeto added a commit to crisbeto/material2 that referenced this issue Oct 8, 2018
Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes angular#11765.
vivian-hu-zz pushed a commit that referenced this issue Nov 6, 2018
Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes #11765.
@aeslinger0
Copy link

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.

@kiwikern
Copy link

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

@thw0rted
Copy link

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.

@virtuoushub
Copy link

@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.

@thw0rted
Copy link

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.

@AshurovRustam
Copy link

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.

@thw0rted
Copy link

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!

@AshurovRustam
Copy link

.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.

@thw0rted
Copy link

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.

@AshurovRustam
Copy link

AshurovRustam commented Dec 17, 2018

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)

crisbeto added a commit to crisbeto/material2 that referenced this issue Jan 5, 2019
Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes angular#11765.
@tfalvo
Copy link

tfalvo commented Feb 11, 2019

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

@stefanmatar
Copy link

stefanmatar commented Feb 11, 2019

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;
    }
  }
}

@simeyla
Copy link

simeyla commented Feb 20, 2019

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?

@am-awais
Copy link

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 :(

@alexsanqp
Copy link

Я решил это временно, пока не пришло исправление с помощью глобального стиля в 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.
<mat-expansion-panel-header [collapsedHeight]="'66px'" [expandedHeight]="'66px'">

@pburkindine
Copy link

pburkindine commented Jul 11, 2019

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.

@thw0rted
Copy link

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.

@am-awais
Copy link

am-awais commented Jul 18, 2019

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

@chipicow
Copy link

chipicow commented Oct 16, 2019

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

crisbeto added a commit to crisbeto/material2 that referenced this issue Jan 1, 2020
Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes angular#11765.
mmalerba pushed a commit that referenced this issue Apr 7, 2020
Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes #11765.
@meblum
Copy link

meblum commented Jul 13, 2020

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

@Diralytic
Copy link

any updates?

@TheParad0X
Copy link

The only workaround that worked for me is:

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

Thanks @chipicow

@ALGDB
Copy link

ALGDB commented Jan 12, 2021

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;
  }

@manuelfuchs
Copy link

@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.

crisbeto added a commit to crisbeto/material2 that referenced this issue May 31, 2021
…ting away

Fixes an issue where the expansion panel will appear as if it is open, if the element is part of a component that is animating away. The issue comes from the fact that Angular resets the animation state to `void` which we don't have in the animation definitions.

Fixes angular#11765.
@kbsh
Copy link

kbsh commented Oct 7, 2021

I solved only this

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

@OlliHolli
Copy link

I've found another workaround that might help a few people.
See my comment here.

@mmalerba mmalerba removed the has pr label Dec 8, 2022
@antoci-alin
Copy link

I have also fixed using:

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

@Vestelth
Copy link

Vestelth commented Oct 20, 2023

similar to above, works for me:

.mat-expansion-panel .mat-expansion-panel-content {
    &.ng-animating {
       height: 0;
    }
}

@Nividica
Copy link

Nividica commented Oct 30, 2023

TL;DR

Changing

  • query(':enter', animateChild(), { optional: true })

to

  • query(':enter', query('@*', animateChild(), { optional: true }), { optional: true })

fixed my issues.


Also ran into this issue today, and stumbled into a solution that seems to fix my issues.

My Setup

Dependencies:

"@angular/core": "16.2.0",
"@angular/material": "16.2.4",

Two router animations

  1. Fade in the first page load.
  2. Slide/Swipe when navigating from any page to any other page.

<mat-expansion-panel [expanded]="false" on the target page.

All components use OnPush change detection.

Issue

For both situations the expansion panel starts as expanded, the page fades/slides in, then a moment later the expansion panel closes.
I also sometimes have to click the header twice to get the panel to expand.

Research

I tried some of the solutions given above, with [@.disabled]="true" being the simplest fix for me, but I was left unsatisfied as it all felt like work-arounds for whatever the root problems was, and I was hoping for a solution that only changed the animation sequence/config.

After spending some time debugging in and out of the animation code and the components, I came to the conclusion that Angular wasn't animating the components on-page until some time after the router animation had finished.
In other words the animation triggers were not getting triggered until the page was done animating and what appeared to be a new change detection cycle had started.

This is obviously undesired, but even more concerning is this doesn't match my expectations, as I have an animateChild() query at the very end of my sequence that I expected to link up the child animations with the route animation.

transition('* => *', [
  // Prep for animation
  group([
    query(':enter', style(...), { optional: true }),
    query(':leave', style(...), { optional: true }),
  ]),
  // Slide in
  group([
    query(':enter', animate(...), { optional: true }),
    query(':leave', animate(...), { optional: true }),
  ]),
  // Animate children
  query(':enter', animateChild(), { optional: true }),
]);

I admit I know very little about Angular animations, this is the first project I have used them, so I thought if I moved the animateChild() higher up in the sequence it would do all the on-page animations first, then slide the page in.
However, no matter where I put the animateChild() the results were the same. Even if it was the first element in the sequence, the animations on-page always ran some time after the route animation had finished.

Stumbling into a solution

What stood out to me at this point was the Child of animateChild(), I had assumed that it animated all descendants not just direct children. Maybe that was a bad assumption?
I didn't find anything like animateChildRecursive() or animateDescendants().

So, is there a way I can target all descendants?
A quick read of the query function, I spotted some potential candidates

`query(":animating")` : Query all currently animating elements.
`query("@*")` : Query all elements that contain an animation triggers.

I tried with query(":animating") and got the same results as before, so I tried query("@*") and unexpectedly everything worked!

Working Solution

In the end my animation sequence ended up being

transition('* => *', [
  // Prep for animation
  group([
    query(':enter', style(...), { optional: true }),
    query(':leave', style(...), { optional: true }),
  ]),
  // Slide in
  group([
    query(':enter', animate(...), { optional: true }),
    query(':leave', animate(...), { optional: true }),
  ]),
  // Animate :enter descendants
query(':enter', query('@*', animateChild(), { optional: true }), { optional: true }),
]);

I think query(':enter', query('@*' is targeting all descendants of the page entering and animating them.

I still had the .ng-animating red border @simeyla suggested from before, and with the fix, the expansion panel no longer has a red border while the page is sliding in.

Final Thoughts

While this did fix all the issues I was having, I still don't fully understand why it fixed them. It seems like Angular was not animating the full descendant tree with animateChild() and I don't know if that was my fault or just a bad expectation.
I hope this helps someone else having similar issues, but I will say I don't know the full scope of the @* selector so it is entirely possible this will trigger animations deep in the page undesirably.

My remaining unanswered questions are

  1. Why query(':enter', animateChild() doesn't seem to make it all the way down to the elements on the page.
    • I assume this is a failure of my understanding of Angular animations, and I have a broken "chain" of animation links
  2. Why does placing query(':enter', query('@*' as the last step in the route animation fix the problem?
    • I would have expected that placing it at the end would yield the same results, but no, the expansion panel is collapsed before the route animation slides the page over.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: material/expansion P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

Successfully merging a pull request may close this issue.