I recently built the following Angular 2 Read More component. What this component does is collapse and expand long blocks of text with "Read more" and "Read Less" links. Not on the basis of the character count but on the basis of the specified max 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 like:
<read-more [text]="details" [maxHeight]="250"></read-more>
The component works well. Now I need to add some slide up/down animation to the component so that when Read More link is clicked the contents slide down and when Read less is clicked, the contents slide up to specified max height.
Can anyone please guide how to achieve this?
The BrowserAnimationsModule must be imported in the main module of the application app. module. ts and after importing, it is necessary to restart the application so that the imported module is recognized.
What Does ngAnimate Do? The ngAnimate module adds and removes classes. The ngAnimate module does not animate your HTML elements, but when ngAnimate notice certain events, like hide or show of an HTML element, the element gets some pre-defined classes which can be used to make animations.
The keyframes() function in Angular allows you to specify multiple interim styles within a single transition, with an optional offset to define the point in the animation where each style change should occur.
My Solution with :enter
, :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 had to incorporate this fix also (for slideOut): https://github.com/angular/angular/issues/15798
This is what I use in Angular 8.1.2. The beauty of the code is that it supports unlimited height of the div that needs to be shown/collapsed and also makes smooth transitions.
TS FILE:
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>
Automatic property calculation
Animation with automated height calculation
Sometimes you don't know the value of a dimensional style property until runtime. For example, elements often have widths and heights that depend on their content and the screen size. These properties are often tricky to animate with CSS.
In these cases, you can use a special * property value so that the value of the property is computed at runtime and then plugged into the animation.
In this example, the leave animation takes whatever height the element has before it leaves and animates from that height to zero :
animations: [ trigger('shrinkOut', [ state('in', style({height: '*'})), transition('* => void', [ style({height: '*'}), animate(250, style({height: 0})) ]) ]) ]
from Angular official documentation (now as an archive) : https://v2.angular.io/docs/ts/latest/guide/animations.html#!#automatic-property-calculation
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