Angular (4, 5, 6, 7) - An easy guide to implementing slide in and out animations using ngIf

How can you implement a basic sliding animation in Angular4 to show and hide a container element?

For example:

<div *ngIf="show">
    <!-- Content -->
</div>

Slide the content in (similar to jQuery's slideDown() method) from top to bottom when show becomes true.

Slide the content out smoothly with an ease-out effect when show becomes false.

Answer №1

First we have some code, followed by an explanation. If you want more details, the official documentation can be found here.

import { trigger, transition, animate, style } from '@angular/animations'

@Component({
  ...
  animations: [
    trigger('slideInOut', [
      transition(':enter', [
        style({transform: 'translateY(-100%)'}),
        animate('200ms ease-in', style({transform: 'translateY(0%)'}))
      ]),
      transition(':leave', [
        animate('200ms ease-in', style({transform: 'translateY(-100%)'}))
      ])
    ])
  ]
})

In your HTML template:

<div *ngIf="visible" [@slideInOut]>When the 'visible' value changes between true and false, this element will smoothly slide up and down.</div>

The angular animation concept may seem complex at first, but once you get the hang of it, it becomes straightforward and powerful.

Breaking down the animation in simpler terms:

  • We're calling this animation 'slideInOut'.

  • For adding the element (:enter), we perform the following actions:

  • ->Initially shift the element 100% upwards (relative to itself) to make it appear off-screen.

  • ->Then gradually animate the translateY value until it reaches 0%, where the element naturally belongs.

  • When removing the element, animate the translateY value (currently at 0) to -100% (off-screen).

Our chosen easing function is ease-in, taking 200 milliseconds – feel free to alter this according to your preferences.

Answer №2

I provided a solution to a similar question previously, and here is one way to achieve it:

Start by creating a file where you define your animations and export them for clarity in your app.component.ts

In the example below, I set the max-height of the div to transition from 0px (when hidden) to 500px, but adjust as needed.

This animation utilizes states (in and out) which toggle upon clicking a button triggering the animation.

animations.ts

import { trigger, state, style, transition,
    animate, group, query, stagger, keyframes
} from '@angular/animations';

export const SlideInOutAnimation = [
    trigger('slideInOut', [
        state('in', style({
            'max-height': '500px', 'opacity': '1', 'visibility': 'visible'
        })),
        state('out', style({
            'max-height': '0px', 'opacity': '0', 'visibility': 'hidden'
        })),
        transition('in => out', [group([
            animate('400ms ease-in-out', style({
                'opacity': '0'
            })),
            animate('600ms ease-in-out', style({
                'max-height': '0px'
            })),
            animate('700ms ease-in-out', style({
                'visibility': 'hidden'
            }))
        ]
        )]),
        transition('out => in', [group([
            animate('1ms ease-in-out', style({
                'visibility': 'visible'
            })),
            animate('600ms ease-in-out', style({
                'max-height': '500px'
            })),
            animate('800ms ease-in-out', style({
                'opacity': '1'
            }))
        ]
        )])
    ]),
]

In your app.component, import the above animation and create a method toggling the animation state.

app.component.ts

import { SlideInOutAnimation } from './animations';

@Component({
  ...
  animations: [SlideInOutAnimation]
})
export class AppComponent  {
  animationState = 'in';

  ...

  toggleShowDiv(divName: string) {
    if (divName === 'divA') {
      console.log(this.animationState);
      this.animationState = this.animationState === 'out' ? 'in' : 'out';
      console.log(this.animationState);
    }
  }
}

Your app.component.html should resemble the following:

<div class="wrapper">
  <button (click)="toggleShowDiv('divA')">TOGGLE DIV</button>
  <div [@slideInOut]="animationState" style="height: 100px; background-color: red;">
  THIS DIV IS ANIMATED</div>
  <div class="content">THIS IS CONTENT DIV</div>
</div>

slideInOut refers to the animation trigger specified in animations.ts

For reference, here is a StackBlitz demonstration:

To address any potential errors prompting you to add BrowserAnimationsModule, simply import it in your app.module.ts:

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@NgModule({
  imports: [ ..., BrowserAnimationsModule ],
  ...
})

Answer №3

In response to the initial query, it is actually possible to achieve the desired outcome in Angular by simply adding a class to the DOM element when the show variable is true and handling the animation/transition through CSS.

The bare minimum Angular code required for this is as follows:

<div class="box-opener" (click)="show = !show">
    Open/close the box
</div>

<div class="box" [class.opened]="show">
    <!-- Content -->
</div>

To make this solution work, you will need to define CSS rules for the transition like so:

.box {
    background-color: #FFCC55;
    max-height: 0px;
    overflow-y: hidden;
    transition: ease-in-out 400ms max-height;
}

.box.opened {
    max-height: 500px;
    transition: ease-in-out 600ms max-height;
}

If you encounter compatibility issues with older browsers, don't forget to include vendor prefixes in the transition properties.

For an example demonstration, check out this link

Answer №4

The top-voted solution does not incorporate a genuine slide in/out effect, for the following reasons:

  1. There is no smooth transition for the height attribute. The element starts with 100% of its height at time zero, causing a sudden jump in the elements below.
  2. During sliding out/up, the element uses translateY(-100%) and then abruptly vanishes, creating another disruption in the elements below it.

To achieve a proper slide in and slide out, consider the following implementation:

my-component.ts

import { animate, style, transition, trigger } from '@angular/animations';

@Component({
  ...
  animations: [
    trigger('slideDownUp', [
      transition(':enter', [style({ height: 0 }), animate(500)]),
      transition(':leave', [animate(500, style({ height: 0 }))]),
    ]),
  ],
})

my-component.html

<div @slideDownUp *ngIf="isShowing" class="box">
  I am the content of the div!
</div>

my-component.scss

.box {
  overflow: hidden;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to change the number 123456789 to look like 123***789 using either typescript or

Is there a way to convert this scenario? I am working on a project where the userID needs to be displayed in a specific format. The first 3 characters/numbers and last 3 characters/numbers should be visible, while the middle part should be replaced with ...

Steps for storing API information in localStorage:1. Retrieve the API data

My app is running sluggish due to the excessive API calls for information retrieval. To optimize performance, I want to create a unified object containing all the data that can be shared across pages and accessed from localStorage, thus enhancing the app ...

Experiencing a specific build error on cloud build that does not occur during a docker build process

Challenges with gcloud Build Whenever I try to submit a build using gcloud, I encounter an error. Oddly enough, the build works perfectly fine on my local machine and even when creating a docker image locally. Despite my initial assumption that a file mig ...

What sets apart :host::ng-deep .class from .class :host::ng-deep?

Can you explain the distinction between the following two lines of code in scss, Provide some sample snippets as examples. :host::ng-deep .content-body { ... } and .content-body :host::ng-deep { ... } ...

Encountering TS1204 error on version 1.5.0-beta with ES6 target, yet all functionalities are running smoothly

After successfully compiling everything from Typescript to ES6 to ES5, I encountered an error that has me stumped. The error message reads as follows: Error TS1204: Cannot compile external modules into amd or commonjs when targeting es6 or higher. Here i ...

Best practices for applying the Repository pattern within a NestJS application

After reviewing the NestJS documentation and examining their sample source codes, it appears challenging to implement a Repository pattern between the service layer and the database layer (e.g. MongoDB). In NestJS, database operations are executed directl ...

Typescript excels at gracefully handling cases where an element is not found

While working with Typescript-Protractor Jasmine, I encountered an issue where the test case (the 'it' block) is not failing when an element is not found. Instead, it shows an UnhandledPromiseRejectionWarning but still marks the script as passed. ...

What are the appropriate scenarios for extending and utilizing an abstract class in Angular?

@Component({ selector: 'my-component', template: `<ng-content></ng-content>`, providers: [ { provide: AbstractClass, useExisting: forwardRef(() => TargetComponent) } ] }) export class TargetComponent extends AbstractCla ...

Tips for efficiently utilizing mapActions in vue with Typescript class components!

Can someone please provide guidance on the correct way to use ...mapActions([]) within a Typescript vue class component? This is my current approach: <script lang="ts"> import { Component, Prop, Vue } from "vue-property-decorator"; import { mapActi ...

One way to ensure a necessary check on the mat-expansion-panel

Currently, I am working on a form that includes an upload panel (mat-expansion-panel) for uploading documents. My goal is to indicate that this panel is required by adding an asterisk (*) next to it. While I know how to add the asterisk to <textarea&g ...

Node/Angular application facing a breach in content security

After working on a website built with node/express/angular/javascript, I returned to the project months later only to find that I couldn't display any page of the application when running it locally on node js. The error message "cannot get "{webpage} ...

How can Typescript help enhance the readability of optional React prop types?

When working with React, it is common practice to use null to indicate that a prop is optional: function Foo({ count = null }) {} The TypeScript type for this scenario would be: function Foo({ count = null }: { count: number | null }): ReactElement {} Wh ...

What causes this conditional type to function correctly in a static context while failing in a dynamic setting

I have created a unique conditional type that accurately generates a union of valid array indices: type ArrayIndices< N extends any[], Acc extends number[] = [] > = Acc['length'] extends N['length'] ? Acc[number] : ArrayIn ...

Is it feasible to pre-render an Angular2 view invisibly when hovering?

Can a view be preloaded and prerendered invisibly upon hover? I have an application that displays a list of items. Each item is its own component, and when you click on any of these items, it is replaced by a detailed view (another component) of the same ...

Angular 2 - Dependency Injection failing to function

I have created two different implementations for an interface and assigned them as providers for two separate components. However, I am encountering the following error: Error: Can't resolve all parameters for ChildComponent: (?). What could be the i ...

Angular 7 ng-select validation with required form control

Currently, I am utilizing the ng-select plugin for a dropdown search feature, but I am encountering issues with validation when a selection is not made from the dropdown menu. Here is how I have implemented it: <div class="form-group"> <ng-sel ...

After executing "npm run dev" in Svelte and Vite, a common error message of "HTMLElement is not defined" might appear

Incorporating several web components into my Svelte project led to the appearance of an error message stating HTMLElement is not defined after running npm run dev (which actually translates to vite dev). The complete error message reads as follows: HTMLEl ...

The primary route module is automatically loaded alongside all other modules

I have configured my lazy loaded Home module to have an empty path. However, the issue I am facing is that whenever I try to load different modules such as login using its URL like /new/auth, the home module also gets loaded along with it. const routes: R ...

Having trouble with a single GET request not functioning properly on Safari due to an Authorization issue in Angular 6

I've encountered this issue in several locations, yet haven't found a clear solution. Only one GET request is showing as unauthorized (401), but when I check the debugger, everything seems to be fine and all other requests are functioning properl ...

Having trouble retrieving the value of an HTML input field using form.value in Angular 5?

I am currently working with Angular 5 Within my HTML, I am dynamically populating the value of an input field using: <input type="number" class="form-control" id="unitCost" name="unitCost" [(ngModel)]="unitCost" placeholder="Average Unit Price"> ...