0

I am trying to remove an element from an Observable array and utilize an exit animation. The enter animation works perfectly, however the exit animation simply doesn't fire and the element is removed suddenly. I've included my code below. I appreciate any help or suggestions!

ao-notifications.service.ts

import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject, timer } from 'rxjs';
import * as _ from 'lodash';

import Message from '../dependencies/Message';
import MessageOptions from '../dependencies/MessageOptions';

@Injectable({
  providedIn: 'root'
})
export class AoNotificationsService {
  id: BehaviorSubject<number> = new BehaviorSubject<number>(0);
  messages: BehaviorSubject<Message[]> = new BehaviorSubject<Message[]>([]);

  list(): Observable<Message[]> {
    return this.messages;
  }

  emit({ content, style, delay }: MessageOptions) {
    const id = this.id.getValue();
    const message = new Message(id, content, style);

    this.messages.next(_.sortBy(_.concat(this.messages.getValue(), message), (o: Message) => o.id).reverse());

    if (delay) {
      timer(delay).subscribe(() => this.purge(id));
    }

    this.id.next(id + 1);
  }

  purge(id: number) {
    this.messages.next(_.filter(this.messages.getValue(), o => o.id !== id));
  }
}

ao-notifications.component.html

<div class="wrapper">
  <aside *ngFor="let message of (messages | async)">
    <div
      @messageState
      class="notification"
      [ngClass]="{
        primary: message.style == 'primary',
        secondary: message.style == 'secondary',
        success: message.style == 'success',
        danger: message.style == 'danger',
        warning: message.style == 'warning',
        info: message.style == 'info'
      }"
    >
      <button class="dismiss" (click)="dismiss(message.id)">X</button> {{ message.content }}
    </div>
  </aside>
</div>

ao-notifications.component.ts

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

import Message from './dependencies/Message';

import { AoNotificationsService } from './services/ao-notifications.service';

@Component({
  selector: 'ao-notifications',
  templateUrl: './ao-notifications.component.html',
  styleUrls: ['./ao-notifications.component.scss'],
  animations: [
    trigger('messageState', [
      transition('void => *', [
        style({
          opacity: 0
        }),
        animate(
          '1s ease-in-out',
          style({
            opacity: 1
          })
        )
      ]),
      transition('* => void', [
        animate(
          '1s ease-in-out',
          style({
            opacity: 0
          })
        )
      ])
    ])
  ]
})
export class AoNotificationsComponent implements OnInit {
  messages: Observable<Message[]>;

  constructor(private notifications: AoNotificationsService) {}

  ngOnInit() {
    this.messages = this.notifications.list();
  }

  dismiss(id: number) {
    this.notifications.purge(id);
  }
}
pelham
  • 128
  • 1
  • 10

1 Answers1

1

The issue was due to the fact that the *ngFor was on the aside, rather than on the div calling the @messageState. If it is modified like below, the animations work properly.

<div class="wrapper">
  <aside>
    <div
      *ngFor="let message of (messages | async)" <!-- Fixed Here -->
      @messageState
      class="notification"
      [ngClass]="{
        primary: message.style == 'primary',
        secondary: message.style == 'secondary',
        success: message.style == 'success',
        danger: message.style == 'danger',
        warning: message.style == 'warning',
        info: message.style == 'info'
      }"
    >
      <button class="dismiss" (click)="dismiss(message.id)">X</button> {{ message.content }}
    </div>
  </aside>
</div>
pelham
  • 128
  • 1
  • 10