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

Angular mit Looker Embedded: So entfallen Boilerplate und TypeScript-Interfaces für deine Datentabellen

In jeder Verwaltungsanwendung kommt der Moment von Tabelle Nummer zwanzig: noch ein TypeScript-Interface, noch ein DTO, noch eine Komponente mit Paginierung, Sortierung, Filtern und Export. Nützlicher Code, aber immer derselbe, und er wächst mit jeder Anforderung. In diesem Artikel zeige ich, wie Angular mit Looker Embedded diesen Boilerplate für Auswertungs- und Analysedaten fast vollständig überflüssig macht.

Das Problem mit klassischem Boilerplate

Der klassische Ansatz, ein Dataset in Angular anzuzeigen, braucht immer dieselben Bausteine:

  • Interfaces und DTOs, die die Datenstruktur des Backends im Frontend duplizieren und bei jeder Änderung angepasst werden müssen.
  • Eine eigene Tabellenkomponente mit Angular Material oder PrimeNG, von Hand definierten Spalten und Templates für jedes Feld.
  • Paginierung, Sortierung und Filter, die verdrahtet und getestet werden müssen, oft auch serverseitig.
  • Formatierung von Beträgen, Datumsangaben und Status, in jeder Tabelle aufs Neue.
  • Export nach CSV oder Excel, von Hand geschrieben oder an eine weitere Bibliothek delegiert.

So sieht – stark gekürzt – eine einzige Auftragstabelle aus:

// 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

Und das ist nur das Frontend: Jede neue Spalte bedeutet Änderungen an DTO, Mapping, Template und Tests sowie ein gemeinsames Release von Backend und Frontend.

Die Wende: Angular mit Looker Embedded

Mit Looker leben Datenvertrag und Darstellung im LookML-Modell: Dimensionen, Kennzahlen, Formate, Beschriftungen und Beziehungen werden einmal definiert, auf der Analytics-Seite. Das Frontend muss die Struktur der Daten nicht mehr kennen: Es zeigt nur fertige Inhalte an, die Looker in einem sicheren iframe einbettet.

Das SDK wird einmal für die gesamte Anwendung initialisiert, mit dem Endpoint unseres Backends, der die Embed-URLs signiert:

// 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')),
  ],
};

Danach genügt eine generische Komponente, <app-looker-table>, die nur die ID des Inhalts erhält und nichts über Spalten oder Typen weiß:

@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

Beachte, was fehlt: kein interface, kein DTO, keine Spaltendefinition, keine Exportfunktion. Dieselbe Komponente zeigt Aufträge, Rechnungen oder jedes andere Dataset.

Die Vorteile für das Team

Neue Spalten ohne Frontend-Deployment

Eine Spalte hinzufügen oder ein Format bzw. eine Beschriftung ändern ist eine Änderung am LookML-Modell oder am Look: Sobald sie gespeichert ist, ist sie in der Anwendung sichtbar. Das Frontend muss weder neu gebaut noch ausgeliefert werden.

Export, Drill-down und Paginierung inklusive

Downloads als CSV, Excel und PDF, Sortierung, Paginierung und Drill-down auf Werte sind Looker-Funktionen – bereits getestet, in jeder Tabelle einheitlich und über die Berechtigungen des Nutzers steuerbar.

Sicherheit auf Zeilenebene

Beim Signed Embedding erzeugt unser Backend die signierte URL mit den Attributen des angemeldeten Nutzers. Im LookML-Modell nutzt ein access_filter diese Attribute, um jede Abfrage zu filtern: Ein Nutzer kann die Zeilen einer anderen Region nicht sehen, auch nicht durch Manipulation der 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
  }
}

Der Vergleich

AspektKlassische Tabelle in AngularAngular mit Looker Embedded
Frontend-Code für ein neues DatasetDutzende bis Hunderte Zeilen: Service, Mapping, Komponente, TemplateEine Template-Zeile mit einer gemeinsamen Komponente
TypeScript-Interfaces pro DatasetMindestens zwei: DTO und View-ModelKeine
Eine Spalte hinzufügenEntwicklung, Review und Deployment von Backend und FrontendÄnderung am LookML-Modell, kein Frontend-Deployment
Paginierung, Sortierung, Filter, ExportSelbst implementieren und testenEnthalten
Datensicherheit pro NutzerEigene Logik im BackendNutzerattribute und Access Filter in LookML
WartungWächst mit der Zahl der TabellenIm Datenmodell gebündelt

Wann man es nicht einsetzen sollte

  • Bearbeitbare Tabellen: Erfassen, Inline-Bearbeitung und Zeilenaktionen bleiben Aufgabe der Anwendung.
  • Stark individualisierte Oberflächen: Der Inhalt lebt in einem iframe, das Styling wird also über Looker-Themes angepasst, nicht über das CSS der App.
  • Kosten und Infrastruktur: Looker ist eine lizenzpflichtige Plattform mit einem zu pflegenden Datenmodell; es lohnt sich bei vielen, häufig wechselnden Datasets.
  • Anwendungslogik braucht weiterhin ihre Typen: Interfaces entfallen für Auswertungstabellen, nicht für Formulare und Geschäftsregeln.

Fazit

  • Time-to-Market: Ein neuer Report wird zu einer Template-Zeile, nicht zu einem Sprint.
  • Wartbarkeit: Die Angular-Codebasis wächst nicht mehr mit jedem Dataset; die Komplexität bleibt im Datenmodell, wo sie hingehört.
  • Sicherheit: Regeln zur Datensichtbarkeit sind zentralisiert und gelten für jede Abfrage.
  • Klare Rollen: Frontend-Entwickler konzentrieren sich auf die Anwendung, Datenleute auf das Modell.

Für Auswertungs- und Analysedaten verlagert Angular mit Looker Embedded die Arbeit vom repetitiven Code ins Datenmodell. Wenn du tiefer einsteigen willst, wie man den Rest der Anwendung organisiert, habe ich über Angular-Architektur und über Design Systems mit wiederverwendbaren Komponenten geschrieben; wenn du eine ähnliche Integration planst, schreib mir.

💬 Leser-Notizen

0 Notizen

Notiz schreiben

Teile deine Meinung, einen Vorschlag oder ein Kompliment

Neueste Notizen

Noch keine Notizen. Sei der Erste, der kommentiert!