A brief introduction to Angular 10 Animations

Portrait of MartinMartin07-08-2020

A brief introduction into Angular 10 Animations

Angular 10 is here and it's a mayor leap forward in every single aspect of the platform. Let's dive ride in!

Hint: If you know the basics of Angular setup like routing and adding modules, you can skip right to Animations.

Setup

To generate a new angular app you simply type:

ng new <app-name>

This generates a brand new app.

Info: We prepared a fast-forward example repo

Let's run the sample app to check if everything works as expected.

yarn start

and head over to http://localhost:4200/. If everything works fine you should see the new starter page of angular.

Let's examine this starter page of Angular 10 a little and click on the button
+ Angular Material.

ng add @angular/material

Let's execute this statement to add Angular Material.

Material Theme

Adding the material theme is straight forward. We choose the standard theme Pink/Blue Grey. There is a preview for this theme available at https://material.angular.io/?theme=pink-bluegrey

The next questions should be answered with yes and Voila - we even added the BrowserAnimationsModule (which is required for Animations to work in Angular).

After adding the angular material design these files should be changed:

Let's move on and generate a component to isolate our animation experiments ;)

Components

After the successfull setup of the Angular 10 app with material design we need to add a new component to kick-off the animations magic.

ng generate component animations

This adds a new component animations.

Changeset: These files are affected after adding your new component animations.

To prepare everything for animations we need to add the animations module as well (as it does not ship by default).

Let's do this in app.module.ts.

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AnimationsComponent } from './animations/animations.component';

@NgModule({
  declarations: [
    AppComponent,
    AnimationsComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Ok great - everything should work as expected:
We have a component in place (AnimationsComponent) where we actually implement the animations and imported the BrowserAnimationsModule into app.module.ts.

There is one thing we need to do finally to be able to route to the AnimationsComponent.

Routing

The CLI already generated the basic rounting setup for us, so we simply need to tell the app where we want our AnimationsComponent to be reachable.

For this we need to change the app-routing.module.ts file.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AnimationsComponent } from './animations/animations.component';

const routes: Routes = [
  { path: 'animations', component: AnimationsComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

That's it - now we are ready to animate at http://localhost:4200/animations

Hint: The CLI generates a placeholder in app.component.html which is super useful for setup purposes (as it gives hints to adding libraries, routing, etc.). BUT: You need to replace it once the core skeleton is setup to have a clean app.

Animations

To have the animations in our AnimationsComponent available we need to import them:

animations.component.ts:

import { Component, OnInit } from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition,
  // ...
} from '@angular/animations';

@Component({
  selector: 'app-animations',
  templateUrl: './animations.component.html',
  styleUrls: ['./animations.component.scss']
})
export class AnimationsComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}

The most important part of animations is in the metadata property of the component.

So let's add some transitions to the component in

animations.component.ts:

import { Component, OnInit } from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition,
  // ...
} from '@angular/animations';

@Component({
  selector: 'app-animations',
  animations: [
    trigger('openClose', [
      // ...
      state('open', style({
        height: '300px',
        opacity: 1,
        backgroundColor: 'black'
      })),
      state('closed', style({
        height: '100px',
        opacity: 0.5,
        backgroundColor: 'white'
      })),
      transition('open => closed', [
        animate('1s')
      ]),
      transition('closed => open', [
        animate('0.5s')
      ]),
    ]),
  ],
  templateUrl: './animations.component.html',
  styleUrls: ['./animations.component.scss']
})
export class AnimationsComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}

As you might already notice the animations are state based.

In this case we have two states:

  • open
  • closed

To showcase the transitions we defined the two states quite differently:

state open

state('open', style({
        height: '300px',
        opacity: 1,
        backgroundColor: 'black'
      })),

state closed

state('closed', style({
        height: '100px',
        opacity: 0.5,
        backgroundColor: 'white'
      })),

As this is quite "static" we want to add some dynamics to it. This is where transitions come into play:

  • transition from state open to state closed
transition('open => closed', [
        animate('1s')
      ]),
  • transition from state closed to state opened
transition('closed => open', [
        animate('0.5s')
      ]),

As you can see we simply name the transitions and define the duration of the animation as the only thing executed during the transition.

Let's see how we can reference the animation in the template

animations.component.html

<div [@openClose]="isOpen ? 'open' : 'closed'" class="open-close-container">
    <p>The box is now {{ isOpen ? 'Open' : 'Closed' }}!</p>
</div>

<button mat-raised-button (click)="toggle()">Toggle</button>

In the template we define a <div> - element that references the trigger defined previously in the animation part.

Below the <div> - element we added a simple button to trigger the animation by click.

To connect the template with the component we simply need to add the toggle function to our

animation.component.ts:

import { Component, OnInit } from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition,
  // ...
} from '@angular/animations';

@Component({
  selector: 'app-animations',
  animations: [
    trigger('openClose', [
      // ...
      state('open', style({
        height: '300px',
        opacity: 1,
        backgroundColor: 'black'
      })),
      state('closed', style({
        height: '100px',
        opacity: 0.5,
        backgroundColor: 'white'
      })),
      transition('open => closed', [
        animate('1s')
      ]),
      transition('closed => open', [
        animate('0.5s')
      ]),
    ]),
  ],
  templateUrl: './animations.component.html',
  styleUrls: ['./animations.component.scss']
})
export class AnimationsComponent implements OnInit {

  isOpen = true;

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

  constructor() { }

  ngOnInit(): void {
  }

}

Finally add some styling to make the whole example look a little better:

:host {
    display: block;
}

.open-close-container {
    border: 1px solid #dddddd;
    margin-top: 1em;
    padding: 20px 20px 0px 20px;
    color: #4cf4b3;
    font-weight: bold;
    font-size: 20px;
}

And that's it! Head over to http://localhost:4200/animations and click to Toggle button to kick off the smooth animations.

If you experience any troubles do not hesitate to drop us a line on Twitter or Facebook

Thanks for reading and happy animating!