Commit graph

728 commits

Author SHA1 Message Date
Leon Senft
46dbd18566 refactor(forms): remove customError()
Remove the `customError` function and `CustomValidationError` type.

These were made obsolete by support for returning plain object literals
as custom errors.

This also catches few `field` properties that were missed in the
renaming to `fieldTree`.
2026-01-07 15:07:30 -05:00
SkyZeroZx
b2f417585a docs: update angular.io links to angular.dev in comments, TSDoc, and warnings 2026-01-07 14:12:15 -05:00
SkyZeroZx
e7d99f02cb fix(forms): clean up abort listener after timeout
Removes the abort event listener once the debounce timeout completes.

This avoids lingering listeners, prevents potential memory leaks, and ensures
the abort logic runs at most once.
2026-01-07 14:07:25 -05:00
Miles Malerba
2d85ae5811 feat(forms): add [formField] directive
This will replace the `[field]` directive, since `[field]` is a very
generic name for signal forms to commandeer

refactor(forms): hook up `formField` directive in compiler

Hooks up the `formField` direcive to get the same treatment as the
`field` directive in the compiler.

apply updated formatting
2026-01-06 17:21:06 -05:00
Leon Senft
82a2de201f refactor(forms): bind native properties on interop controls
The framework will now bind all field state properties to their
corresponding native properties (if any) on interop form controls (those
using `ControlValueAccessor`), excluding those handled explicitly by
`ControlValueAccessor` such as `disabled`.
2026-01-06 13:12:17 -05:00
Leon Senft
15bddbdcda refactor(forms): bind field properties to all directives on interop controls
The framework will now bind the field state properties to all matching
directive inputs on form controls using Reactive Forms'
`ControlValueAccessor`.
2026-01-06 13:12:17 -05:00
Leon Senft
0c8f15d546 refactor(forms): bind field properties to all directives on custom controls
The framework will now bind the field state properties to all matching
directive inputs on custom form controls.
2026-01-06 13:12:17 -05:00
Leon Senft
c149f47ef6 refactor(forms): bind field properties to all directives on native controls
Since the `Field` directive manages binding `FieldState` properties to
the underlying form control automatically, the type checker prohibits
explicit bindings to the same properties to avoid conflicts. This proved
problematic in cases where developers wanted to bind these properties to
the inputs of other directives on form controls. Now the framework will
bind the field state properties to all matching directive inputs on
native controls.

Fix #65617
2026-01-06 13:12:17 -05:00
Shuaib Hasan Akib
b93d5ec27a docs(forms): add documentation links to Signal Forms API
Add @see JSDoc tags to Signal Forms API functions and classes to link to the essentials guide and detailed documentation pages. This improves discoverability of Signal Forms documentation from the API reference.
2026-01-06 10:34:46 -05:00
Shuaib Hasan Akib
456ca35906 docs(forms): fix duplicate validator reference titles in AbstractControl
The removeValidators and hasValidator methods both had identical "Reference to a ValidatorFn" section titles, causing duplicate entries in the API documentation table of contents.
2026-01-05 19:26:31 -05:00
Charles Lyding
ab0fb1633c build(forms): setup infrastructure for code examples
This commit introduces the infrastructure for embedding code examples within Angular packages, enabling them to be consumed by the Angular CLI's MCP server.

The `@angular/forms` package is the first to adopt this new feature. A new `ng_examples_db` Bazel rule is used to build a SQLite database from markdown files containing code examples. This database is then included in the published npm package.

The `package.json` for `@angular/forms` has been updated with an `angular.examples` field to allow tooling to discover and use the code examples.
2026-01-05 12:36:00 -05:00
SkyZeroZx
b735c45974 docs: update tsDoc code examples to use TypeScript syntax highlighting 2026-01-05 12:31:44 -05:00
SkyZeroZx
b1a0d1d8e2 docs: Add form control state management and event options 2026-01-05 12:16:09 -05:00
Shuaib Hasan Akib
e3781cd88a docs: fix incorrect FormArray example link
Update the typed forms guide link to point to the correct
FormArray section instead of the FormControl getting started example.
2026-01-02 08:28:17 +01:00
kirjs
282220d032 fix(forms): Support readonly arrays in signal forms
This would allow using `readonly Array<...>` in types
2026-01-02 08:26:06 +01:00
SkyZeroZx
398078f456 refactor(forms): add MANAGED_METADATA_LAZY_CREATION runtime error
Add the `MANAGED_METADATA_LAZY_CREATION` `RuntimeError`  in signal form,
making it tree-shakeable.
2026-01-02 08:17:39 +01:00
Shuaib Hasan Akib
abb179466d refactor(core): mark VERSION exports as pure for better tree-shaking
Adds a PURE annotation to VERSION constants, enabling tree-shaking
and keeping the implementation consistent with other pure exports.
2026-01-02 08:13:40 +01:00
Leon Senft
e7745dc9dd test(forms): add test coverage for binding errors to custon controls
This input was missing dedicated test coverage.
2026-01-02 08:09:35 +01:00
Leon Senft
8832fc01b2 test(forms): remove duplicate test case
We had two test cases that tested the `Field` directive synchronizes
with a custom checkbox control components.
2026-01-02 08:09:35 +01:00
Leon Senft
1a4c3eb1d0 fix(forms): allow custom controls to require pending input
* Allow custom controls to make `pending` a required input
* Refactor test for `pending` input to be consistent with other control
  properties
* Test that `pending` inputs are reset when the field binding changes
2026-01-02 08:09:35 +01:00
Leon Senft
9ad603fa11 test(forms): remove obsolete test case
There's no longer a need to test that inputs are set before
initialization on custom controls now that required inputs are
supported.
2026-01-02 08:09:35 +01:00
Leon Senft
4f73a350a5 test(forms): refactor and improve test coverage for disabledReasons input
* Refactor test for `disabledReasons` input to be consistent with other control
  properties
* Test that `disabledReasons` inputs are reset when the field binding changes
2026-01-02 08:09:35 +01:00
Leon Senft
89c37f1f7f fix(forms): allow custom controls to require dirty input
* Allow custom controls to make `dirty` a required input
* Refactor test for `dirty` input to be consistent with other control
  properties
* Test that `dirty` inputs are reset when the field binding changes
2026-01-02 08:09:35 +01:00
Leon Senft
b563b5cfc2 test(forms): refactor and improve test coverage for invalid input
* Refactor test for `invalid` input to be consistent with other control
  properties
* Test that `invalid` inputs are reset when the field binding changes
2026-01-02 08:09:35 +01:00
Leon Senft
82edf18427 fix(forms): allow custom controls to require hidden input
* Allow custom controls to make `hidden` a required input
* Refactor test for `hidden` input to be consistent with other control
  properties
* Test that `hidden` inputs are reset when the field binding changes
2026-01-02 08:09:35 +01:00
Leon Senft
cb09fb8308 fix(forms): support custom controls with non signal-based models
* Recognize directives with non signal-based models as valid custom controls
* Relax type checker to allow non signal-based models

The `FormValueControl` and `FormCheckboxControl` interfaces still
require a `model()`-input, however, a custom control need not implement
either interface to be bound by the `Field` directive.

All of the following examples can be used to define a custom control:

```ts
// Preferred: model()
class MyFormControl implements FormValueControl<string> {
  readonly value: model.required<string>();
}

// Supported: input() + output()
class MyFormControl {
  readonly value: input.required<string>();
  readonly valueChange: output<string>();
}

// Supported: @Input() + @Output()
class MyFormControl {
  @Input({required: true}) value!: string;
  @Output() valueChange: new EventEmitter<string>();
}
```

The latter two may still choose to implement `FormUiControl` for other
properties, but again it is not required.

Fix #65478
2026-01-02 08:09:03 +01:00
Miles Malerba
244b54c9bf refactor(forms): rename server errors to submission errors
This better reflects the intent to indicate an error during submission,
regardless of whether the error came from the server or not.
2025-12-17 15:42:07 -08:00
Matthieu Riegler
6270bba056 ci: reformat files
This is after we've slightly changed a rule in #66056
2025-12-16 14:44:19 -08:00
Miles Malerba
ae0c59028a
refactor(forms): rename field to fieldTree in FieldContext and ValidationError
BREAKING CHANGE:
2025-12-16 10:26:22 -08:00
Kirill Cherkashin
193aa332fc
docs(forms): improve JSDoc for Signal Forms Schema types
Improved documentation for Schema, SchemaFn, and SchemaOrSchemaFn types
with clearer descriptions and usage examples.
2025-12-15 14:02:35 -08:00
Matthieu Riegler
44d4439bc4 refactor(forms): add signal forms to the type tests
This will ensure that signal forms emit valid typings.
Also this commits moves `@standard-schema/spec` from peer-dep to regular dep
2025-12-15 11:44:54 -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
Miles Malerba
d0097f7d0c fix(forms): fix signal forms type error
Removes the `implements` clause on the `Field` directive since it is
causing type errors for people.
2025-12-11 15:33:12 -08:00
Matthieu Riegler
14713d0992 fix(forms): allow resetting with empty string
Both empty string and null values should be accepted when resetting

fixes #65949
2025-12-10 10:04:54 -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
aff8b248b3
feat(forms): expose element on signal forms Field directive
This allows for easier focusing of the relevant element based on the
`field` property of a `ValidationError`
2025-12-09 09:47:51 -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
cexbrayat
9fe9566581 fix(forms): add signals for dirty, hidden, and pending states in custom controls
FormUiControl states that hidden, pending and dirty will be bind in custom controls, but this is currently not the case.

Fixes #65575
2025-12-08 10:30:43 -08:00
Anuj Chhajed
96b79fc393 refactor(core): correct all typeof ngDevMode comparison patterns introduced by #63875
This change replaces all remaining occurrences of `typeof ngDevMode !== undefined`
with the correct `typeof ngDevMode !== 'undefined'` form. This aligns the codebase
with JavaScript typeof semantics and maintains consistency with other Angular code.
2025-12-08 10:30:01 -08:00
Miles Malerba
efde94a525 refactor: followup cleanup for #65758 fallout
Fixes some issues left behind by recent refactor
2025-12-08 10:27:59 -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
Alex Rickabaugh
37472e9185 refactor(forms): simplify child field creation (#65802)
Previously, several values were being passed into the creation of
`FieldNodeStructure`s that were only used in the creation of child nodes.
Separately, we also passed a `createChildNode` function which these values
were passed back into.

Instead, this moves the small bit of logic from structure.ts behind the
`createChildNode` callback, which reduces the passing of values back-and-
forth and gives `createChildNode` a much more suitable signature.

PR Close #65802
2025-12-03 12:52:42 -08:00
SkyZeroZx
7d1e502345 feat(forms): Allows transforms on FormUiControl signals
Extends the `FormUiControl` interface to allow `InputSignalWithTransform` in addition to `InputSignal` for its properties.

Fixes #65756
2025-12-03 15:13:01 +01:00
Leon Senft
e6d5632a30 perf(core): tree shake unused dynamic [field] binding instructions (#65599)
Move the instructions used to dynamically bind a `Field` directive to a
form control onto the `Field` itself. This way the instructions are only
retained if the app uses the `Field` directive.

PR Close #65599
2025-12-03 15:10:49 +01:00
Leon Senft
cd7ae7e2ce fix(forms): support dynamic [field] bindings (#65599)
Support binding a `Field` directive to a component created dynamically
with `createComponent()`.

Fix #64632

PR Close #65599
2025-12-03 15:10:49 +01:00
Matthieu Riegler
f35b2ef47c refactor(compiler): Generate the controlCreate instruction after the native element has been created
This is necessary to exclude a race condition where the MutationObserver initialized by the instruction fired before the inputs are binded.

fixes #65678
2025-12-02 12:59:49 +01:00
Matthieu Riegler
a784995a98 docs(docs-infra): Show examples on function overloads 2025-12-02 12:13:11 +01:00
kirjs
dec222d4d7 refactor(forms): Make reset take value
Now you can do form.reset({name: 'cat', age: 4});
2025-11-25 10:51:35 -05:00