Angular 2 Slide Up and Animation

I recently built the following Angular 2 Read More component. What this component does is collapse and expand long blocks of text using the Read More and Read Less links. Not based on the number of characters, but based on the specified maximum height.

import { Component, Input, ElementRef, AfterViewInit } from '@angular/core'; @Component({ selector: 'read-more', template: ` <div [innerHTML]="text" [class.collapsed]="isCollapsed" [style.height]="isCollapsed ? maxHeight+'px' : 'auto'"> </div> <a *ngIf="isCollapsable" (click)="isCollapsed =! isCollapsed">Read {{isCollapsed? 'more':'less'}}</a> `, styles: [` div.collapsed { overflow: hidden; } `] }) export class ReadMoreComponent implements AfterViewInit { //the text that need to be put in the container @Input() text: string; //maximum height of the container @Input() maxHeight: number = 100; //set these to false to get the height of the expended container public isCollapsed: boolean = false; public isCollapsable: boolean = false; constructor(private elementRef: ElementRef) { } ngAfterViewInit() { let currentHeight = this.elementRef.nativeElement.getElementsByTagName('div')[0].offsetHeight; //collapsable only if the contents make container exceed the max height if (currentHeight > this.maxHeight) { this.isCollapsed = true; this.isCollapsable = true; } } } 

And used as:

 <read-more [text]="details" [maxHeight]="250"></read-more> 

The component works well. Now I need to add the slide animation up / down to the component so that when I click on the โ€œRead moreโ€ link, the content goes down, and when I click the โ€œRead lessโ€ button, the content slides to the specified maximum height.

Can anyone explain how to achieve this?

+9
angular typescript angular2-template angular2-directives
source share
4 answers

Automatic property calculation

Animation with automatic height calculation

Sometimes you donโ€™t know the value of a dimension style property before runtime. For example, elements often have a width and height, which depends on their content and screen size. These properties are often difficult to animate with CSS.

In these cases, you can use the special value * of the property so that the value of the property is computed at runtime and then connected to the animation.

In this example, the exit animation takes any element height before it leaves and animate from this height to zero:

 animations: [ trigger('shrinkOut', [ state('in', style({height: '*'})), transition('* => void', [ style({height: '*'}), animate(250, style({height: 0})) ]) ]) ] 

From the official Angular documentation (now in archive form): https://v2.angular.io/docs/ts/latest/guide/animations.html#!#automatic-property-calculation

+14
source share

My solution with :enter *ngIf :leave and *ngIf :

 @Component({ selector: 'accordion', templateUrl: './accordion.component.html', animations: [ trigger('slideInOut', [ state('in', style({height: '*', opacity: 0})), transition(':leave', [ style({height: '*', opacity: 1}), group([ animate(300, style({height: 0})), animate('200ms ease-in-out', style({'opacity': '0'})) ]) ]), transition(':enter', [ style({height: '0', opacity: 0}), group([ animate(300, style({height: '*'})), animate('400ms ease-in-out', style({'opacity': '1'})) ]) ]) ]) ] }) ... 

Template:

 <div *ngIf="shown" [@slideInOut] > // ...content </div> 

Unfortunately, I also had to enable this fix (for slideOut): https://github.com/angular/angular/issues/15798

+14
source share

The alarming answer is correct - the only problem is that the CSS transition will not work with 'auto'. Therefore, you need to fix the auto-height in the ngAfterViewInit function and save it as a string. Also note the use of the setTimeout function to stop the "unidirectional data flow violation error" that may occur.

 import { Component, Input, ElementRef, AfterViewInit } from '@angular/core'; @Component({ selector: 'read-more', template: ` <div [style.height]="isCollapsed ? maxHeight+'px' : autoHeight"> <ng-content></ng-content> </div> <span *ngIf="isCollapsable" class="btn-link cpointer" (click)="isCollapsed =! isCollapsed">Read {{isCollapsed? 'more':'less'}} ...</span> `, styles: [` div { overflow-y: hidden; -moz-transition: height .5s; -ms-transition: height .5s; -o-transition: height .5s; -webkit-transition: height .5s; transition: height .5s; ease;} .cpointer {cursor:pointer; } `] }) export class ReadMoreComponent implements AfterViewInit { @Input() maxHeight: number = 40; //two lines ////set these to false to get the height of the expended container isCollapsed: boolean = false; isCollapsable: boolean = false; autoHeight: string= "auto"; constructor(private elementRef: ElementRef) {} ngAfterViewInit() { // Inportant !! // wait a tick to avoid one-time devMode // unidirectional-data-flow-violation error setTimeout(_ => { let currentHeight = this.elementRef.nativeElement.getElementsByTagName('div')[0].offsetHeight; this.autoHeight = currentHeight + "px"; //collapsable only if the contents make container exceed the max height if (currentHeight >= this.maxHeight) { this.isCollapsed = true; this.isCollapsable = true; } } ); } } 
+2
source share

This is what I use in Angular 8.1.2. The beauty of the code is that it supports the unlimited height of the div element to show / collapse, and also makes smooth transitions.

FILE TS:

 import {Component, OnInit} from '@angular/core'; import {trigger, transition, animate, style, state} from '@angular/animations'; @Component({ selector: 'app-all-data', templateUrl: './all-data.page.html', styleUrls: ['./all-data.page.scss'], animations: [ trigger('openClose', [ state('open', style({ height: '*', opacity: 1, })), state('closed', style({ height: '0', opacity: 0 })), transition('open => closed', [ animate('0.35s') ]), transition('closed => open', [ animate('0.35s') ]), ]), ] }) export class AllDataPage implements OnInit { showCardBody = false; constructor() { } ngOnInit() { } /** * Toggle details on click */ showDetails() { this.showCardBody = !this.showCardBody; } } 

HTML file:

 <button type="button" (click)="showDetails()"> Toggle Details </button> <div class="card-body" [@openClose]="showCardBody ? 'open' : 'closed'"> <p>This is some content</p> <p>This is some content</p> <p>This is some content</p> <p>This is some content</p> <p>This is some content</p> </div> 
+1
source share

All Articles