Changelog
This page summarizes consumer-visible changes. The repository's complete changelog is the canonical release record.
π¦ Unreleasedβ
π¦ 5.3.0 β 2026-09-21β
Addedβ
- Add
array(..., { initialLength })to create independent items from template or factory defaults. Positional counts and values, and numericinitialValue, remain supported. Conflicting initial sources are rejected in types and at runtime. - Add
array(..., { configureEach })to configure each new item through its inferred API after initial values are applied. Connect sibling fields or install validators with object templates or factories; reused and reordered items keep their existing configuration.
π¦ 5.2.0 β 2026-09-21β
Addedβ
- Add per-subscription
onValueChange(callback, { debounce: 300 })for fields, forms, groups, and arrays. Deliver the latest committed change after a pause without delaying node values, validation, or interaction state; cancel pending delivery on unsubscribe or injector destruction. Omitted or zero debounce retains synchronous notifications.
π¦ 5.1.0 β 2026-09-21β
Addedβ
- Listen to committed control edits with
(formNodeChange)as a shorter name for(formNodeValueChange), including typed binding subscriptions. Both names share the same events, debounce, and cleanup. Invalid node bindings now explain how to use[formNode].
π¦ 5.0.0 β 2026-09-21β
Changedβ
- Breaking: Remove the deprecated
codecoption andQueryParamCodec<T>export from@ngblocks/form-nodes/router. Replace them withserializerandQueryParamSerializer<T>. Built-in names, customparse/serializeobjects, andqueryParamfactories retain their behavior. - Breaking: Infer
field()value types from the generic and initial value instead of always addingnull. Non-nullish defaults such asfield('')now produce non-nullable fields; explicit nullish values add only their own type, andfield<T>()still starts atnull. Form/group shorthands, array templates, validators, and callbacks follow the inferred types. UnconfiguredcreateFormPrimitives()uses the same inference;nullable: true,field.nullable(), andfield.strict()remain explicit policies. Usefield.nullable(value)or an explicit nullable generic to preserve nullable writes.
π¦ 4.6.0 β 2026-09-21β
Changedβ
- Support negative indexes in
array().at():-1selects the last live item node and-2the previous one. Index normalization now followsArray.prototype.at(), including fractional indexes andNaN; out-of-range indexes returnundefined. - Autocomplete built-in and registered custom error kinds in
hasError()andgetError()across nodes, their APIs, and the control-state facade while continuing to accept arbitrary strings and preserving structured error inference. - Prefer
serializerandQueryParamSerializer<T>when configuringsyncQueryParams(). Thecodecoption andQueryParamCodec<T>remain compatible deprecated aliases;serializertakes precedence when both options are supplied. Named serializers, customparse/serializeobjects, andqueryParamfactories retain their behavior.
π¦ 4.5.0 β 2026-09-20β
Addedβ
- Synchronize fields, forms, groups, arrays, and writable Angular signals with Angular Router query parameters using
syncQueryParams()from@ngblocks/form-nodes/router. Mix direct sources and configured{ source, ...options }bindings with typed codecs (objects or built-in names), repeated array parameters, JSON values, defaults, shared batching, replace/push history,onInitialUrlSyncandonUrlSynchooks with typed snapshots after URL imports, automatic cleanup, and a connection exposing only configured raw query signals throughparams, pending/closed state, and manual cancellation while preserving form validation and pending-edit navigation semantics. - Subscribe to existing fields, forms, groups, and arrays with
node.onValueChange(callback, { injector? }). Independent listeners receive typed value changes, support early cancellation, and clean up automatically with their consumer or node owner while preserving equality, debounce, and DI-free use.
π¦ 4.4.0 β 2026-09-17β
Addedβ
- Nodes and their callable
$apinow support AngularWritableSignal<T>utilities while preserving form writes, validation, and propagation. AddasReadonly()for a stable live readonly value signal; use$apiwhen child names shadow signal operations.
π¦ 4.3.0 β 2026-09-17β
Addedβ
- Add
array.templateValue()to prepare a typed item value without inserting a row. Template arrays return independent copies of declared defaults; factory arrays execute their factory on each call and return the generated item's value.
π¦ 4.2.1 β 2026-09-15β
Changedβ
- Documentation links in the package now point to
https://form-nodes.js.org/.
π¦ 4.2.0 β 2026-09-15β
Addedβ
- Validator contexts now expose
root()as a reactive shortcut to node root navigation, available in inline validators,validator(), and everyasyncValidator()callback with the same readonly root types. - The npm package now includes a consumer
AGENTS.mdguide, with setup instructions for connecting coding agents to the installed library API.
Fixedβ
- Array
set()andpatch()now fill omitted item properties in untyped data from template/factory defaults, consistently for new and reused rows. Explicitundefinedremains explicit, and TypeScript still requires complete items.
π¦ 4.1.0 β 2026-09-14β
Changedβ
- Array
patch()now reconciles a complete collection likeset(), including when nested inside form/group patches. Incoming arrays determine length and order, reuse nodes by index ortrackBy, and require complete item values. Empty arrays,null, andundefinedclear the collection. Use a row'spatch()to update selected properties without replacing the collection.
π¦ 4.0.0 β 2026-09-14β
Addedβ
- Add
lengthBetween(minimum, maximum)to validate inclusive text or collection lengths with reactive bounds, conditional activation, and custom messages or errors. It preserves the existingminLengthandmaxLengtherrors and constraint metadata.
Changedβ
- Breaking:
minLengthnow measures empty strings as length zero, so a positive minimum rejects'', consistently with empty collections. Nullish values still pass andminLength(0)still allows empty text; its types now also accept explicitly undefined-valued fields. Usewhento keep optional empty strings valid; combiningrequiredandminLengthnow reports both errors for empty text. See the migration guide.
π¦ 3.9.1 β 2026-09-12β
Fixedβ
- Native text inputs bound to fields initialized or reset to
nullnow accept text without numeric parse errors. Text remains text, including numeric-looking strings; established numeric text bindings retain numeric parsing through clearing and reset, and rebinding starts fresh. - Native text inputs without pattern validators no longer receive an empty
patternattribute that rejects nonempty values during browser validation. Removing the final pattern validator also removes its native restriction.
π¦ 3.9.0 β 2026-09-11β
This breaking change ships in minor version 3.9.0 at the maintainer's explicit request, as an exception to the default Semantic Versioning policy.
Addedβ
- Add
requiredTruefor values that must be exactlytrue, andnotNilfor values that must only be non-null and defined. Both support reactive conditions, custom errors, and configurable messages with their own error kinds.
Changedβ
- Breaking:
requiredand activerequiredIfnow acceptfalse. UserequiredTruefor terms, consent, and other mandatory checked values; migrate error handling and message catalogs to therequiredTruekind. Both presence and acceptance contribute logical required state, while native checkboxes and custom controls exposing acheckedinput synchronize HTML-style required constraints only for acceptance.notNilcontributes no required metadata or native required constraint.
π¦ 3.8.1 β 2026-09-11β
Documentation maintenance release. The package API and runtime behavior are unchanged from 3.8.0.
π¦ 3.8.0 β 2026-09-11β
Addedβ
- Display validation messages with
FormNodeErrors(<form-node-errors>) for a node or custom-control state. Show one message after touch or attempted submission by default, with customizable visibility, message resolution, and safe defaults for invalid runtime configuration. - Customize error markup with a nested
#messagetemplate receiving the first message, visible message list, and error details. Error text defaults to red,0.875remfont size, and1.5line height, all configurable through CSS custom properties. Measured height animation is enabled by default and controlled through theanimateinput. - Access nearest-form submission state and the owning Form Nodes API through
useFormNodeState().form, withformSubmitted()as a direct signal shortcut for submission attempts.useClosestFormState()remains available independently. - Bind native file inputs to
File | nullorFile[]fields, including standalone[formNodeValue]bindings. Selection updates reactive values and validation; programmatic updates and resets synchronize the browser selection without assigning filesystem paths.
Changedβ
- Add
@angular/commonas an explicit peer dependency alongside Angular core and forms to support custom error templates.
π¦ 3.7.0 β 2026-09-11β
Addedβ
-
Bind raw values with
[formNodeValue]or use[(formNodeValue)]for committed two-way updates. Controls create one independent field when no[formNode]is supplied, reuse explicit nodes when provided, and expose the same state and CVA error integration in both modes. -
React to committed value changes with typed
onValueChange(value, node)options on fields, forms, groups, and arrays. Callbacks respect equality and debounce, skip initialization, and observe complete aggregate updates without requiring an injector. -
Contribute reactive component errors with
useFormNodeState({ errors }), returning one error, a message, an array, or no result. Contributions participate in bound-control validity and clean up on rebinding or destruction. AddprovideFormNodeStateErrors()for CVAs used with Angular 22 Signal Forms. -
Return field or form errors from
onSubmitto reject a submission and expose them through normal node validation state. Errors clear on edits/reset and before retries; stale responses cannot overwrite edited, detached, or newer submitted nodes. Thrown request failures continue to rejectsubmit().
Changedβ
- Breaking: Replace
useClosestForm()withuseClosestFormState()for shared submission history across Form Nodes, Reactive Forms, and NgForm. Form Nodes takes priority; access its callable API through the reactiveformNode()property when available. This breaking change ships in minor version 3.7.0 at the maintainer's explicit request, as an exception to the default Semantic Versioning policy.
π¦ 3.6.0 β 2026-09-10β
Addedβ
- Pass nullable array item types directly to
ctx.parent<TParent>(), includingPageForm['roles'][number]and(typeof this.pageForm.roles)[number]. The generic strips null and undefined automatically while the result remains a restricted node view or null. - Opt out of external signal tracking with
validator(callback, { reactive: false })while retaining validation on node value changes, including returned synchronous compositions.
Changedβ
- Breaking: Validator contexts now expose read-only node views that omit validation results, derived constraints, validator-resolution queries, and mutations recursively. This includes
node(),field(),$api, descendants, traversal, andparent<TParent>()even with an explicit generic. Use values for cross-field rules and perform mutations outside validation. This breaking type change ships in minor version 3.6.0 at the maintainer's explicit request.
π¦ 3.5.0 β 2026-09-10β
Addedβ
- Configure each new field, group, form, or array synchronously through its typed
configureoption. Configure row templates to install sibling validators with inferred types and independent references for every clone. - Declare an explicit immediate-parent contract with
ctx.parent<TParent>()in validator contexts (no runtime check; nullable), and extract existing array row types withArrayItemNode<TArray>.
Changedβ
- Allow context-taking declaration validators to reference their initializing form or group without return annotations, including error-or-null/undefined ternaries. Context and node types stay inferred; callback returns are intentionally unchecked. Use
ValidationResult,ComposableValidationResult, or a context-takingvalidator()helper for checked authoring; returned inline validators need a checked context. - Accept numeric validator error kinds and normalize them to strings in synchronous and asynchronous results, preserving messages and targets. Query these errors using string identifiers such as
getError('123').
π¦ 3.4.0 β 2026-09-10β
Changedβ
- Use
.$apias the single callable, collision-safe node API. The.apialias is removed; replace API accesses with.$api, while keeping ordinary child fields namedapi. This change is included in minor version 3.4.0 at the maintainer's request while the library has no other consumers.
Addedβ
-
Read own and descendant validation errors with
errors({ descendants: true })on any node.allErrors()remains its shortcut, whileerrors()keeps its existing own-error signal behavior and typing. -
Allow
form()andgroup()with no arguments to create empty objects, andarray()to create an empty collection of unknown-valued fields with null defaults. Configured factories support the same declarations and preserve their defaults.
Fixedβ
-
Coordinate CVA value and disabled-state updates so enabling and setting a value in the same turn displays the latest value, including ng-bootstrap rating. Enabling also replays values that a control ignored while disabled; resets use the same synchronization order without emitting user changes.
-
Refresh custom CVA views after model and disabled-state writes, including controls such as ng-bootstrap rating and timepicker that do not request a check themselves. This prevents stale rendering and expression-changed errors when enabling or disabling these controls.
-
Restore CVA views on resets even when the value is unchanged, including ancestor resets and discarded pending input. Rebinding to an equal-valued node refreshes the CVA without carrying over an old draft; stale and destroyed bindings no longer participate in resets.
-
Initialize CVA values and disabled state synchronously when
[formNode]connects, so Angular Material radio groups display preloaded selections correctly, including inside nested custom controls and conditional views. Later model-to-view updates continue through the signal rendering cycle. -
Support unannotated class self-references in parameterless
whenconditions across built-in and async validators while preserving typed contexts and checked boolean returns for context-taking conditions. Async conditions start safely after construction and restart validation when reenabled, even with unchanged values. -
Allow
requiredIfconditions to reference their class form through a later declared computed without explicit type annotations. Form and computed types remain inferred; the condition's return type is intentionally unchecked, so consumers must return a boolean.
π¦ 3.3.0 β 2026-09-10β
Released as a minor version. The incompatible nested value API migration is an explicit versioning exception while the maintainer is the only consumer. See the version policy.
Addedβ
-
Add
formNodeSubmitandformNodeSubmitBlockedoutputs for native form attempts and validation-blocked attempts, with typed value, form, and original-event payloads. Pending input is flushed before notification; programmatic submissions remain callback-only. -
Added readonly
form.submitted()to record submit attempts until the form is reset, including attempts blocked by validation or missing actions. Nested forms keep independent histories, and subtree resets clear descendant form histories. AddeduseClosestForm()to reactively observe the form owning the nearest injectable[formNode]binding, enabling submission-aware error components without event subscriptions.
Changedβ
-
Node
$apiobjects and unshadowedapialiases are now callable Angular signals with collision-safe state and operations, typed byCallableNodeApi. Array APIs preservelength().useClosestForm()now returns this API directly: replaceclosestForm()?.$api.submitted()withclosestForm()?.submitted(); its calls still read the exposed form value. -
Breaking: Replaced public
controlValue()andsetControlValue(value)withvalue.control()andvalue.control.set(value). Fields, groups, forms, and arrays now exposevalue.committed()for committed data before configured equality checks, andvalue.committed.set(value)for immediate writes equivalent toset(value). Control writes preserve debounce and dirty tracking; neither setter emits binding outputs by itself. ExportedNodeValueSignaldescribes these nested signals and hides native function members from IntelliSense on all three views. BareFieldNodeannotations retain the nested reads and setters; explicit value generics preserve their precise types.
π¦ 3.2.0 β 2026-09-09β
Addedβ
- Added
resetToInitial()to fields, groups, forms, and arrays. It restores captured initial values, clears subtree interaction state, and cancels pending control input without emitting control-originated value outputs. Object schemas are preserved; arrays restore their initial records through reconciliation. Supported data containers are copied, while opaque instances retain their references.
π¦ 3.1.0 β 2026-09-09β
Addedβ
- Added typed
formNodeControlValueChangeandformNodeValueChangeoutputs to[formNode]for immediate control values and committed values after debounce or flush. Native controls, CVAs, signal controls, and enabled input/output pairs share this contract; programmatic node writes and cancelled or obsolete pending notifications do not emit. Duplicate native parsed values do not repeat notifications or restart debounce.
Fixedβ
-
Restored initial string-literal suggestions for explicit union types such as
field<IborCode>(...), including nullable and configured factories. Invalid values remain type errors, and null/undefined inference is preserved. -
Custom signal controls and enabled input/output pairs now update node values, parent values, synchronous validation, and touched state before consumer
valueChange,checkedChange, andtouchtemplate handlers run. Debounce and consumer resets remain respected, including controls that injectNgControl,FORM_NODE, orFormNodeDirectiveduring construction. CVA and pass-through transports retain their ownership, and native DOM events with custom-output names do not update the custom model.
π¦ 3.0.2 β 2026-09-09β
Fixedβ
- Native
[formNode]bindings now process user input before template event handlers, so handlers can read the updated node value, parent value, and synchronous validation. Blur handlers observe the updated touched state; configured debounce and IME buffering remain respected, and resets inside handlers are no longer overwritten by a late input update. Native value listeners remain isolated from CVAs, custom controls, and pass-through bindings, including custom outputs with DOM event names.
π¦ 3.0.1 β 2026-09-09β
Fixedβ
useFormNodeState().required()and its equivalent required-validator queries now also recognize an active ownrequirederror on every supported binding. Custom, composed, asynchronous, and manual errors can drive the required indicator without a directly registered required validator; the fallback clears when the error disappears.
π¦ 3.0.0 β 2026-09-08β
Changedβ
- Breaking: Renamed the common
Nodetype toAnyNode, concreteField,Group, andFormtypes toFieldNode,GroupNode, andFormNode, and the AngularFormNodedirective/binding type toFormNodeDirective.ArrayNode, primitive factories, and[formNode]retain their names.isFormNode()now narrows toAnyNode.FieldNode,GroupNode,FormNode, andArrayNodenow accept omitted generic arguments for components and utilities handling unspecified values or structures; explicit arguments retain precise typing. AddedFormNodesModuleas an optional Angular import point that re-exportsFormNodeDirectivewithout configuring providers.
Addedβ
- Added
isFormNode(value)to recognize field, form, group, and array nodes and narrow unknown values toAnyNodewithout evaluating them.
Fixedβ
- Generic node APIs now expose optional
messageand binding metadata on errors returned byerrors(),allErrors(), andgetError(), matching concrete node error types.
π¦ 2.0.0 β 2026-09-08β
β Changedβ
-
Breaking: Replaced the
ValidationErrornamespace with directly exported types:ValidationErrorForKind,ValidationErrorWithTargetNode,ValidationErrorWithOptionalTargetNode,ValidationErrorWithoutTargetNode, andValidatorError. Update qualified type references and imports; error shapes and validation behavior are unchanged. -
Validator results now ignore malformed errors and accidentally returned nodes with development-only warnings, preserving valid errors in mixed-validity arrays. Error objects require a string
kind. Validators can also return message strings, normalized to{ kind: 'custom', message }, including empty strings and arrays mixing messages with error objects. The same filtering applies to resolved asynchronous andonErrorresults without leaving validation pending. Ignored results do not block the form. -
Empty
form({})andgroup({})declarations now typeforEachChild()callbacks asDynamicNode, so operations such aschild.set('')compile. Visiting added children still requiresincludeDynamic: true; nonempty declarations retain their concrete child union. -
Parameterless validator callbacks, including inline
validator()andasyncValidator()callbacks, can reference their own class form without return annotations, preserving field and aggregate inference across primitive options, positional validators, and configured factories. Their return type is intentionally unchecked (including zero-argument overloads); context-taking callbacks and parameterized asynchronous validators keep their checked contracts. Option-object keys remain available in editor completion for all primitives. Initial setup for mixed synchronous and asynchronous rules is deferred so they can safely read the form after its class property is assigned; synchronous errors still suppress asynchronous execution. -
Validator queries on nodes and
useFormNodeState()retain up to 32 reference/resolve combinations to reduce cache eviction when querying many rules; error queries retain their 20-entry limits. -
Breaking behavior/API change:
bindInputOutputPairs: truenow independently enables separate value/valueChange and checked/checkedChange connections. False/null pauses the complete pair connection, including state writes, touch/focus/reset hooks, and writable node access. SyncInputs no longer enables values; empty lists do nothing. Models, CVAs, and native controls retain their standard connections. -
Breaking behavior/API change: experimental
syncInputsnow selects only state/constraint inputs: false, 'declared', 'all', 'signal-controls', exact lists, or{ inputs, target }. Targets are all, signal-controls, and cva; CVA precedence is preserved on hybrid components. True and the earlier presets/mode objects are replaced by explicit selections. Declared includes initial disabled/readonly/hidden options, excluding validators. Both binding options default to false and inherit independently through nodes, factory defaults, providers, and global configuration. Validation is unchanged. -
Breaking API change:
configureGlobalFormNodes({ validatorMessages, classes, syncInputs })replacesconfigureGlobalValidatorMessages(). Global options update independently below Angular providers; binding defaults are captured on connection, while global messages remain reactive. Cleanup callbacks preserve later overrides and skip already cleaned-up configurations. -
Breaking API change:
provideFormNodesConfig({ validatorMessages, classes, syncInputs })replacesprovideValidatorMessages()andprovideFormNodeConfig();FormNodesConfigreplacesFormNodeConfig. Message factories retain injection and reactive message support, and the unified provider also works in component providers. All options inherit independently; configuring input synchronization preserves inherited classes, and configuring or clearing classes preserves synchronization. Explicit class maps replace rather than merge with inherited maps; an empty configuration is a no-op. -
Breaking behavior change:
forEachChild()on forms and groups now visits only declared children by default. Pass{ includeDynamic: true }as its second argument to include children added withadd()and receiveDynamicNodecallbacks. Runtime boolean options also useDynamicNode. Empty declarations require the option to visit their children; default callbacks useDynamicNode.
β Addedβ
-
useFormNodeState().hasValidator()supports equivalent Form Nodes/Angular required queries and direct synchronous/asynchronous validator references where available, with reactive updates, opt-in{ resolve: true }composition queries for[formNode], andundefinedfor unsupported or disconnected queries. Its queries andhasError()/getError()now use bounded memoization to avoid recomputing consumers when results are unchanged. -
useFormNodeState()exposes reactivehasError(kind)andgetError(kind)queries for every supported binding, returning normalized error presence and the first matching error object without traversing child paths. -
useFormNodeState()recognizes direct AngularValidators.requiredTrueand exposes numeric, length, and pattern constraints declared through standard Angular validator directives, including dynamic changes while valid. -
Each
provideFormNodesConfig()option acceptsnullto reset only that option: no automatic classes, input synchronization disabled, or an empty provider message catalog with normal fallback. Omitted options andundefinedstill inherit. -
provideFormNodesConfig({ validatorMessages })accepts a message catalog object directly as well as an injectable factory. Both forms retain reactive message callbacks and the same message precedence. -
validators({ resolve: true })andhasValidator(validator, { resolve: true })inspect final validator references reached through synchronous compositions, share validation evaluation, and react to composition dependencies. Default queries retain direct-registration semantics; async validators are listed without starting their work.
β Fixedβ
-
Custom-control model binding resolves public aliases and requires input/output metadata for the same model property, preventing internal signals from being selected as value models.
-
useFormNodeState().required()now detects AngularValidators.requiredand active required directives with[formControl],[formControlName], and[(ngModel)], including dynamic and silent updates even when the value is valid. -
Form and group
childrenmaps accept arbitrary runtime keys while preserving exact declared-property types. WithnoUncheckedIndexedAccess, missing-key lookups are optional.Object.values(children)includesDynamicNodein its element type to account for added children; useforEachChild()for the precise declared-child union.
π¦ 1.1.0 β 2026-09-07β
β Changedβ
- Breaking type change: form and group
childrenmaps now expose only declared keys in TypeScript, soObject.values(children)infers the union of declared child types withoutundefined. Useget(key)or the result ofadd()for dynamic access. Runtime maps still contain dynamically added nodes, which are not represented in this static union.forEachChild()uses the same declared-child union; useget(key)inside the callback when handling arbitrary dynamic node types.
β Addedβ
-
All primitive nodes now expose reactive
hasError(kind)queries for their own errors andhasValidator(validator)queries for directly registered validator functions, including async validators. -
Forms and groups now provide
forEachChild((child, key) => ...)to visit a snapshot of immediate children with the union of declared child types and reactive tracking of structural changes. Runtime iteration also includes dynamically added nodes, whose types are outside that static union.
π¦ 1.0.1 β 2026-09-07β
β Fixedβ
- Fields, forms, groups, and arrays now satisfy Angular
Signal<T>and are recognized byisSignal(), so they can be passed directly to signal-based utilities while preserving value inference and reactive updates.
π¦ 1.0.0 β 2026-09-07β
First public release of @ngblocks/form-nodes, establishing the stable public API. Supports Angular ^21.0.7 || ^22.1.5.
β Fixedβ
-
Angular 21 consumers can now install Form Nodes on Node.js 20.19.0+, 22.12.0+, and 24.0.0+ within those majors without an engine mismatch. Angular 22 retains its higher Node.js requirements.
-
[formNode]now recognizes CVAs assigned directly through an injectedNgControl.valueAccessor. Method-wrapping state hooks also observe values, errors, status, and interaction changes across node edits, async validation, reset, and rebinding without rerunning validators. -
Library console warnings now appear only in Angular development mode, including ignored keys/indexes, unsupported reset options, and custom-control input synchronization diagnostics.
-
[formNode]now supports CVAs that subscribe to an injectedNgControl: value/status streams and Angular control-state events follow the bound node, survive rebinding, and complete on destruction. CVAs can also report parsing errors throughcontrol.setErrors(): binding-owned errors affect node and ancestor validity and clear independently of configured validators on correction, reset, or detachment.getError()andhasError()expose these error payloads and support relative descendant paths. InjectedNgControl.nameandpathnow follow the bound nodeβs structural location through array moves, detachment, reattachment, and rebinding. CVAs can reset the bound subtree throughNgControl.reset()orcontrol.reset(), preserving node reset semantics and supporting local notification suppression with{ emitEvent: false }. UnsupportedonlySelfandoverwriteDefaultValuereset options warn and are ignored instead of interrupting reset.validatorandasyncValidatornow explicitly returnnullon both adapter surfaces: no Angular validator functions are exported; node validation remains observable through errors, pending, and status. -
useFormNodeState().value()with[formNode]now reports the latest committed value even when node equality retains an older public value. Pending debounce input remains separate. -
Asynchronous validators preserve pending work when a computed dependency compares equal, while continuing to react to later value changes.
-
Nested forms, groups, and populated arrays can now be constructed inside
computed(). Declaration inputs remain reactive, while internal initialization no longer makes node edits rebuild the tree. -
Pending or cancelled control-value debounce work no longer retains otherwise unreachable fields, forms, groups, arrays, or their parent trees through timers and custom debounce promises.
-
Compiled array templates no longer retain their source fields, forms, groups, arrays, or parent trees through clone callbacks. Later items still use the declared values, validators, and options.
β Addedβ
-
The npm package now provides repository, issue-reporting, and author links, clearer search metadata, and a bundled changelog.
-
provideFormNodeConfig({ syncControlInputs: false })lets custom components and consumer templates own state and constraint inputs while retaining value/checked synchronization, interaction hooks, native controls, and CVA disabled-state callbacks. Automatic input synchronization remains enabled by default. -
field(),form(),group(), andarray()accept shallow, deep, or custom equality for exposed values, validation, update callbacks, and submission. Public parents compose exposed child values; internal writes, reset, controls, and debounce remain independent. Equality is evaluated lazily, and comparator errors affect exposed reads after writes have committed. Array structure and keyed reconciliation continue updating even when the exposed collection compares equal. -
FormNodeValue<typeof node>extracts the committed value type of any form, group, array, or field, preserving nested objects, arrays, and field nullability. -
A reactive
root()signal on every node returns the complete structural root, including standalone fields, groups, forms, and arrays. -
An
erroroption on every built-in validator for replacing a failed rule's standard error with one or more static or reactively produced custom errors. -
field.strict()andfield.nullable()provide concise local nullability overrides on package and configured field factories. -
createFormPrimitives()creates isolated form primitive factories with optional defaults for field nullability, validator messages, and injector inheritance policies. -
FormValueContract<TValue>checks an inferred form or group against a named aggregate value withsatisfieswhile preserving concrete child-node types such asArrayNode. -
A complete consumer documentation website with tutorials, API reference, recipes, integration guides, and executable examples. The unfinished interactive playground remains deferred.
-
Typed
field(),form(),array(), andgroup()signal-based primitives. -
Synchronous and asynchronous validation with reactive, configurable messages.
-
Angular
[formNode]binding for native, signal-based, andControlValueAccessorcontrols.
β Changedβ
-
Breaking: Form submission options are now flat: use
onSubmit(value, form),onSubmitBlocked(form), andsubmitWheninstead ofsubmission. The default still allows pending validation; use'valid'to require valid state or'always'to bypass the validation gate. The standaloneFormSubmissionOptionstype is removed; useFormOptions. -
Package and documentation links now target
gastonmesseri/form-nodes, and the documentation site uses the/form-nodes/base path. -
Breaking: Rename
useControlState()touseFormNodeState(). Update imports and calls; the returnedControlStatetypes and supported bindings are unchanged. -
Support Angular
^21.0.7 || ^22.1.5, building the library with Angular 21 and TypeScript 5.9. Custom-control types now expose the Form Nodes contract consistently across both majors. -
Breaking: Remove the
$fieldadapter. Bind Form Nodes with[formNode]="node"instead of[formField]="node.$field".provideFormNodeConfig()now configures[formNode]only and can coexist with Angular's class configuration.useFormNodeState()still observes independently created Angular Signal Forms, Reactive Forms, and template-driven controls. -
The npm package is published as
@ngblocks/form-nodes. Use the scoped name in dependencies, imports, and module augmentations; exported APIs and runtime behavior are unchanged by the rename. -
Callable field objects now include a runtime
patch()method equivalent toset(). The public types continue to expose field patching only throughapiand$api. -
Separately declared validators now preserve their declared value type when reading through
context.field()orcontext.node(), including the node's value signal and API aliases. -
Inline validators now infer their owning
Field,Form,Group, orArrayNode, including aggregate children and array items. This also works through inlinevalidator()andasyncValidator()helpers, with configured primitives and nullability overrides. IntelliSense shows the expanded model for inlinecontext.value()reads, matching the node value signal. -
Validator contexts expose the validated node through the same readonly signal under
nodeandfield. Usecontext.node()(orcontext.field()) for the node andcontext.value()for its value. Flatcontext.form()andcontext.root()have been removed; usecontext.node().form()andcontext.node().root()instead. Interaction, availability, required, and submission state signals also move from the flat context to the node, such ascontext.node().touched()andcontext.node().disabled(). This applies to synchronous validators and every asynchronous callback. Access the API throughcontext.node().apiorcontext.field().apiinstead ofcontext.api; inline validators retain the concrete node API.parent()remains available on the context. -
form()returns the nearest explicit form workflow instead of the complete structural root. Nested forms own their descendants, while standalone fields, groups, and arrays returnnull; useroot()when the outermost node is required. -
field(undefined)preservesundefinedas its initial value, while an omitted initial value continues to start atnull. Explicitly typed calls includeundefinedin the field value type. A no-argumentfield()fromcreateFormPrimitives({ nullable: false })now also typechecks asField<unknown>, matching its existingnullinitial value. -
Field-level
nullableoptions have been removed. Usefield.strict()orfield.nullable()for local nullability choices;createFormPrimitives({ nullable })remains the factory-wide default. -
Form and group callable values display their complete nested object shape in IntelliSense rather than internal value and normalization helper types.
-
Nested object shorthand and object templates in
array()now create structuralGroupnodes.Formis reserved for explicit submission workflow boundaries. -
Object shorthand validates the complete declaration before construction, reports nested error paths, and rejects enumerable accessors, symbol child keys, and
__proto__. Inherited and non-enumerable properties are ignored, and diagnostics recommendfield(value)when an object was intended to remain an atomic field value. -
form.add()andgroup.add()accept the same field and nested-group shorthands as initial declarations through both the single-child and atomic batch signatures, with aligned runtime and TypeScript normalization. -
array()object templates and object-template factories accept field shorthands such as{ name: '', age: 0 }, producing independently cloned groups and fields with matching TypeScript inference. -
Array values in
form(),group(), dynamicadd(), andarray()object templates consistently become atomicFieldnodes. Only an explicitarray(...)creates a dynamic collection, so the inferred node never depends on array length or contents. Empty mutable array shorthands inferunknown[]instead ofnever[].
π¦ Changelog categoriesβ
Each release uses the categories that apply:
- Added for new consumer-facing capabilities.
- Changed for behavior or API changes.
- Deprecated for APIs scheduled for removal.
- Removed for removed APIs.
- Fixed for corrected behavior.
- Security for security-related changes.
Breaking changes include a direct link to their corresponding migration instructions.