BACK
Use casesForm VM — canSubmit/errors/preview in one stream
Use cases · 17

Form VM — canSubmit/errors/preview in one stream

Pattern

The Submit button depends on value, status, dirty, and server availability. The error list depends on value and status. The preview depends on value. Instead of keeping four async pipes in the template, we assemble one VM object that holds everything needed.

The problem it solves

When related data comes from different streams, the template can momentarily render an inconsistent state: canSubmit=true, say, but errors=[required]. Imperative component fields give the same effect — five handlers, each assigning its own thing, and the execution order decides whether the UI is correct.

Operators and why they matter

  • combineLatest — rebuilds the VM whenever any source changes. This is the heart of the pattern.
  • startWith — gives initial values so combineLatest emits right away instead of staying silent until each field's first change.
  • map — a pure projection: from four inputs it computes {canSubmit, errors, preview}.
  • shareReplay({ bufferSize: 1, refCount: true }) — caches the latest VM. A subscriber arriving later gets the current state immediately.

Gotchas

  • shareReplay(1) without a config holds the upstream forever. Use the object config with refCount: true.
  • Don't compute canSubmit in several places (once for the button, once for the hint) — the rules will drift apart eventually. One source of truth: vm.canSubmit.
  • Without startWith, combineLatest stays silent until EVERY source emits. The template will be empty until the first interaction.

What you get

The template gets one consistent object that fully describes the form's state. No intermediate "half-valid" renders.

script.ts // TypeScript
CONSOLE · Console output
Hit Run to see the result...