Commit graph

665 commits

Author SHA1 Message Date
Leon Senft
d337cfb68f feat(forms): add debounce() rule for signal forms
The `debounce()` rule allows developers to control when changes to a
form control are synchronized to the form model.

This feature necessitated some changes to `FieldState`:

  * `controlValue` is a new signal property that represents the current
    value of a form field as it appears in its corresponding control.

  * `value` conceptually remains unchanged; however, its value may lag
    behind that of `controlValue` if a `debounce()` rule is applied.

The `debounce()` rule essentially manages when changes to `controlValue` are
synchronized to `value`. The intent is that an expensive or slow
validation rule can react to the debounced `value`, rather than a more
frequently changing `controlValue`.

Directly updating `value` immediately updates `controlValue`, and cancels any
pending debounced updates.

When multiple `debounce()` rules are applied to the same field, the last
currently active rule is used to debounce an update. These rules are
applied to child fields as well, unless they override them with their
own rule.
2025-11-11 12:00:09 -08:00
Leon Senft
8866934334 refactor(forms): do not infer accumulated metadata type from initial value
This removes the need to specify type arguments for
`reducedMetadataKey()` when the value returned from the `getIntial`
callback is a subtype of the accumulated type.
2025-11-11 12:00:09 -08:00
cexbrayat
800b01f5a1 docs: remove error mentions in signal forms docs
`error` no longer exists and is called `validate`
2025-11-11 08:30:08 -08:00
arturovt
d3f67f6ca8 refactor(core): mark VERSION as @__PURE__ for better tree-shaking
Annotate the `new Version(...)` call with `/* @__PURE__ */` to signal to optimizers that the constructor is side-effect free.

Without this hint, bundlers such as Terser or ESBuild may conservatively retain the `VERSION` instantiation even when unused. With the annotation, the constant can be tree-shaken away in production builds if not referenced, reducing bundle size.
2025-11-10 12:04:04 -08:00
SkyZeroZx
e3fc57e8fc docs: improve discoverability of forms 2025-11-10 07:57:43 -08:00
Leon Senft
850f0d6b3d perf(forms): only update interop controls when bound field changes
https://github.com/angular/angular/pull/64590 implemented change
detection for field bindings, but only for those bound to native or
custom form controls. This change extends that optimization to apply to
field bindings on interoperable controls built using Reactive Forms as well.
2025-11-07 11:58:17 -08:00
Kristiyan Kostadinov
10915f2f5f test(forms): fix type checking errors in signal form tests
Fixes some type checking errors in the signal forms tests that accumulated while there wasn't any type checking.
2025-11-07 11:57:50 -08:00
Miles Malerba
0efb3f6d1f refactor(forms): nicer type errors
By intersecting with `object` instead of `unknown` in the primitive and
`FormControl` cases, we get TypeScript to show nicer type errors that
mention `FieldTree<...>` insetad of `() => FieldState<...>`
2025-11-07 07:45:05 -08:00
Miles Malerba
faccf03ee0
refactor(forms): allow FieldTree of recursive type
Adjusts our typings to work better with recursive types and avoid
infinite recursion in the type system.
2025-11-06 13:43:43 -08:00
Miles Malerba
2fd8dc9195
refactor(forms): expose pathKeys as part of the API
Currently we maintain the pathKeys internally, but do not expose them
through the `FieldContext`, this PR updates the `FieldContext` to expose
this property.
2025-11-06 13:43:13 -08:00
Miles Malerba
c9058087ae
refactor(forms): support dynamic object logic
extends `applyEach` to work on objects as well, conditionally applying
logic to each property of the object.
2025-11-06 13:42:48 -08:00
arturovt
9f76fb61df refactor(forms): tree-shake ngControlStatusHost and ngGroupStatusHost
This commit removes `ngGroupStatusHost` variable because it's a side-effect, ending up preserving `ngControlStatusHost` and `ngGroupStatusHost`.
2025-11-06 10:57:45 -08:00
kirjs
60447945bc refactor(forms): add compatForm
This allows using reactive form controls in signal forms
2025-11-06 10:51:28 -08:00
Alan Agius
26fed34e0e
build: format md files
This commit configures prettier to format markdown files.
2025-11-06 10:03:05 -08:00
Miles Malerba
fa02d96508
test(forms): re-enable some disabled tests
Adjusts test behavior to reflect current implementation and enables them
2025-11-06 09:41:28 -08:00
SkyZeroZx
ca3ef38143 refactor(common): Removes unused imports to clean up dependencies
Eliminates unnecessary imports to reduce clutter and improve maintainability
2025-11-06 08:35:28 -08:00
Miles Malerba
e87f423d90 refactor(forms): add better support for Object.keys and Symbol.iterator
Improves support for using `Object.keys` (and related methods) and
`Symbol.iterator` to work with a `FieldTree` of unknown shape.
2025-11-06 07:51:28 -08:00
Miles Malerba
194b41199b refactor(forms): improved select support
When we set the `value` on a `<select>` element we're really just
setting the selected `<option>`. It treats the selected option as the
source of truth, rather than the actual value. This means that if
options are added or removed or their value changes, the `<select>` may
wind up with a different `value` than what's in our model.

This PR resolves this issue by adding a `MutationObserver` to the select
that is used to resync its value to the model whenever the options may
have changed.
2025-11-06 07:49:06 -08:00
Miles Malerba
96388b6d8a refactor(forms): improve discoverability of ValidationError flavors (#64747)
Improves discoverability by putting the WithField, WithoutField, etc as
subtypes of the main ValidationError type

PR Close #64747
2025-11-05 22:42:59 +00:00
Leon Senft
41be02da2f perf(forms): implement change detection for field control bindings
For each field state property, check if it has changed since the last
time it was checked before writing it the corresponding form control
property.

The `pattern` and `required` properties of the field state now return a
default value rather than `undefined` if not defined by metadata.
2025-11-05 00:11:22 +00:00
SkyZeroZx
78e6716f40 refactor(forms): remove redundant providedIn: 'root' from injection tokens
Removes unnecessary `providedIn: 'root'` declarations from injection tokens
2025-11-04 00:31:52 +00:00
Kristiyan Kostadinov
b9e2ccdda8 refactor(common): remove unused import (#64699)
Cleans up unused code to improve readability and maintainability.

PR Close #64699
2025-10-30 19:27:33 +00:00
Miles Malerba
662f0e5c00
feat(docs-infra): add support for namespaces
Adds support for generating docs for namespaces (and merged declarations
of namespace + type)
2025-10-29 20:22:21 +00:00
Miles Malerba
a7180b65db docs(forms): make FIELD experimental
Adds `@experimental` tag to `FIELD`
2025-10-29 10:40:49 +01:00
Miles Malerba
3d70d68676 refactor(forms): rename the logic property on FieldPathNode
`logic` was a confusing name, as it is a logic builder instance, not a
logic instance.
2025-10-29 10:40:07 +01:00
Miles Malerba
fa13a167f1 refactor(forms): preserve logic order when apply is used on root path
In some cases the logic order was not preserved properly when using `apply`. In particular this occurs when some logic is registered on a child of the root, followed by an apply to the root, followed by further logic registered on a child. In this case the final registered logic wound up running before the applied logic.

This happened because `FieldPathNode` for a child path was caching its `LogicNodeBuilder` at creation time. This meant that if the parent's `LogicNodeBuilder` changed (e.g., due to an `apply` call), the child would still be using the old one.

This commit fixes the issue by dynamically resolving the `LogicNodeBuilder` for a child path whenever it is accessed.
2025-10-29 10:40:07 +01:00
Miles Malerba
f80b51a738 refactor(forms): track arrays in a parent array by index
This commit changes arrays in a parent array to be tracked the same way
as primitive values like strings and numbers. This is necessary because
the tracking key symbol used to maintain identity for objects in an
array does not survive the array spread operation:

```
return {...oldValue} // tracking symbol preserved 
return [...oldValue] // tracking symbol lost 
```
2025-10-28 20:52:51 +01:00
kirjs
c57bbaa87d refactor(forms): Allow returning plain values from validators
This makes the API nicer to use
2025-10-28 20:50:45 +01:00
kirjs
8562e29be4 refactor(forms): Allow returning plain values from validators
This makes the API nicer to use
2025-10-28 20:50:45 +01:00
kirjs
28847c9a19 refactor(forms): Allow returning plain values from validators
This makes the API nicer to use
2025-10-28 20:50:45 +01:00
kirjs
b5c29d0d0a refactor(forms): Allow returning plain values from validators
This makes the API nicer to use
2025-10-28 20:50:45 +01:00
csorrentino
a5678f6f2b refactor(forms): add onError callback to validateHttp for HTTP errors
Adds onError callback inside validateHttp validator in signal forms

PR-Close: #63949
2025-10-27 17:12:25 +01:00
Miles Malerba
01e1ad2b37 refactor(forms): fix some typos
Fix some typos in signal forms code
2025-10-27 09:27:02 +01:00
Matthieu Riegler
15d4bd58d1 fix(forms): interop supports CVAs with signals (#64618)
The directive implemnetation might set CVA values during the template evaluation. Since the template is a reactive context we need to untrack when setting the CVA values to prevent writing to signals in a reactive context.

fixes #64614

PR Close #64618
2025-10-24 09:31:39 +02:00
Miles Malerba
515c2949d8 test(forms): test reactive iteration over array field (#64113)
Adds a test that verifies @for iteration over an array field is actually
reactive to new items being added to the array.

PR Close #64113
2025-10-24 09:29:29 +02:00
Miles Malerba
ef34e39b2a refactor(forms): rename files related to metadata (#64603)
Renames some files to reflect the property => metadata name change

PR Close #64603
2025-10-23 18:13:16 +02:00
Miles Malerba
884765be56 refactor(forms): rename field state related to metdata (#64603)
Renames the field state related to metadata to reflect the new
"metadata" name. In particular:
- `property(...)` is renamed to `metadata(...)`
- `hasProperty(...)` is renamed to `hasMetadata(...)`

PR Close #64603
2025-10-23 18:13:16 +02:00
Miles Malerba
b29e6469ed refactor(forms): rename logic functions related to metadata (#64603)
Renames logic functions related to metadata to align with the new
"metadata" name. Notably:
- `property(...)` => `metadata(...)`
- `aggregateProperty(...)` => `aggregateMetadata(...)`

PR Close #64603
2025-10-23 18:13:16 +02:00
Miles Malerba
ef37ecf444 refactor(forms): rename Property and AggregateProperty (#64603)
These have been renamed to `MetadataKey` and `AggregateMetadataKey`
respectively. The team consensus is that the term "property" is so
overloaded that it makes the topic difficult to explain & discuss, hence
the rename.

PR Close #64603
2025-10-23 18:13:16 +02:00
kirjs
a3e91b2e96 refactor(forms): Add a test better documenting behavior of the error function in built-in validators (#63993)
We will need to find a better way to name it.

PR Close #63993
2025-10-22 18:27:37 +00:00
Leon Senft
fd9af2afaf fix(forms): only propagate schema defined properties from field to control (#64446)
Prior to this change, `FieldState` defined a signal for each built-in
property. This unfortunately meant that the `Field` directive had no way
of knowing which property had actually been defined in the schema, and
would thus attempt to propagate them all to the bound form control. This
meant that the default values of these signals would override the
default or template defined values of these control properties.

Now these properties are `undefined` by default, and only initialized if
defined in the schema. Thus the `Field` directive will not attempt to
bind any properties that aren't explicitly managed by the schema.

PR Close #64446
2025-10-21 17:38:34 +00:00
Leon Senft
4261a8c583 refactor(forms): use FormValueControl instead of FormUiControl (#64446)
`FormUiControl` is the base type used for shared properties of
`FormValueControl` and `FormCheckboxControl`.

PR Close #64446
2025-10-21 17:38:34 +00:00
Leon Senft
6fca0e03ff test(forms): propagate pattern property to custom control input (#64446)
Note that unlike the other built-in properties, `pattern` is not
propagated to native controls so the lack of symmetry with other
property tests is intentional.

PR Close #64446
2025-10-21 17:38:34 +00:00
cexbrayat
9c5e969f51 fix(forms): bind invalid input in custom controls (#64526)
FormUiControl has an `invalid` input signal, but it was not bound by the control instructions.

PR Close #64526
2025-10-21 17:37:58 +00:00
cexbrayat
bc9c814ca7 refactor(forms): remove Mutable from signal forms public API (#64436)
The `Mutable` type is only used internally in the `addDefaultField` function, so it can be avoided to be publicly exposed.

PR Close #64436
2025-10-20 21:20:11 +00:00
Kristiyan Kostadinov
c2d376b85a feat(core): make SimpleChanges generic (#64535)
Currently it's easy to make a mistake when accessing properties on `SimpleChanges`, because the keys aren't typed. These changes add an optional generic to the interface so that users can get a compilation error if they make a typo.

A few things to note:
1. The generic argument is optional and we revert to the old behavior if one isn't passed for backwards compatibility.
2. All of the keys are optional, because they aren't guaranteed to be present for any `ngOnChanges` invocation.
3. We unwrap the values of input signals to match the behavior at runtime.

Fixes #17560.

PR Close #64535
2025-10-20 17:49:39 +00:00
Leon Senft
505bde1fed fix(forms): mark field as dirty when value is changed by ControlValueAccessor (#64471)
This corresponds with fixes made for native and custom signal form
controls: https://github.com/angular/angular/pull/64483.

PR Close #64471
2025-10-20 15:35:42 +00:00
Leon Senft
a0f3960270 refactor(forms): prefix framework-private methods on Field with ɵ (#64471)
These methods are only intended to be used internally within framework
instructions. Prefix them with `ɵ` to indicate that they are
framework-private and should not be called from user code.

PR Close #64471
2025-10-20 15:35:42 +00:00
Leon Senft
94b0afec00 fix(forms): implement interoperability between signal forms and reactive forms (#64471)
Add support for interoperability between signal forms and reactive forms that
commit effccffde0 had removed.

A signal forms field can once again be bound to any element or component with a
`ControlValueAccessor`.

PR Close #64471
2025-10-20 15:35:42 +00:00
Leon Senft
3529877772 fix(forms): mark field as dirty when value is changed by a bound control (#64483)
Fix https://github.com/angular/angular/issues/64465.
Fix https://github.com/angular/angular/issues/63623.

PR Close #64483
2025-10-17 16:02:16 +00:00