I am currently displaying a table with a list of items that are updated via polling using an http get request to the server. The response is rendered only if there have been changes in the data.
My goal is to add animations to the rows of the table and trigger the animation only for new rows. Currently, the animation is triggered every time the response updates and the component re-renders, affecting all rows instead of just the new ones.
In my main.component.ts
, I pass down observables to child components. These include the event$ stream with the array of events to display and newSeenPlatformIds$ that emits the maximum value of IDs when there is a change. This is used to trigger the animation for new rows based on this number.
ngOnInit() {
// Events 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))
)
);
// Animation triggered every time a new ID is emitted
this.newSeenPlatformIds$ = this.events$.pipe(
map(events => Math.max(...events.map(event => event.id))),
distinctUntilChanged()
);
}
This is the table component where the animation logic resides:
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;
});
}
}
Lastly, here's the template code:
<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>