Skip to content

v2.7.0

Latest

Choose a tag to compare

@github-actions github-actions released this 06 May 10:51
· 15 commits to master since this release
51bbbbf

2.7.0 (2026-05-06)

Non-breaking minor release. Headlined by a four-bundle correctness sweep across the directive lifecycle, path utilities, state-sync, and equality machinery, plus two new opt-in features for draft / auto-save workflows and dependent-field display tuning.

Compatibility: Angular >=19, RxJS >=7.8.0, Vest >=5.4.6. No public-API removals; no behavior changes for forms not opting into the new features.


ngx-vest-forms

✨ New features

  • Field-blur events for draft workflows ([#0f86da4]) — adds (fieldBlur) output and NgxFieldBlurEvent type on ngxVestForm. Emits a fresh, validated snapshot of the blurred field's value plus full form value, designed for auto-save / draft-persistence flows that need to fire on blur rather than on every keystroke. Handles nested control paths, dynamic groups, radios, and repeated leaf names; cancels in-flight emissions on form reset.

    • New public exports: NgxFieldBlurEvent type.
  • Opt-in dependent-field validation display ([#a14b2ea], closes #93) — fields that are tracked by a validationConfig dependency can now be configured to show errors only after they (or their dependency) have been touched. Avoids the surprise where dependent fields lit up on first render. Off by default; enable via the existing validation-config builder.

  • Pluggable equality function ([#ea711c6]) — adds the NGX_EQUALITY_FN injection token (typed via NgxEqualityFn). Lets consumers swap the form-value diff strategy (e.g. drop in lodash.isEqual, a structural-hash compare, or anything cheaper for known-shape models). Defaults preserve the existing semantics with Object.is short-circuit on top-level identity.

    • New public exports: NGX_EQUALITY_FN, NgxEqualityFn.
  • Reactive pendingDebounce input ([#eb9d690]) — [pendingDebounce] on form-group-wrapper is now a signal-accessor input; runtime changes propagate. Previously read once at construction. Adds the public DebouncedPendingStateOptionsInput type.

Bug fixes — the four-bundle correctness sweep

Bundle A — Lifecycle safety ([#104] / #108)

  • New internal destroy-scheduler primitive (scheduleTimeout / scheduleMicrotask) that ties timer / microtask cancellation to DestroyRef.
  • form.directive and validate-root-form.directive now route async work through it. Result: no more ViewDestroyedError after destroy-mid-async-validation, no leaked setTimeouts, and no cdr.detectChanges() / updateValueAndValidity() writes against destroyed views.
  • Async validators now emit cleanly even when the directive is torn down before resolution.

Bundle B — Path utilities ([#105] / #109)

  • setValueAtPath no longer destroys arrays. Calling setValueAtPath(form, 'addresses[0].street', 'x') on an existing populated array used to silently overwrite the entire array with {}, wiping every sibling entry. Bracket notation now decides container shape from the segment (numeric → array, string → object) and preserves siblings.
  • parseFieldPath rejects malformed segments ('a..b', '.a', 'a.', '.[' patterns) with a dev-mode console.warn instead of silently truncating. Production builds are unchanged (warning is ngDevMode-gated and tree-shakable).
  • validateShape is correct on opaque values. Date, Map, Set, RegExp, File, Blob short-circuit recursion instead of being walked as plain records. Numeric-key detection switched from parseFloat to ^\d+$, so '123abc' is no longer silently mapped to array index 0 — it's flagged as a type mismatch (TYPE_MISMATCH warning when the shape expects a primitive).

Bundle C — State-sync correctness ([#106] / #112)

  • Empty form no longer shows ghost data. formState().value now resets via linkedSignal when all controls are dynamically removed, instead of returning the previous snapshot.
  • ngxValidateRootFormMode precedence matches the docstring. Default switched to an undefined sentinel so the runtime can distinguish "not set" from 'submit'. Order: ngx ?? legacy ?? 'submit'. Observable difference only when both ngx- and legacy attributes are set explicitly; existing single-attribute usage is unchanged.
  • Late-attached NgModel.control is retried via afterNextRender so form-control-state correctly tracks controls that haven't attached yet at construction time.
  • Touched state syncs for dependent validation ([#299a29d]) — blurring a field now propagates touched into dependents so their error display flips on the same tick.
  • End-date validation now checks both endpoints exist before comparing, fixing a false-positive on partial date ranges ([#fcd88b6]).

Bundle D — Equality polish ([#107] / #110)

  • fastDeepEqual got real cycle detection. A single WeakMap of seen pairs replaces the previous depth-10 reference-fallback. Two distinct cyclic objects with identical structure now compare equal; self-referential objects compare equal to themselves; deep trees beyond 10 levels are correctly compared instead of bailing to reference equality and triggering spurious re-renders / breaking distinctUntilChanged.
  • Functions are treated as reference-only.
  • Existing Map / Set / Date / RegExp semantics preserved and pinned by tests.

🧹 Internal / refactors

  • cloneDeep is still exported but now logs a dev-mode deprecation warning once. Scheduled for removal in v3 ([#114]). Use structuredClone.
  • field-clearing switched to Object.hasOwn.
  • mergeValuesAndRawValues now treats null target like undefined so disabled-control raw values aren't dropped from submit payloads.

Demo apps & examples (projects/examples)

These only affect the examples / docs site / Storybook.

  • Auto-save on blur demo ([#7d6c106]) — end-to-end demo of the new (fieldBlur) output for draft persistence, including dynamic groups and submit-time persistence policy. Documented in docs/AUTO-SAVE-ON-BLUR.md.
  • Composite custom-control adapter recipe ([#05e48c5]) — recipe demo showing how to wire one ngModel value to multiple field paths with proper a11y. Documented in docs/COMPOSITE-ADAPTER-RECIPE.md. A first-class library API is tracked in #100.

Docs