95 lines
3.3 KiB
Markdown
95 lines
3.3 KiB
Markdown
---
|
|
type: guide
|
|
title: Setup Form Field Errors
|
|
description: Pure error-computation functions that drive the inline validation messages on the first-admin modal.
|
|
tags: [setup, validation, forms, frontend]
|
|
timestamp: 2026-07-21T16:48:20Z
|
|
---
|
|
|
|
# Purpose
|
|
|
|
The three pure helpers exported by
|
|
`frontend/src/app/features/setup/setup-create-admin.field-errors.ts`
|
|
turn the raw Angular reactive-form control state into the user-facing
|
|
strings shown beside each input on the first-admin modal. Keeping them
|
|
as standalone functions (instead of `computed()` signals inside the
|
|
component) makes them trivially unit-testable in isolation — see
|
|
`tests/frontend/setup-create-admin.spec.ts`.
|
|
|
|
# Helpers
|
|
|
|
| Function | Returns | Reads |
|
|
|-----------------|---------------------------------------------------------|------------------------------------------------------------------------|
|
|
| `usernameError` | `string \| null` | `value`, `touched`, plus `errors` keys `required`/`minlength`/`maxlength`/`pattern`. |
|
|
| `passwordError` | `string \| null` | `value`, `touched`, plus `errors.required`. |
|
|
| `confirmError` | `string \| null` | `value`, `touched`, `errors.required`, and the group-level `groupMismatch` flag from [the password-match validator](/architecture/key-files.md). |
|
|
|
|
All three return `null` when the field is not yet `touched` (or, for
|
|
`confirmError`, when no group-level mismatch has been raised), so the
|
|
UI stays clean until the user has interacted with the form.
|
|
|
|
# Inputs
|
|
|
|
```ts
|
|
export interface FieldErrorContext {
|
|
value: string;
|
|
touched: boolean;
|
|
errors: Partial<Record<FieldErrorKey, true>> | null;
|
|
groupMismatch?: boolean;
|
|
}
|
|
|
|
export type FieldErrorKey =
|
|
| 'required'
|
|
| 'minlength'
|
|
| 'maxlength'
|
|
| 'pattern'
|
|
| 'passwordMismatch';
|
|
```
|
|
|
|
# Examples
|
|
|
|
```ts
|
|
usernameError({
|
|
value: 'ab',
|
|
touched: true,
|
|
errors: { minlength: true },
|
|
});
|
|
// -> 'Username must be at least 3 characters'
|
|
|
|
confirmError({
|
|
value: 'hunter2',
|
|
touched: true,
|
|
errors: null,
|
|
groupMismatch: true,
|
|
});
|
|
// -> 'Passwords do not match'
|
|
|
|
passwordError({
|
|
value: '',
|
|
touched: true,
|
|
errors: { required: true },
|
|
});
|
|
// -> 'Password is required'
|
|
```
|
|
|
|
# Wiring
|
|
|
|
* `SetupCreateAdminComponent` (`frontend/src/app/features/setup/setup-create-admin.component.ts`)
|
|
binds the three helpers to template method calls (`usernameError()`,
|
|
`passwordError()`, `confirmError()`) that read the corresponding
|
|
`FormControl` and pass its current state.
|
|
* The template
|
|
(`frontend/src/app/features/setup/setup-create-admin.component.html`)
|
|
renders the returned string inside a `<small class="field-error">`
|
|
with `data-testid="setup-{username|password|confirm}-error"`, and
|
|
mirrors the result on the input via `[attr.aria-invalid]`.
|
|
* `tests/frontend/setup-create-admin.spec.ts` exercises every code path
|
|
of the three helpers directly — no Angular TestBed is required.
|
|
|
|
# See also
|
|
|
|
- [First-Run Bootstrap](/guides/bootstrap.md) — how the helpers surface
|
|
to the operator.
|
|
- [Setup Endpoint](/api/setup.md) — server-side validation that
|
|
mirrors these messages.
|
|
- [Key Files Index](/architecture/key-files.md) |