0

I have a table showing a list of items. This list of items is being updated by using polling by doing an http get request against the server, and rendering the response only if there has been a change in the response.

What I want is to attach an animation to the rows of the table and perform the animation if this row is new. right now the animation is being triggered every time the response is new and the component gets re-rendered, but it is being triggered for all rows of the table instead for the new one.

I have the main.component.ts which contains both the table and another component, these are the observables passed down to their children. I'm passing both the event$ stream with the array of events to show, and the newSeenPlatformIds$ that emits the maximum value of id for the array of events fetched only when there is a change. I use it to trigger the animation, if this number changes, a new row is present.

ngOnInit() {
    // events that get rendered in the table
    this.events$ = timer(0, 5000).pipe(
      switchMap(() => this.eventsService.fetchLastEvents()),
      distinctUntilChanged(
        (curr, prev) =>
          Math.max(...curr.map(currItem => currItem.id)) === Math.max(...prev.map(prevItem => prevItem.id))
      )
    );
    // everytime a new id is emitted, I want the row containing the event with event.id to animate
    this.newSeenPlatformIds$ = this.events$.pipe(
      map(events => Math.max(...events.map(event => event.id))),
      distinctUntilChanged()
    );
  }

Now the table component where the animation is defined:

import { Component, OnInit, Input } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
import { Event } from 'src/app/shared/interfaces/event';
import { trigger, style, transition, animate } from '@angular/animations';
import { tap } from 'rxjs/operators';

@Component({
  selector: 'app-last-events-grid',
  templateUrl: './last-events-grid.component.html',
  styleUrls: ['./last-events-grid.component.scss'],
  animations: [
    trigger('newRowAnimation', [
      transition('* <=> *', [style({ opacity: 0 }), animate('1000ms', style({ opacity: 1 }))])
    ])
  ]
})
export class LastEventsGridComponent implements OnInit {
  @Input() events$: Observable<Event[]>;
  @Input() newSeenPlatformIds$: Observable<number>;
  newSeenPlatformId: number;
  triggerAnimation = false;

  constructor() {}

  ngOnInit() {
    this.newSeenPlatformIds$.pipe(tap(id => console.log(id))).subscribe(id => {
      this.newSeenPlatformId = id;
      this.triggerAnimation = true;
    });
  }
}

An finally the template:

<div class="flex justify-center">
  <table class="w-full mx-10">
    <thead class="text-gray-500">
      <tr>
        <th class="cell-main"></th>
        <th class="cell-main">DESCRIPTION</th>
        <th class="cell-main">TIME</th>
        <th class="cell-main">READER</th>
        <th class="cell-main">STATE</th>
        <th class="cell-main">NEXT CHECK</th>
        <th class="cell-main">READINGS LEFT</th>
      </tr>
    </thead>
    <tbody>
      <tr
        [@newRowAnimation]="triggerAnimation && event.id === newSeenPlatformId"
        [ngClass]="{ 'row-nok': event.estado === false }"
        class="rounded overflow-hidden shadow-lg text-xl text-gray-500"
        app-event-item
        *ngFor="let event of events$ | async"
        [event]="event"
      ></tr>
    </tbody>
  </table>
</div>
Ernesto G
  • 525
  • 6
  • 20

1 Answers1

1

In case someone has the same issue, I was able to find this other question by looking also "trackBy" as @tsiro suggested, I just followed along and it worked:

solution

Ernesto G
  • 525
  • 6
  • 20