Files
atomic-design-poc/src/app/registratie/ui/aanvraag-detail.page.ts
T
eho 199cbe1f8c feat(fp): WP-06 — kill $any() in templates (18x)
Make AsyncLoadedDirective generic with a static ngTemplateContextGuard for
AsyncComponent's own internal typing. That can't propagate to consumer
`<ng-template appAsyncLoaded let-p>` sites though -- Angular only infers a
structural directive's type parameter from an input bound on that same
node, not from a sibling input on the parent component -- so the ~9
root-cause consumers (dashboard, registration-detail, aanvraag-detail,
registratie-wizard) instead unwrap the RemoteData Success value via a
typed computed() and narrow it locally with `@if (x(); as p)`. The
remaining union-narrowing casts (registration-summary, showcase concepts
page) are replaced with a stable @let binding and a direct resource read,
respectively. Documented as a deviation in WP-06's backlog file.
2026-07-03 21:27:01 +02:00

75 lines
2.9 KiB
TypeScript

import { Component, computed, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';
import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ASYNC } from '@shared/ui/async/async.component';
import { ApplicationsStore } from '@registratie/application/applications.store';
import { Aanvraag } from '@registratie/domain/aanvraag';
import { detailRows } from '@registratie/domain/aanvraag-view';
/** Page: a single aanvraag ("case"). Stub — it renders the aanvraag's known fields
(soort, waarvoor, status, referentie, ingediend) in a CIBG Datablock; full case
handling is future work. The dashboard "Mijn aanvragen" rows link here. */
@Component({
selector: 'app-aanvraag-detail-page',
imports: [
PageShellComponent,
SkeletonComponent,
AlertComponent,
DataBlockComponent,
DataRowComponent,
...ASYNC,
],
template: `
<app-page-shell
i18n-heading="@@aanvraagDetail.heading"
heading="Aanvraag"
backLink="/dashboard"
>
<app-async [data]="store.applications()">
<ng-template appAsyncLoaded>
@if (applications(); as list) {
@let a = find(list);
@if (a) {
<app-data-block
i18n-ariaLabel="@@aanvraagDetail.ariaLabel"
ariaLabel="Aanvraaggegevens"
>
@for (row of rows(a); track row.key) {
<div app-data-row [key]="row.key" [value]="row.value"></div>
}
</app-data-block>
<app-alert class="app-section" type="info" i18n="@@aanvraagDetail.stub">
De volledige afhandeling van deze aanvraag is nog niet beschikbaar in deze POC.
</app-alert>
} @else {
<app-alert type="warning" i18n="@@aanvraagDetail.nietGevonden"
>Deze aanvraag is niet gevonden.</app-alert
>
}
}
</ng-template>
<ng-template appAsyncLoading>
<app-skeleton height="2.5rem" [count]="5" />
</ng-template>
</app-async>
</app-page-shell>
`,
})
export class AanvraagDetailPage {
protected store = inject(ApplicationsStore);
private id = inject(ActivatedRoute).snapshot.paramMap.get('id') ?? '';
protected find = (list: Aanvraag[]): Aanvraag | undefined => list.find((a) => a.id === this.id);
protected rows = detailRows;
/** See DashboardPage's `profile` for why this narrows via a computed instead of `let-`. */
protected readonly applications = computed(() => {
const rd = this.store.applications();
return rd.tag === 'Success' ? rd.value : undefined;
});
}