<link rel="stylesheet" href="/assets/fonts/inter/inter.css" />
All posts

Angular con Looker Embedded: come azzerare il boilerplate e le interfacce TypeScript per le tue tabelle dati

In ogni applicazione gestionale arriva il momento della tabella numero venti: un'altra interfaccia TypeScript, un altro DTO, un altro componente con paginazione, ordinamento, filtri ed export. È codice utile, ma tutto uguale, e cresce a ogni richiesta. In questo articolo mostro come, per i dati di consultazione e analisi, Angular con Looker Embedded permetta di eliminare quasi del tutto quel boilerplate.

Il problema del boilerplate tradizionale

L'approccio classico per mostrare un dataset in Angular richiede sempre gli stessi pezzi:

  • Interfacce e DTO che duplicano sul frontend la forma dei dati del backend, da aggiornare a ogni modifica.
  • Un componente tabella dedicato, con Angular Material o PrimeNG, colonne definite a mano e template per ogni campo.
  • Paginazione, ordinamento e filtri da collegare e testare, spesso anche lato server.
  • Formattazione di valute, date e stati, ripetuta in ogni tabella.
  • Export in CSV o Excel, scritto a mano o affidato a un'altra libreria.

Ecco come appare, in forma ridotta, una sola tabella ordini:

// 1. The API contract, duplicated on the frontend
export interface OrderDto {
  id: number; customer_name: string; region: string;
  total_cents: number; status: 'OPEN' | 'PAID' | 'CANCELLED'; created_at: string;
}
// 2. The view model, because the API shape is not the table shape
export interface OrderRow {
  id: number; customer: string; region: string;
  total: number; status: string; createdAt: Date;
}

@Component({ /* ... MatTableModule, MatPaginatorModule, MatSortModule ... */ })
export class OrdersTableComponent implements AfterViewInit {
  readonly columns = ['id', 'customer', 'region', 'total', 'status', 'createdAt'];
  readonly dataSource = new MatTableDataSource<OrderRow>([]);
  @ViewChild(MatPaginator) paginator!: MatPaginator;
  @ViewChild(MatSort) sort!: MatSort;

  constructor(private api: OrdersApi) {
    this.api.list().subscribe(dtos => (this.dataSource.data = dtos.map(toRow)));
  }
  ngAfterViewInit(): void {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }
  applyFilter(value: string): void { this.dataSource.filter = value.trim().toLowerCase(); }
  exportCsv(): void { /* build the CSV by hand, escape quotes, trigger the download... */ }
}

// 3. Mapping and formatting, also by hand
const toRow = (d: OrderDto): OrderRow => ({
  id: d.id, customer: d.customer_name, region: d.region,
  total: d.total_cents / 100, status: d.status, createdAt: new Date(d.created_at),
});
// ...plus the HTML template with one <ng-container matColumnDef> per column

E questo è solo il frontend: per ogni nuova colonna serve cambiare il DTO, il mapping, il template, i test, e rilasciare backend e frontend insieme.

La svolta: Angular con Looker Embedded

Con Looker il contratto dati e la presentazione vivono nel modello LookML: dimensioni, misure, formati, etichette e relazioni sono definiti una volta sola, lato analytics. Il frontend non ha più bisogno di conoscere la forma dei dati: deve solo mostrare un contenuto già pronto, che Looker incorpora in un iframe sicuro.

Si inizializza l'SDK una volta per l'applicazione, indicando l'endpoint del nostro backend che firma gli URL di embed:

// app.config.ts — once for the whole application
import { LookerEmbedSDK } from '@looker/embed-sdk';

export const appConfig: ApplicationConfig = {
  providers: [
    // The SDK asks our backend for a signed embed URL for each content item
    provideAppInitializer(() => LookerEmbedSDK.init('analytics.example.com', '/api/looker/embed-auth')),
  ],
};

Poi basta un componente generico, <app-looker-table>, che riceve solo l'ID del contenuto e non sa nulla di colonne o tipi:

@Component({
  selector: 'app-looker-table',
  template: `<div #host class="looker-host"></div>`,
  styles: `.looker-host, .looker-host iframe { width: 100%; height: 100%; border: 0; }`,
})
export class LookerTableComponent {
  /** Look ID defined in Looker: the component knows nothing about the data. */
  readonly lookId = input.required<string>();
  private readonly host = viewChild.required<ElementRef<HTMLDivElement>>('host');

  constructor() {
    afterNextRender(() => {
      LookerEmbedSDK.createLookWithId(this.lookId())
        .appendTo(this.host().nativeElement)
        .build()
        .connect()
        .catch(err => console.error('Looker embed failed', err));
    });
  }
}

// Usage: the same component for any dataset
// <app-looker-table lookId="42" />   orders
// <app-looker-table lookId="57" />   invoices

Notate cosa manca: nessuna interface, nessun DTO, nessuna definizione di colonna, nessuna funzione di export. Lo stesso componente mostra gli ordini, le fatture o qualunque altro dataset.

I vantaggi per il team

Colonne nuove senza deploy del frontend

Aggiungere una colonna, cambiare un formato o un'etichetta è una modifica al modello LookML o al Look: appena salvata, è visibile nell'applicazione. Il frontend non va né ricompilato né rilasciato.

Export, drill-down e paginazione inclusi

Download in CSV, Excel e PDF, ordinamento, paginazione e drill-down sui valori sono funzioni di Looker, già testate e coerenti in ogni tabella, e si abilitano con i permessi dell'utente.

Sicurezza a livello di riga

Con il signed embed, il nostro backend genera l'URL firmato con gli attributi dell'utente loggato. Nel modello LookML un access_filter usa quegli attributi per filtrare ogni query: un utente non può vedere le righe di un'altra regione, nemmeno manipolando l'URL.

// NestJS: signs the embed URL with the logged-in user's attributes
@Get('looker/embed-auth')
async embedAuth(@Query('src') src: string, @CurrentUser() user: User) {
  const embed = await this.looker.ok(this.looker.create_sso_embed_url({
    target_url: `https://analytics.example.com${src}`,
    session_length: 3600,
    external_user_id: String(user.id),
    first_name: user.firstName,
    last_name: user.lastName,
    permissions: ['access_data', 'see_looks', 'download_without_limit'],
    models: ['sales'],
    user_attributes: { region: user.region },   // drives row-level security
  }));
  return { url: embed.url };
}
# LookML: every query on this explore is filtered by the user's region
explore: orders {
  access_filter: {
    field: customers.region
    user_attribute: region
  }
}

Il confronto

AspettoTabella tradizionale in AngularAngular con Looker Embedded
Codice frontend per un nuovo datasetDecine o centinaia di righe: service, mapping, componente, templateUna riga di template, con un componente condiviso
Interfacce TypeScript per datasetAlmeno due: DTO e modello di vistaNessuna
Aggiungere una colonnaSviluppo, review e deploy di backend e frontendModifica al modello LookML, senza deploy frontend
Paginazione, ordinamento, filtri, exportDa implementare e testareInclusi
Sicurezza sui dati per utenteLogica dedicata nel backendAttributi utente e access filter in LookML
ManutenzioneCresce con il numero di tabelleConcentrata nel modello dati

Quando non usarlo

  • Tabelle modificabili: inserimento, modifica in linea e azioni sulle righe restano compito dell'applicazione.
  • Interfacce molto personalizzate: il contenuto vive in un iframe, quindi lo stile si adatta con temi Looker, non con il CSS dell'app.
  • Costo e infrastruttura: Looker è una piattaforma con licenza e un modello dati da mantenere; ha senso quando i dataset sono molti e cambiano spesso.
  • La logica applicativa ha ancora bisogno dei suoi tipi: le interfacce spariscono per le tabelle di consultazione, non per form e regole di business.

Conclusioni

  • Time-to-market: un nuovo report diventa una riga di template, non uno sprint.
  • Manutenibilità: la codebase Angular smette di crescere a ogni dataset; la complessità resta nel modello dati, dove deve stare.
  • Sicurezza: le regole di visibilità dei dati sono centralizzate e applicate a ogni query.
  • Divisione dei ruoli: gli sviluppatori frontend si concentrano sull'esperienza dell'applicazione, chi lavora sui dati sul modello.

Per i dati di consultazione e analisi, Angular con Looker Embedded sposta il lavoro dal codice ripetitivo al modello dati. Se volete approfondire come organizzare il resto dell'applicazione, ho scritto di architettura Angular e di design system con componenti riutilizzabili; se state valutando un'integrazione simile, scrivetemi.

💬 Note dei lettori

0 note

Scrivi una nota

Condividi la tua opinione, un suggerimento o un complimento

Ultime note

Nessuna nota ancora. Sii il primo a commentare!