0

We have a very simple component that should be faded in and out on :enter and :leave events:

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

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.scss'],
  animations: [
    trigger('host', [
      transition(':enter', [
        style({ opacity: 0 }),
        animate('240ms linear', style({ opacity: 1 })),
      ]),
      transition(':leave', [
        style({ opacity: 1 }),
        animate('240ms linear', style({ opacity: 0 })),
      ])
    ]),
  ]
})

export class TestComponent {
  @HostBinding('@host') host() {
    return true;
  }
}

The component is used like this:

<app-test *ngIf="someBoolean"></app-test>

Now the animation on :enter does run. But the element is not faded out, when the component is being removed (the variable someBoolean becomes false).

What is missing?

lampshade
  • 2,470
  • 3
  • 36
  • 74

2 Answers2

1

Using *ngIf in the parent component makes the element animations not to be registered

Lets follow what happens

  • someBoolean => starting with false
  • <app-test *ngIf='someBoolean'></app-test> does not exist
  • someBoolean => true
  • Load <app-test></app-test>
  • Load animations; ... Wait a minute, what just happened? We loaded app-test then loaded the animations! But at what point is animations to be triggered? The answer to this question is why your animations are not being triggered. We need to reqister the animations before they can be applied

So Lets stop the above sequence of events and retry the steps

  • someBoolean => starting with false
  • <app-test [shown]='someBoolean'></app-test> created
  • Register Animations
  • someBoolean => true
  • Display contents of <app-test></app-test>
  • Animation kicks in as they have been registered!

Okay, now that we have an idea of how this would work lets get down to code

Component usage

`<app-test [shown]='someBoolean'></app-test>`

Component TS File

@Component({
  selector: "app-test",
  templateUrl: "./test.component.html",
  styleUrls: ["./test.component.css"],
  animations: [
    trigger("host", [
      transition(":enter", [
        style({ opacity: 0 }),
        animate("240ms linear", style({ opacity: 1 }))
      ]),
      transition(":leave", [
        style({ opacity: 1 }),
        animate("240ms linear", style({ opacity: 0 }))
      ])
    ])
  ]
})
export class TestComponent implements OnInit {
  // @HostBinding("@host") host() {
  //   return true;
  // }
  @Input() shown;

  constructor() {}

  ngOnInit() {}
}

I have commented out @HostBinding as I didn't see how it is implemented here.

Finally we can apply *ngIf in the contents of the component

<div *ngIf='shown'>
  <!-- Animation works! -->
</div>

See Demo Here

Owen Kelvin
  • 14,054
  • 10
  • 41
  • 74
  • Thanks a lot for your answer. While this works and is pretty neat, it is different from what I want to achieve - if possible. I want to animate the `:host` -element (``. This element should not be present if `someBoolean` is `false` but it should fade in and out, if the variable changes. I don't want to animate a child element. I hope there's a solution for that also. – lampshade May 04 '21 at 11:52
  • In that case you may need to move the animation to the parent host. If the element does not exist then the animation registered in that element has not been registered yet so there is no way an animation not registered will trigger – Owen Kelvin May 04 '21 at 12:03
0

Does this solve the issue?

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

@Component({
  selector: 'parent',
  animations: [
    trigger('toggle', [
      state('true', style({ overflow: 'hidden', opacity: '*' })),
      state('false', style({ overflow: 'hidden', opacity: 0, height: 0 })),
      transition('true => false', animate('400ms ease-in-out')),
      transition('false => true', animate('400ms ease-in-out')),
    ]),
  ],
  template: `
    <button (click)="someBoolean = !someBoolean">CLICK</button>
    <div [@toggle]="someBoolean">
      <app-test></app-test>
    </div>
  `,
})
export class ParentComponent {
  someBoolean = false;
}
David Lopes
  • 227
  • 2
  • 10