48 Commits

Author SHA1 Message Date
Kam 14d6999cd7 docs(forms): close the unterminated code fences in the compatForm docs
Two of the three `compatForm` overloads open a fenced code block in their
`@example` and never close it. The fence then runs to the end of the tag, so the
API renderer emits a plain `<pre><code>` block for those two instead of a
highlighted one.

The result is that on the compatForm API page the first overload's example is
syntax highlighted while the other two, which contain identical code, render as
flat unstyled text.

Rebuilding //adev/src/assets:content with and without this change alters 1 of
1565 pages. That page goes from 6 highlighted and 2 plain code blocks to 8
highlighted and none plain.

(cherry picked from commit b9fb456971)
2026-09-10 14:53:47 -07:00
volkanfilazi 8e8c5524bb removed console log 2026-07-08 11:23:00 -07:00
volkanfilazi b542302a23 fix(forms/signals): make extractValue reactive for compat AbstractControl values 2026-07-08 11:23:00 -07:00
SkyZeroZx bbbcf8fc7f docs: add @see references to Signal Forms 2026-07-06 13:35:20 -07:00
Alex Rickabaugh 7745365910 feat(forms): graduate signal forms APIs to public API
Replaced `@experimental` tags with `@publicApi 22.0` across all Signal Forms APIs under `packages/forms/signals` to mark them as ready for general use in v22.

TAG=agy
CONV=0af6c644-225a-4212-a49a-5843d17ec638
2026-05-06 12:01:41 -07:00
Alex Rickabaugh 3c44d7c90b fix(forms): fix orphan field error on blur during array removal
Explain the race condition: when an item is deleted from a model array, its
DOM element is removed during change detection, which fires a `blur` event
synchronously. The `blur` handler tries to mark the field as touched,
navigating up to `keyInParent` which throws because the item is already gone
from the array in signals state.

Fix by introducing an `isOrphaned` check that short-circuits `markAsTouched`
early, backed by a reactivity-insulated `childrenMap` poll to avoid double
scans and prevent unhandled exceptions.

TAG=agy
CONV=054e0185-f5f0-40e3-9c9b-413309f36cf6

Fixes #66711

Co-Authored-By: Matthieu Riegler <kyro38@gmail.com>
2026-05-05 15:54:42 -07:00
Leon Senft eeba51c50b refactor(forms): make markAsTouched() touch all descendants by default
`markAsTouched()` now marks all descendants as touched. In general this
method is called when controls update the model. Most controls update
leaf nodes, in which case this change has no effect.

Marking all descendants allows triggering validation for subsections of
a form, independently from having to call `submit()` on the entire form.

`markAsTouched()` now accepts a `MarkAsTouchedOptions` parameter, which
includes a `skipDescendants` property. This can be used mark only the
receiving field as touched: `node.markAsTouched({skipDescendants: true})`.
2026-03-16 16:03:36 -06:00
kirjs a94958b59b refactor(forms): Address more feedback
Minor test and type cleanup
2026-03-16 15:24:20 -06:00
kirjs b061495134 refactor(forms): Add more tests
thoroughly tests both propagation directions
2026-03-16 15:24:20 -06:00
kirjs 0d92708b33 refactor(forms): adress feedback
Drop unnecessary casts + cleanup
2026-03-16 15:24:20 -06:00
kirjs 6703e6c803 refactor(forms): add structured extract filter
It's a helper function that takes a form, and extrasts it's value unwrapping compat values and also allowing to filter them.
2026-03-16 15:24:20 -06: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
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
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
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
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
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
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 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
kirjs bbbdf0a6ed refactor(forms): add unsupported method errors and docs
- Add disable, enable methods that throw with helpful messages
- Add validator methods (set/add/remove/clear) that throw
- Add setErrors, markAsPending methods that throw
- Add setters for dirty/pristine/touched/untouched that throw
- Add JSDoc with @usageNotes examples
- Add comprehensive unit tests for SignalFormControl
- Add FormGroup/FormArray integration tests
- Add web tests for CVA directive lifecycle
- Update migration docs with SignalFormControl usage
2026-02-02 14:51:40 -08:00
kirjs a2950805df refactor(forms): add CVA callback registration
- Add registerOnChange, _unregisterOnChange for value change callbacks
- Add registerOnDisabledChange, _unregisterOnDisabledChange for disabled callbacks
- Add disabled changes effect to notify registered callbacks
- Call onChange callbacks from updateValue with emitModelEvent flag
2026-02-02 14:51:40 -08:00
kirjs 35d02f228c refactor(forms): add fieldTree wrapping for sync parent updates
- Add CachingWeakMap utility for memoization
- Add wrapFieldTreeForSyncUpdates Proxy wrapper
- Intercept fieldTree().value.set() calls to sync parent immediately
- Cache wrapped trees and states to preserve identity
2026-02-02 14:51:40 -08:00
kirjs d6b49f12ef refactor(forms): add FormGroup/FormArray parent integration
- Add ValueUpdateOptions type with onlySelf, emitEvent options
- Add parent notification on value changes via effect
- Add parent notification helpers: scheduleParentUpdate, notifyParentUnlessPending
- Propagate dirty/touched/pristine/untouched to parent
- Support onlySelf option to prevent parent propagation
2026-02-02 14:51:40 -08:00
kirjs c2e0be7600 refactor(forms): add events observable with ControlEvents
- Emit ValueChangeEvent, StatusChangeEvent on changes
- Emit TouchedChangeEvent, PristineChangeEvent on status changes
- Emit FormResetEvent on reset()
- Add emitControlEvent helper method
2026-02-02 14:51:40 -08:00
kirjs b63496853d refactor(forms): add valueChanges and statusChanges observables
- Add valueChanges EventEmitter that emits when source signal changes
- Add statusChanges EventEmitter that emits when status changes
- Set up effects to emit to observables
2026-02-02 14:51:40 -08:00
kirjs 1e3462db00 refactor(forms): add reset functionality
- Add reset() method with optional value parameter
- Support FormControlState unboxing ({value, disabled} format)
- Add isFormControlState helper function
2026-02-02 14:51:40 -08:00
kirjs ab3f4367f4 refactor(forms): add dirty/touched status management
- Add dirty, pristine, touched, untouched getters
- Add markAsTouched, markAsDirty methods
- Add markAsPristine, markAsUntouched methods (preserve other state)
2026-02-02 14:51:40 -08:00
kirjs bb5e75a5a1 refactor(forms): add disabled state support via rules
- Add disabled, enabled, pending getters
- Update status getter to check disabled state first
- Add UNSUPPORTED_FEATURE error code for future use
2026-02-02 14:51:40 -08:00
kirjs 3937afc316 feat(forms): introduce SignalFormControl for Reactive Forms compatibility
This commit introduces `SignalFormControl`, a bridge implementation that allows Signal-based forms to interoperate with existing Reactive Forms infrastructure. It extends `AbstractControl` with standard methods and reactive observables while handling state propagation to parent containers.
2026-02-02 14:51:40 -08:00
Miles Malerba ebae211add feat(forms): introduce parse errors in signal forms
Parse errors allow a custom control to communicate that it is currently
unable to produce a valid value.

Parse errors are reported by implementing the optional `parseErrors`
property on the `FormUiControl`. The property should be a signal of the
current parse errors.

Also renames several `*Field` types to `*FieldTree`. This aligns with the new naming of the concept after `Field` was renamed
to `FieldTree`.
2026-01-22 22:19:10 +00:00
Miles Malerba ae0c59028a refactor(forms): rename field to fieldTree in FieldContext and ValidationError
BREAKING CHANGE:
2025-12-16 10:26:22 -08:00
kirjs 3a01d72850 refactor(forms): convert Signal Forms errors to use RuntimeError
- Added 13 new error codes to forms/src/errors.ts (1900-1999)
- use RuntimeError
2025-12-15 11:44:02 -08:00
Miles Malerba 348f149e8b feat(forms): pass field directive to class config
Updates signal forms to pass the full `Field` directive to the class
configuration functions, rather than just the state. This allows
developers to take the element as well as the state into consideration
when deciding classes to apply.

Closes #65762

BREAKING CHANGE: The shape of `SignalFormsConfig.classes` has changed

Previously each function in the `classes` map took a `FieldState`. Now
it takes a `Field` directive.

For example if you previously had:
```
provideSignalFormsConfig({
  classes: {
    'my-valid': (state) => state.valid()
  }
})
```

You would need to update to:
```
provideSignalFormsConfig({
  classes: {
    'my-valid': ({state}) => state().valid()
  }
})
```
2025-12-12 08:07:53 -08:00
kirjs 179b4cba67 fix(forms): Reuse key in parent in compat structure
This fixes the "Compat nodes do not use keyInParent." error when trying to bind compatField.
2025-12-09 12:59:22 -08:00
Miles Malerba ebc5c2b083 feat(forms): redo the signal forms metadata API
This PR makes a number of changes to the metadata API to address design
flaws in the previous API. Some of the changes include:

- Replaces the previous `MetadataKey` and `AggregateMetadataKey` with a
  single unified `MetadataKey` that is used for all metadata.
- The new `MetadataKey` is only defined for fields that explicitly set
  it in their schema logic
- All metadata now has reducer / aggregate behavior
- The new `MetadataKey` has an option to create a managed key which
  wraps the result of its computed aggregate into some other structure
  such as a `Resource` or `linkedSignal`
- There are now two APIs to create metadata keys
  - `createMetadataKey` for pure computed metadata
  - `createManagedMetadataKey` for metadata that manages its computation
    internally
2025-12-09 09:21:41 -08:00
Kirill Cherkashin 5be33048cc refactor(forms): Break logic.ts into separate files
This would make it easier to navigate
2025-12-04 11:33:20 -08:00
Alex Rickabaugh b96f65a963 fix(forms): memoize reads of child fields in signal forms (#65802)
Previously, navigating a `FieldTree` in signal forms involved reactive reads
of the value of the parent field(s), both directly and via `.childrenMap()`.

This meant that on _any_ change to the value of a field, reactive
notifications would trigger updates of computeds, reruns of effects, etc.
So for example, this effect would run on every change to the form:

```ts
const f = form(signal({data: 'abc', unrelated: 0}));
effect(() => {
  // accessing f.data incurs a dependency on f().value() which changes
  // on every change in the whole form
  console.log(f.data().value());
});
```

This is deeply counterintuitive and troublesome when attempting to write
effect logic, and also results in `computed`s unnecessarily updating.

This change introduces the concept of a "reader" computed, which memoizes
the access of a field at a given key via the reactive graph. With this, the
same `f.data` access above now depends on the `data` reader in `f` only,
which is effectively a constant computed. As a result, the effect only
reruns on changes to `data`'s value, as intended.

PR Close #65802
2025-12-03 12:52:42 -08:00
Matthieu Riegler a784995a98 docs(docs-infra): Show examples on function overloads 2025-12-02 12:13:11 +01:00
Miles Malerba c70e246c23 feat(forms): add DI option for classes on Field directive
Adds a DI configuration option for signal forms that allows the
developer to specify CSS classes that should be automatically added
by the `Field` directive based on the field's status.
2025-11-25 10:33:38 -05:00
Miles Malerba fc1ef79ad4 build(forms): expose signal forms compat package
Hooks up @angular/forms/signals/compat to be released and have its docs
published
2025-11-14 09:23:36 -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
kirjs 60447945bc refactor(forms): add compatForm
This allows using reactive form controls in signal forms
2025-11-06 10:51:28 -08:00