Executing Multiple Angular2 Animations Concurrently: A Guide

I am facing a challenge with a dynamic container that expands and shrinks. Inside this container, there is an element that should fade in when the container expands and fade out when it shrinks.

The Issue I'm Facing

  • When the container expands, both element animations work smoothly.
  • However, when the container shrinks, only the container animation is triggered.
  • If I eliminate the container expansion animation, then the fade in/out animations function as intended.

How can I ensure that all animations run simultaneously in both expand and shrink scenarios?

Note: I am deliberately using ngIf, as it removes the element at the end of the animation sequence.

Check out the current state of the project on Plunkr: https://embed.plnkr.co/TXYoGf9QpErWmlRvQF9Z/

The component class:

export class App {
  expanded = true;

  toggleExpandedState() {
    this.expanded = !this.expanded;
  }

  constructor() {
  }
}

The template:

<div class="container" [@expansionTrigger]="expanded">
  <div class="constant-item"></div>
  <div class="fade-item" [@stateAnimation] *ngIf="expanded"></div>
</div>

<button (click)="toggleExpandedState()">Toggle Fade</button>

And here is the component animation code:

  trigger('expansionTrigger', [
    state('1', style({
      width: '250px'
    })),
    state('0', style({
      width: '160px'
    })),
    transition('0 => 1', animate('200ms ease-in')),
    transition('1 => 0', animate('200ms ease-out'))
  ]),
  trigger('stateAnimation', [
    transition(':enter', [
      style({
        opacity: 0
      }),
      animate('200ms 350ms ease-in', style({
        opacity: 1
      }))
    ]),
    transition(':leave', [
      style({
        opacity: 1
      }),
      animate('1s', style({
        opacity: 0
      }))
    ])
  ])

Answer №1

It seems that there is a bug with Angular 4 that may be causing frustration for some users.

There are currently a couple of unresolved issues on Github that seem to be closely related:

https://github.com/angular/angular/issues/15798

https://github.com/angular/angular/issues/15753

Update:

Based on the github discussions, it appears that the animation bug in Angular 4 will not be fixed to function the same way as it did in Angular 2. Instead, new features like query() and animateChild() are being introduced. These new animation capabilities are now available as of version 4.2.0-rc2.

To summarize:

Component Definition:

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <button (click)="toggle()">Toggle</button>
      <div *ngIf="show" @ngIfAnimation>
        <h3 @easeInOut>I'm inside ngIf</h3>
      </div>
    </div>
  `,
animations: [
  trigger('ngIfAnimation', [
    transition(':enter, :leave', [
      query('@*', animateChild())
    ])
  ]),
  trigger('easeInOut', [
    transition('void => *', [
        style({
            opacity: 0
        }),
        animate("1s ease-in-out", style({
            opacity: 1
        }))
    ]),
    transition('* => void', [
        style({
            opacity: 1
        }),
        animate("1s ease-in-out", style({
            opacity: 0
        }))
    ])
  ])
]
})

export class App {
  name:string;
  show:boolean = false;

  constructor() {
    this.name = `Angular! v${VERSION.full}`
  }

  toggle() {
    this.show = !this.show;
  }
}

You can find a demo of this working example here: https://embed.plnkr.co/RJgAunJfibBKNFt0DCZS/

Answer №2

Follow these steps to make the necessary changes:

Visit this Plnkr for reference: https://plnkr.co/edit/AQEVh3GGc31ivQZMp223?p=preview

Firstly, remove the *ngIf="expanded" and replace it with [@stateAnimation]="expanded"

Next, update your stateAnimate trigger with the following code:

trigger('stateAnimation', [
          state('1', style({ opacity: 0 })),
          state('0', style({opacity: 1})),
          transition('0 => 1', animate('200ms ease-in')),
          transition('1 => 0', animate('200ms ease-out'))
        ])

Here is the complete code snippet for your reference:

//Root app component
import {Component, NgModule, VERSION, 
  Output,
  trigger,
  state,
  style,
  transition,
  animate,
} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@Component({
  selector: 'my-app',
  template: `
    <div class="container" [@expansionTrigger]="expanded">
      <div class="constant-item"></div>
      <div class="fade-item" [@stateAnimation]="expanded"></div>
    </div>

    <button (click)="toggleExpandedState()">Toggle Fade</button>
  `,
  animations: [
    trigger('expansionTrigger', [
      state('1', style({ width: '250px' })),
      state('0', style({width:'160px'})),
      transition('0 => 1', animate('200ms ease-in')),
      transition('1 => 0', animate('200ms ease-out'))
    ]),
    trigger('stateAnimation', [
      state('1', style({ opacity: 0 })),
      state('0', style({opacity: 1})),
      transition('0 => 1', animate('200ms ease-in')),
      transition('1 => 0', animate('200ms ease-out'))
    ])
  ]
})
export class App {
  expanded = true;

  toggleExpandedState() {
    this.expanded = !this.expanded;
  }

  constructor() {
  }
}

@NgModule({
  imports: [ BrowserModule, BrowserAnimationsModule ],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}

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

Retrieving the attribute key from a dynamically typed object

Having this specific interface structure: interface test { [key: string]: string } along with an object defined as follows: const obj: test ={ name: 'mda', telephone: '1234' } Attempting to utilize this object in a variab ...

Absence of property persists despite the use of null coalescing and optional chaining

Having some trouble with a piece of code that utilizes optional chaining and null coalescing. Despite this, I am confused as to why it is still flagging an error about the property not existing. See image below for more details: The error message display ...

Ensuring Type Compatibility Between Classes and Object Literals in TypeScript

When working with TypeScript, it is important to note that an object literal can be assigned to a class typed variable as long as the object provides all properties and methods required by the class. class MyClass { a: number; b: string; } // The co ...

Arranging the output of a Typescript project

I'm currently advocating for Typescript to be implemented in our web application. Can anyone provide insight on the best method for structuring Javascript output files? Should they be excluded from source control? And when it comes to testing, is it p ...

What is causing these TypeScript type assertions to go unnoticed?

While reviewing type assertions, I noticed something interesting about the last three variable assignments - they don't produce errors. It's perplexing because I thought I was trying to change 'helo' into 'hello', which should ...

SharePoint Online / Angular - Issue: Unhandled Error: Zone is already loaded

I recently completed a project in Angular and integrated it into a SharePoint page using the Content Editor. Everything was running smoothly until yesterday, when I encountered an error while loading the page. zone-evergreen.js:42 Uncaught Error: Zone alre ...

Error in JavaScript: A surprise anonymous System.register call occurred

Within Visual Studio 2015, there exists a TypeScript project featuring two distinct TypeScript files: foo.ts export class Foo { bar(): string { return "hello"; } } app.ts /// <reference path="foo.ts"/> import {Foo} from './f ...

angular2 with selenium webdriver: Issue with resolving 'child_process' conflict

I followed these steps: ng new typescript-selenium-example npm install selenium-webdriver --save I also made sure to copy chromedriver to my /Application. I updated the app.component.ts file as shown below: import { Component } from '@angular/core ...

update component following an ajax request

I attempted the suggested solutions for similar questions, however, they proved ineffective. Upon inserting data into my database, I am looking to update my component (with the user remaining on the same component). I tried navigating directly to the com ...

Implementing error wrapper in typescript to handle failing promises with n retries

I have a wrapper function that calls an async function: function fetchAPI(arg1: number, arg2: number, arg3: string) { return new Promise((resolve, reject) => { try { myFetchFunction(arg1, arg2, arg3).then((r: any) => { if (!r) { ...

Indicate the location of tsconfig.json file when setting up Cypress

Having trouble integrating Cypress with Typescript? I've encountered an issue where Cypress is unable to locate the tsconfig.json file I created for it. My preference is to organize my project with a custom directory structure, keeping configuration f ...

Steps for adding an input box to an md-menu item

I'm looking for an option that allows me to either add an item to an existing list or create a new one, similar to YouTube's 'Add to playlist' feature. The current implementation sort of works, but the menu disappears as soon as the inp ...

Bundling JSPM with an external system JS file

Currently, I am loading my Angular2 file as a System js module from a CDN. Within my project, I have multiple files importing various System js modules of Angular2. However, I am now looking to bundle my local JavaScript files using JSPM. But when I ente ...

What could be causing the inability to update a newly logged-in user without refreshing the page?

Hello, I have encountered an issue with my application that involves registration and login functionality. The problem arises when a new user logs in, as I must refresh the page to get the current user information. I am currently using interpolation on the ...

What could be causing the Toast message to not show up in react-native-root-toast?

Incorporated react-native-root-toast into my expo project running on expo 51. Please see the code snippet below for reference: const toastColors = { 'error': { color: '#DA5C53', iconName: <WarningIcon size="5 ...

What is the best way to retrieve the value from a Material UI textfield after hitting the enter key

Having trouble retrieving input values with the provided code. Attempted using onKeyUp, onKeyDown, and onKeyPress, but none of them returned the value as desired. Typically, I would use the onChange property to get the value, but it triggers for every ne ...

Issue with secondary router outlet nested within primary router outlet not functioning properly

I've set up my router outlets like this, with the secondary router outlet nested inside the primary one. However, when I attempt to use routerLink to display the roster.component.html, I keep encountering the same error message no matter what configur ...

Encountering an issue with TypeScript and Jest when trying to import a file leads to an

Having trouble with using Jest in a TypeScript environment. //myprovider.tsx class MyProvider{ constructor(){} giveMeFive(): int{ return 5; } } export { MyProvider } // myprovider.test.js import { MyProvider } from './myprovider'; ...

Executing TypeScript Mocha test cases using ES6 modules

Setting up mocha tests for the TypeScript App in my Rails application has been a bit of a challenge. Initially, I added a basic test to kick things off, but encountered the following error: /home/bernhard/Programs/ruby/cube_trainer/jstests/utils/optional. ...

Primeng - Concealed dropdown values within a scrollable table header

Recently, I integrated Primeng p-table with a filter and frozen column feature (with one column being fixed while the others are movable). However, I encountered an issue with the select dropdown in the header. When I try to open the dropdown, the values a ...