Në çdo aplikacion menaxhimi vjen momenti i tabelës numër njëzet: një tjetër ndërfaqe TypeScript, një tjetër DTO, një tjetër komponent me paginim, renditje, filtra dhe eksport. Është kod i dobishëm, por gjithmonë i njëjtë, dhe rritet me çdo kërkesë. Në këtë artikull tregoj si, për të dhënat e konsultimit dhe analizës, Angular me Looker Embedded lejon të eliminohet pothuajse plotësisht ai boilerplate.
Problemi i boilerplate-it tradicional
Qasja klasike për të shfaqur një dataset në Angular kërkon gjithmonë të njëjtat pjesë:
- Ndërfaqe dhe DTO që dyfishojnë në frontend formën e të dhënave të backend-it, për t'u përditësuar në çdo ndryshim.
- Një komponent tabele i dedikuar, me Angular Material ose PrimeNG, kolona të përcaktuara me dorë dhe template për çdo fushë.
- Paginim, renditje dhe filtra për t'u lidhur dhe testuar, shpesh edhe në anën e serverit.
- Formatim i valutave, datave dhe statuseve, i përsëritur në çdo tabelë.
- Eksport në CSV ose Excel, i shkruar me dorë ose i besuar një librarie tjetër.
Ja si duket, në formë të reduktuar, një tabelë e vetme porosish:
// 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
Dhe ky është vetëm frontend-i: për çdo kolonë të re duhet ndryshuar DTO-ja, mapping-u, template-i, testet, dhe duhen publikuar bashkë backend-i dhe frontend-i.
Kthesa: Angular me Looker Embedded
Me Looker kontrata e të dhënave dhe prezantimi jetojnë në modelin LookML: dimensionet, masat, formatet, etiketat dhe marrëdhëniet përcaktohen një herë të vetme, në anën e analitikës. Frontend-i nuk ka më nevojë të njohë formën e të dhënave: duhet vetëm të shfaqë një përmbajtje të gatshme, që Looker e integron në një iframe të sigurt.
SDK-ja inicializohet një herë për aplikacionin, duke treguar endpoint-in e backend-it tonë që nënshkruan URL-të e embed-it:
// 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')),
],
};
Pastaj mjafton një komponent gjenerik, <app-looker-table>, që merr vetëm ID-në e përmbajtjes dhe nuk di asgjë për kolonat apo tipet:
@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
Vini re çfarë mungon: asnjë interface, asnjë DTO, asnjë përkufizim kolone, asnjë funksion eksporti. I njëjti komponent shfaq porositë, faturat ose çdo dataset tjetër.
Përfitimet për ekipin
Kolona të reja pa deploy të frontend-it
Shtimi i një kolone, ndryshimi i një formati ose i një etikete është një ndryshim në modelin LookML ose në Look: sapo ruhet, është i dukshëm në aplikacion. Frontend-i nuk duhet as rikompiluar, as publikuar.
Eksport, drill-down dhe paginim të përfshira
Shkarkimi në CSV, Excel dhe PDF, renditja, paginimi dhe drill-down-i mbi vlerat janë funksione të Looker-it, tashmë të testuara dhe koherente në çdo tabelë, dhe aktivizohen me lejet e përdoruesit.
Siguria në nivel rreshti
Me signed embed, backend-i ynë gjeneron URL-në e nënshkruar me atributet e përdoruesit të identifikuar. Në modelin LookML një access_filter i përdor ato atribute për të filtruar çdo query: një përdorues nuk mund të shohë rreshtat e një rajoni tjetër, as duke manipuluar URL-në.
// 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
}
}
Krahasimi
| Aspekti | Tabelë tradicionale në Angular | Angular me Looker Embedded |
|---|---|---|
| Kodi frontend për një dataset të ri | Dhjetëra ose qindra rreshta: service, mapping, komponent, template | Një rresht template, me një komponent të përbashkët |
| Ndërfaqe TypeScript për dataset | Të paktën dy: DTO dhe modeli i pamjes | Asnjë |
| Shtimi i një kolone | Zhvillim, rishikim dhe deploy i backend-it dhe frontend-it | Ndryshim në modelin LookML, pa deploy frontend |
| Paginim, renditje, filtra, eksport | Për t'u implementuar dhe testuar | Të përfshira |
| Siguria e të dhënave për përdorues | Logjikë e dedikuar në backend | Atribute përdoruesi dhe access filter në LookML |
| Mirëmbajtja | Rritet me numrin e tabelave | E përqendruar në modelin e të dhënave |
Kur të mos e përdorësh
- Tabela të modifikueshme: futja, modifikimi në rresht dhe veprimet mbi rreshtat mbeten detyrë e aplikacionit.
- Ndërfaqe shumë të personalizuara: përmbajtja jeton në një iframe, ndaj stili përshtatet me temat e Looker-it, jo me CSS-në e aplikacionit.
- Kostoja dhe infrastruktura: Looker është një platformë me licencë dhe një model të dhënash për t'u mirëmbajtur; ia vlen kur dataset-et janë shumë dhe ndryshojnë shpesh.
- Logjika e aplikacionit ka ende nevojë për tipet e saj: ndërfaqet zhduken për tabelat e konsultimit, jo për formularët dhe rregullat e biznesit.
Përfundime
- Time-to-market: një raport i ri bëhet një rresht template, jo një sprint.
- Mirëmbajtja: codebase-i Angular ndalon së rrituri me çdo dataset; kompleksiteti mbetet në modelin e të dhënave, aty ku duhet të jetë.
- Siguria: rregullat e dukshmërisë së të dhënave janë të centralizuara dhe zbatohen në çdo query.
- Ndarja e roleve: zhvilluesit frontend përqendrohen te përvoja e aplikacionit, ata që punojnë me të dhënat te modeli.
Për të dhënat e konsultimit dhe analizës, Angular me Looker Embedded e zhvendos punën nga kodi i përsëritur te modeli i të dhënave. Nëse doni të thelloheni se si të organizoni pjesën tjetër të aplikacionit, kam shkruar për arkitekturën Angular dhe për design system me komponentë të ripërdorshëm; nëse po vlerësoni një integrim të ngjashëm, më shkruani.