Commit graph

811 commits

Author SHA1 Message Date
Matthieu Riegler
4874c54c08 refactor(forms): remove deprecated WithField
This commit removes, `WithField`, `WithOptionalField`, `WithoutField`
2026-03-12 14:55:46 -06:00
Alan Agius
667219230a test: remove duplicate tests (#67518)
These tests are duplicate and have been removed.

PR Close #67518
2026-03-11 13:37:33 -07:00
cexbrayat
3c2d95aea8 refactor(forms): normalize experimental version tags in signals api
Also replaces v21.3 with v22.0, as v21.3 should not exist.
2026-03-11 11:40:06 -07:00
Leon Senft
57ba621c81 test(forms): read only context prevents writing to field value
Test that the read only context prevents writing to a field value:

* In validation rules
* In a provided configuration
2026-03-10 15:07:42 -07:00
Leon Senft
3e7ce0dafc fix(forms): restrict SignalFormsConfig to a readonly API
Introduce `FormFieldBinding` to represent a binding between a field and
a UI control through a `FormField` directive. This interface is used to
restrict `SignalFormsConfig` and `formFieldBindings` to a readonly API.

Fix #65779.
2026-03-10 15:07:42 -07:00
Leon Senft
a1a6c5282e refactor(forms): restrict reactive logic to a readonly API
Reactive logic in forms is not intended to mutate state, but this was
poorly communicated by the permissive and highly mutable field context
provided to all logic functions. This change splits all of the
state-related API into writable and readonly interfaces.

* Top-level functions that produce a `FieldTree` (e.g. `form()`) expose
  writable signals (e.g. `value: WritableSignal<T>`) and mutating
  methods (e.g. `markAsDirty()`).

* Reactive logic expose readonly signals (e.g. `value: Signal<T>`) and
  omit mutating methods.
2026-03-10 15:07:42 -07:00
Sonu Kapoor
71b8159b37 test(forms): cover transformedValue without FormField context
Adds a test verifying that `transformedValue` exposes parse errors via
the returned signal's `parseErrors()` property when no FormField
context is present.

This ensures that:
- parse errors are still observable without DI-based field propagation
- the model is not updated when `parse` omits `value`
- valid input clears parse errors and updates the model

This test protects the documented contract that DI-based error
propagation is expected for FormValueControl usage, while standalone
usage relies on explicit consumption of `parseErrors()`.
2026-03-09 16:41:48 -07:00
Leon Senft
c767d678cf
feat(forms): add 'blur' option to debounce rule
Expands the `debounce` rule configuration to accept `'blur'`. When this option
is provided, the rule will delay model synchronization until the field loses
focus (is touched). This introduces a debouncer that defers resolution
until the framework automatically aborts pending debounces upon touch events.
2026-03-05 09:55:14 -08:00
kirjs
d2e33e86b9 refactor(forms): address feedback
Refactor the normalizeFormArgs utility to include the internal FieldAdapter
2026-03-02 08:46:43 -08:00
kirjs
985d828f12 refactor(forms): hide adapter in public options
Moves adapter to internal options to prevent exposure but keep compatibility.
2026-03-02 08:46:43 -08:00
Sonu Kapoor
547ed65b6f
docs(forms): transformedValue parse error wiring 2026-02-25 08:13:12 -08:00
Alan Agius
d550bf713a build: update minimum supported Node.js versions
This commit updates the minimum supported Node.js versions. Node.js v20 support is dropped, and the minimum version for Node.js v22 is bumped to v22.22.0, and for v24 it is bumped to v24.13.1.

BREAKING CHANGE: Node.js v20 is no longer supported. The minimum supported Node.js versions are now v22.22.0 and v24.13.1.
2026-02-25 07:57:18 -08:00
Miles Malerba
23fd8fa586 fix(forms): use consistent error format returned from parse
Aligns the errors returned from the `parse` function in
`transformedValue` to use the same convention as the rest of signal
forms (a property called `error` that can contain a single error or list
of errors)
2026-02-23 09:11:51 -08:00
Leon Senft
1a19d61e19 refactor(forms): clean up
* Remove unused `TValue` type parameter from `FormUiControl`
* Remove unused imports
* Remove unnecessary cast
2026-02-23 09:09:55 -08:00
cexbrayat
fe25c57a5c fix(forms): preserve parse errors when parse returns value
Fixes #67170 by keeping the errors even a value is returned from the parse function.
2026-02-20 10:28:54 -08:00
Matthieu Riegler
2061fd8253 fix(forms): Untrack setValue in reactive forms
Reasonably, writing to an `AbstractControl` shouldn't register signal reads.

fixes #67073
2026-02-19 09:11:06 -08:00
Leon Senft
fb166772d2 fix(forms): split the touched model into an input and touch output
The `touched` property was never meant to support two-way binding; a
control should not be able to dictate that a field is no longer touched.

* The `touched` input represents the touched state of the field.
* The `touch` output allows a control implementation to indicate when
  the bound field is touched.

Note the distinction is that the `touch` output indicates _when_ the
field is touched, and not _whether_ the field is touched.
2026-02-19 09:07:03 -08:00
Leon Senft
567f292e8e fix(forms): support custom controls as host directives
Add the missing code to update control properties when control is a host
directive.

Fix #66592.
2026-02-18 14:06:54 -08:00
Leon Senft
dcb9af6dfa test(forms): [formField] synchronizes with a host directive
Test that `[formField]` synchronizes its value with a custom form
control implemented as a host directive on a component.
2026-02-18 14:06:54 -08:00
SkyZeroZx
fd956f675a refactor(forms): simplify destroy subject handling
Remove unnecessary conditional check when completing the destroy subject, since it is always defined
2026-02-18 07:12:02 -08:00
Leon Senft
b481294047 refactor(forms): remove unused generic type parameter
The `TValue` type parameter was unused by `FormUiControl`.
2026-02-17 13:02:42 -08:00
Miles Malerba
a6a0347998 refactor(forms): extract common parser logic (#66917)
native controls and custom controls (via transformedValue) use similar
parsing logic but it needs to be hooked up differently. This commit
extracts the common bits into a shared piece.

PR Close #66917
2026-02-13 12:11:06 -08:00
Miles Malerba
30f0914754 feat(forms): support binding null to number input (#66917)
Supports binding `null` to a `<input type=number>`.

- Binding in `null` clears the input
- Binding in `NaN` also clears the input
- When the user clears the input, the model is set to `null`
- The model is _never_ set to `NaN` based on user interaction. It is
  either set to `null` if the user cleared the input, or is unchanged
  and a parse error added if the user entered an invalid number like
  "42e"

PR Close #66917
2026-02-13 12:11:06 -08:00
Miles Malerba
22afbb2f36 feat(forms): add parsing support to native inputs (#66917)
Integrates native inputs with the new parseErrors API so that they can
report parse errors when the user types an un-parsable value (e.g. "42e"
in a number field).

When a user types an un-parsable value, the model does not update. It
retains its previous value and a parse error is added for the control
that received the un-parsable value.

PR Close #66917
2026-02-13 12:11:06 -08:00
Miles Malerba
27397b3f4f fix(forms): clear parse errors when model updates (#66917)
Changes `parsedErrors` to a `linkedSignal` based on the model value.
This ensures that the parse errors are reset if the model changes from
outside the control.

PR Close #66917
2026-02-13 12:11:06 -08:00
cexbrayat
63d8005703 fix(forms): preserve custom-control focus context in signal forms
Fixes #67051

Store custom control focus callbacks in a wrapper so method invocation keeps the original object context. Without this, custom focus methods that access instance members throw at runtime when focusBoundControl() is called.
2026-02-13 09:37:19 -08:00
Miles Malerba
d75046bc09
fix(forms): warn when showing hidden field state
In signal forms, it is up to the user to guard hidden fields from being
rendered in the template. To help catch instances where it is
accidentally not guarded, this commit introduces a warning in dev mode.
2026-02-11 14:46:43 -08:00
Leon Senft
3606902b33
refactor(forms): relax [formField] input type from FieldTree to Field
`FieldTree` was an unnecessarily specific type for the `[formField]`
input. It forced the directive to care about what _kind_ of `FieldTree`
was bound–specifically whether it was Reactive Forms compatible or not.
This made it difficult to author forms system-agnostic components with
passthrough `[formField]` inputs.
2026-02-11 11:45:20 -08:00
Miles Malerba
ba009b6031
feat(forms): add form directive
Adds a `formRoot` directive to manage submitting the form in signal
forms.
2026-02-10 14:34:48 -08:00
SkyZeroZx
19d0ceede3 test(forms): move timeout and autoTick helpers to shared testing utilities
Centralizes common test helpers under testing utilities and updates usages
2026-02-10 07:45:00 -08:00
SkyZeroZx
8b3b069be7 refactor(forms): use optional chaining for safer method calls in form directives
Simplifies null checks by leveraging optional chaining when invoking
optional callbacks
2026-02-10 07:42:56 -08:00
Miles Malerba
f56bb07d83 feat(forms): add field param to submit action and onInvalid
The `action` and `onInvalid` handlers now recevie two pieces of
information:
1. The form that is being submitted
2. The specific field that the submit was triggered on

Remove the `submit()` method on field state - supporting this is complex
from a typing perspective, since the `FieldState` only knows its
`TValue` type, not the `TModel` type of its owning `FieldTree`. Rather
than try to pack additional generics on to `FieldState`, we'll just
leave the `submit` function as a standalone importable function.
2026-02-09 14:49:43 -08:00
Leon Senft
b772f518f1 refactor(forms): add fieldTree property to FieldState
The `fieldTree` property of `FieldState` returns its associated
`FieldTree`.

Note that the round trip from `FieldTree` to `FieldState` and back will
lose type information. This is because `FieldState` intentionally does
not know whether it came from a pure Signal Forms field tree, or a
Reactive Forms compatible field tree:

```ts
// Pure Signal Forms:
const x: FieldTree<string>;

x();           // FieldState<string>;
x().fieldTree; // FieldTree<unknown>

// Reactive Forms compatibility:
const y: FieldTree<FormControl<string>>;

y();            // FieldState<string>;
y().fieldTree;  // FieldTree<unknown>;
```
2026-02-09 12:28:35 -08:00
Miles Malerba
adfb83146b
fix(forms): simplify design of parse errors
Reoves the `parseErrors` property on `FormUiControl` and instead
introduces a new utility `transformedValue` that automatically handles
synchronizing the raw value and model value using the given `parse` and
`format` functions. It also automates the reporting of `parseErrors` to
the `FormField`, simplifying the API surface
2026-02-09 12:27:41 -08:00
SkyZeroZx
f50ec42f15 refactor(forms): guard unsupportedFeatureError with ngDevMode
Move unsupportedFeatureError message behind ngDevMode
to keep it dev-only and allow proper tree-shaking.
2026-02-09 12:26:35 -08:00
Angular Robot
11767cabe4 build: update Jasmine to 6.0.0
Jasmine enables `forbidDuplicateNames: true` by default. So we also need to desambiguate duplicate spec names.
2026-02-09 12:15:57 -08:00
SkyZeroZx
24c0c5a180 feat(forms): support signal-based schemas in validateStandardSchema
Allow `validateStandardSchema()` to consume a computed schema so
validation rules stay in sync when the schema changes over time.

This supports schemas stored in computed signals (e.g. zod schemas that
depend on input signals) and ensures the effective schema updates after
initialization instead of being captured once.

Fixes #66867
2026-02-06 07:40:46 -08:00
SkyZeroZx
7dfbacbcf8 test(forms): remove zone-based testing utilities
Removes usages of zone-based helpers such as `fakeAsync` , `tick`
`waitForAsync` as part of the migration to zoneless tests.

Completes the transition to zoneless.
2026-02-04 15:38:09 -08:00
Kristiyan Kostadinov
680d99b1c3 refactor(forms): work around internal issue
Works around an internal property renaming issue by changing how we declare the interface for `InteropNgControl`.
2026-02-04 14:25:44 -08:00
Miles Malerba
95ecce8334 feat(forms): allow setting submit options at form-level
Updates FormOptions to accept a submission configuration object.
This allows defining default submit options (action, validation behavior, etc.)
when creating the form, which can be overridden when calling submit().
2026-02-03 12:43:31 -08:00
Miles Malerba
dd208ca259 feat(forms): update submit function to accept options object
Changes the `submit` function signature to accept a `FormSubmitOptions` object instead of a direct action callback.
This allows for more flexibility, including:

- `action`: The standard submit action to perform with the data.
- `onInvalid`: A callback to execute when the submit action is not triggered due to failing validation
- `ignoreValidators`: Controls whether pending validators or invalid validators should be ignored

Also updates the return value of `submit` to a `Promise<boolean` to indicate submission success.
2026-02-03 12:43:31 -08:00
kirjs
c750b3cf11 refactor(forms): Setup closure compatible property override
We have to do this because Abstract control doesn't allow us to have value as a getter type-wise
2026-02-02 14:51:40 -08:00
kirjs
80f08838b0 refactor(forms): Address more feedback
Clean up tests, drop old todos
2026-02-02 14:51:40 -08:00
kirjs
fdae63c11f refactor(forms): Address more feedback
Make the way reset works for it to be more consistent
2026-02-02 14:51:40 -08:00
kirjs
e4eeb3adc0 refactor(forms): Address more feedback
Untrack callbacks, so they are not called when signals change
2026-02-02 14:51:40 -08:00
kirjs
b3b5611096 refactor(forms): Address more feedback
Document that injectors are optional
2026-02-02 14:51:40 -08:00
kirjs
0df5442f4e refactor(forms): Address more feedback
- Add more comments and docs
- In signalErrorsToValidationErrors return null for empty object
- Drop messages in prod mode
2026-02-02 14:51:40 -08:00
kirjs
dabe34ad06 refactor(forms): Clean up the way errors are done
Make them tree-shakeable and fix the naming
2026-02-02 14:51:40 -08:00
kirjs
8e80575ff4 refactor(forms): address feedback
Consolidate everything related to converting errors in one place
2026-02-02 14:51:40 -08:00
kirjs
05d5087252 refactor(forms): use markAsPristine and markAsUntouched on field node
This make things cleaner
2026-02-02 14:51:40 -08:00