import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, effect, inject, input, signal, viewChild, } from '@angular/core'; import { Skeleton } from 'primeng/skeleton'; import { Table, TableLazyLoadEvent, TableModule } from 'primeng/table'; import { Column, DuckdbService } from '../duckdb.service'; @Component({ selector: 'app-file-viewer', standalone: true, imports: [TableModule, Skeleton], template: ` @if (file() && columns().length > 0) { @for (col of columns; track $index) { {{ col.name }} } @for (col of columns; track $index) { {{ rowData[col.name] }} } @for (col of columns; track $index) { } } `, styleUrl: './file-viewer.component.css', changeDetection: ChangeDetectionStrategy.OnPush, }) export class FileViewerComponent { file = input(); private duckdbService = inject(DuckdbService); private cdr = inject(ChangeDetectorRef); // Can't be computed since effect has to run first so file exists in duckdb protected columns = signal([]); protected numRows = 200; protected currentValue = signal([]); constructor() { effect(async () => { const file = this.file(); if (file) { await this.duckdbService.addFile(file); this.columns.set(await this.duckdbService.getColumns(file)); const rows = await this.duckdbService.getRows( file, 0, this.numRows, this.columns(), [], [], ); const newValue = Array.from({ length: Number(rows.totalRows) }); this.currentValue.set(newValue); } }); } protected async onLazyLoad(event: TableLazyLoadEvent) { const file = this.file(); if (file) { const rows = await this.duckdbService.getRows( file, event.first ?? 0, event.rows ?? 0, this.columns(), [], [], ); // First clear out existing data, don't want to risk loading entire file into memory // Keep data in previous/next page so when user scrolls between 2 pages they don't see missing data for (let i = 0; i < this.currentValue().length; i++) { if ( i < event.first! - this.numRows || i > event.first! + event.rows! + this.numRows ) { this.currentValue()[i] = undefined; } } // Can't update the current value, otherwise we get an infinite loop this.currentValue().splice(event.first!, event.rows!, ...rows.rows); this.cdr.markForCheck(); } } }