I've successfully gotten a panel to animate expanding and closing when entering and leaving the DOM. The problem is I now have a busy indicator inside the panel prior to showing details, and the animation only occurs for opening the busy indicator, and snaps when the detail content is shown.
How can I get the Angular animation to animate on any height change?
I have an example here: https://stackblitz.com/edit/angular-animation-for-dynamically-changing-height?embed=1&file=src/app/app.component.ts
trigger('expandCollapseDetails', [ state('void', style({ 'height': '0px', overflow: 'hidden' })), //element being added into DOM. transition(':enter', [ animate('500ms ease-in-out', style({ 'height': '*', overflow: 'hidden' })) ]), //element being removed from DOM. transition(':leave', [ animate('500ms ease-in-out', style({ 'height': '0px', overflow: 'hidden' })) ]) ])
I've written a component that smoothly animates the height of projected content if that content changes. It's used like this:
<smooth-height [trigger]="content"> {{content}} </smooth-height>
Here's a stackblitz: https://stackblitz.com/edit/angular4-kugxw7
This is the component:
import {ElementRef, HostBinding, Component, Input, OnChanges} from '@angular/core'; import {animate, style, transition, trigger} from "@angular/animations"; @Component({ selector: 'smooth-height', template: ` <ng-content></ng-content> `, styles: [` :host { display: block; overflow: hidden; } `], animations: [ trigger('grow', [ transition('void <=> *', []), transition('* <=> *', [ style({height: '{{startHeight}}px', opacity: 0}), animate('.5s ease'), ], {params: {startHeight: 0}}) ]) ] }) export class SmoothHeightComponent implements OnChanges { @Input() trigger: any; startHeight: number; @HostBinding('@grow') grow: any; constructor(private element: ElementRef) {} ngOnChanges(){ this.startHeight = this.element.nativeElement.clientHeight; this.grow = { value: this.trigger, params: {startHeight: this.startHeight} }; } }
I made a directive based on @MartinCremer answer. I think using a directive makes more sense since by doing that, you also should add the animation to your parent component (and it's close the standard way of adding animations).
So inside my animations.ts
file. I've added the animation:
export const smoothHeight = trigger('grow', [ transition('void <=> *', []), transition('* <=> *', [style({ height: '{{startHeight}}px', opacity: 0 }), animate('.5s ease')], { params: { startHeight: 0 } }) ]);
then you should add this animation to your parent component (the component that you want to use the animation inside it):
import { smoothHeight } from '@app/animations'; @Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrls: ['./parent.component.scss'], animations: [smoothHeight] })
And here is the directive which is really close to the component of @MartinCremer:
import { Directive, OnChanges, Input, HostBinding, ElementRef } from '@angular/core'; @Directive({ selector: '[smoothHeight]', host: { '[style.display]': '"block"', '[style.overflow]': '"hidden"' } }) export class SmoothHeightAnimDirective implements OnChanges { @Input() smoothHeight; pulse: boolean; startHeight: number; constructor(private element: ElementRef) {} @HostBinding('@grow') get grow() { return { value: this.pulse, params: { startHeight: this.startHeight } }; } setStartHeight() { this.startHeight = this.element.nativeElement.clientHeight; } ngOnChanges(changes) { this.setStartHeight(); this.pulse = !this.pulse; } }
Finally inside parent.component.html
use the directive:
<div [smoothHeight]="yourAnimationIndicator"> // any html content goes here </div>
Just replace yourAnimationIndicator
with the variable that the animation should trigger on change of its value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With