fix: enable strict mode, honest HTTP boundary, and add routing
Three fixes to the parts of the template that contradicted its own "illegal states unrepresentable" claim: - tsconfig: turn on strict + strictTemplates (measured zero fallout — the codebase already typechecked cleanly, it just wasn't enforced). - RemoteData<T> drops its unused error type parameter (Resource.error is always Error) and Failure now carries a real Error. Pages read the union with @let instead of re-deriving from the resource, which deletes the non-null assertion strictNullChecks would otherwise flag. - users.adapter.ts never checked response.ok, so an HTTP error resolved as a garbage Success and crashed instead of reaching RemoteData's Failure branch. New shared/infrastructure/http.ts adds the status check plus hand-written parse guards and abortSignal forwarding; the six fetch stubs across the test suite (which encoded the missing check) and adapter spec now cover the Failure and Empty paths. Also adds real routing (@angular/router was a dependency with zero imports and a fake "Back" button): /users and /users/:id are now deep-linkable via withComponentInputBinding(), tested with RouterTestingHarness driving real navigation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Component, computed, signal } from '@angular/core';
|
||||
import { Component, computed, inject, input } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { PageShellComponent } from '@shared/ui/templates/page-shell.component';
|
||||
import { AsyncComponent } from '@shared/ui/molecules/async.component';
|
||||
import { fromResource } from '@shared/application/remote-data';
|
||||
@@ -13,11 +14,12 @@ import { isEmptyUserList } from '@users/domain/user';
|
||||
template: `
|
||||
<app-page-shell heading="Users">
|
||||
@if (selectedUserId(); as id) {
|
||||
<app-user-detail [userId]="id" (close)="selectedUserId.set(null)" />
|
||||
<app-user-detail [userId]="id" (close)="router.navigate(['/users'])" />
|
||||
} @else {
|
||||
<app-async [data]="listData()" (retry)="usersResource.reload()">
|
||||
@if (usersResource.hasValue()) {
|
||||
<app-user-list [users]="usersResource.value()!" (select)="selectedUserId.set($event)" />
|
||||
@let list = listData();
|
||||
<app-async [data]="list" (retry)="usersResource.reload()">
|
||||
@if (list.tag === 'Success' && list.value) {
|
||||
<app-user-list [users]="list.value" (select)="router.navigate(['/users', $event])" />
|
||||
}
|
||||
</app-async>
|
||||
}
|
||||
@@ -25,7 +27,12 @@ import { isEmptyUserList } from '@users/domain/user';
|
||||
`,
|
||||
})
|
||||
export class UsersPage {
|
||||
protected selectedUserId = signal<number | null>(null);
|
||||
protected router = inject(Router);
|
||||
userId = input<string>();
|
||||
protected selectedUserId = computed(() => {
|
||||
const id = this.userId();
|
||||
return id ? Number(id) : null;
|
||||
});
|
||||
protected usersResource = usersResource();
|
||||
protected listData = computed(() => fromResource(this.usersResource, isEmptyUserList));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user