Currently we maintain the pathKeys internally, but do not expose them
through the `FieldContext`, this PR updates the `FieldContext` to expose
this property.
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.
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.
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.
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 ❌
```
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
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
Renames logic functions related to metadata to align with the new
"metadata" name. Notably:
- `property(...)` => `metadata(...)`
- `aggregateProperty(...)` => `aggregateMetadata(...)`
PR Close#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
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
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
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
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
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
When a directive injects a `ViewContainerRef`, the runtime inserts a container that was throwing off the logic that recognizes native controls.
These changes switch to check if the node is a native control through the `TNode`. This also makes it a bit less prone to breaking during SSR.
Fixes#64362.
PR Close#64368
Caches information about the kind of form control that a `TNode`
represents in `TNodeFlags`. This avoids redundant computations on
subsequent template create and update passes.
Renames the `INVALID_CONTROL_HOST` error code to
`INVALID_FIELD_DIRECTIVE_HOST` for clarity and adds a test for it.
PR Close#64351
Renames the control directive and the input that users set to bind a
field to a UI control.
Previously users would do:
```
<input [control]="someField">
```
Now users will do:
```
<input [filed]="someField">
```
PR Close#64300
Fix a bug where the min property of a form field was not correctly
propagated to custom controls. Also ensure that min and max are only
bound to native input elements that support them.
PR Close#63884
Fix several typos caught by the added test cases:
* `disabled` attribute for native controls
* `readonly` property for custom controls
Note that the `name` test cases have been marked `pending()` due to
https://github.com/angular/angular/issues/63882.
PR Close#63884
Move most of the implementation of the `Control` directive into core
framework instructions. This allows field state changes to be propagated
to their corresponding UI controls directly during execution of a
template update block, instead of relying on `effect()`s to synchronize
each change later during the update (and too late in the case of
required inputs).
* Define a private API in `@angular/core` for signal forms to implement:
* `ɵControl` for the `Control` directive.
* `ɵFieldState` for the control's associated `FieldState`.
* Emit specialized instructions when compiling a `[control]` binding:
* `ɵɵcontrolCreate` sets up the `ɵControl` directive if present,
determines whether it's bound to a native control element or a
custom control component, and adds the appropriate event listeners
to notify the `ɵFieldState` of UI changes.
* `ɵɵcontrol` propagates changes from `ɵFieldState` properties to their
corresponding UI control properties (in additional to binding the `control`
property itself).
PR Close#63773
There are two primary reasons for this renaming:
1. It better reflects the actual nature of the proxy object, namely that
the form is represented as a tree, and that this object is used for
navigating the tree structure (while `FieldState` is used for getting
the state at a particular point in the structure).
2. This frees up the name `Field` to be used for the directive that
binds a `FieldTree` to a UI control.
PR Close#64214
Since those are top level APIs, `ngDevMode` might not be available at runtime if they're invoked before the variable is set.
fixes#62796
PR Close#63875
This updates tests and examples only to prepare for zoneless by default.
These changes were identified and made as part of #63382. Anything that
failed gets `provideZoneChangeDetection` unless the fixes were easily
and quickly determined.
It also adds the zoneless provider to the `initTestEnvironment` calls
for tests in this repo to prevent regressions before #63382 is merged.
PR Close#63668
Moves the control directive under api/ since it is part of the public
API. Also removes the interop abstract control from the public API
PR Close#63616
Adds additional handling for input types that handle numbers:
- number
- range
- datetime-local
As well as input types that handle dates:
- date
- month
- week
- time
- *not* datetime-local which does not allow setting valueAsDate
This PR allows binding either a `Field<string>` or `Field<number>` to
the number type inputs and a `Field<string>`, `Field<numebr>`, or
`Field<Date | null>` to the date type inputs. Binding uses the
corresponding `.value`, `.valueAsNumber`, or `.valueAsDate` according to
the type of the `Field`.
When reading new values out of the input, the `Control` directive will
read the property that aligns with the type of the existing value in the
`Field` and write back the same type.
PR Close#63585
This allows passing errors and disabled reasons that did not originate
from `@angular/forms/signals` in case the the control is being used
separately from the forms system
PR Close#63455
Removes custom handling of emptiness in several of the validators and
replaces it with a common `isEmpty` check. The common empty check
considered the following values to be empty: `null`, `undefined`, `''`,
`false`, `NaN`
Generally most validators should treat an empty value as valid. This
aligns with both the behavior or native HTML validators and reactive
forms validators.
As an example, consider an optional email field. If the email validator
considered empty string to be an invalid email, there would be no way
for the user to not enter it.
There are several exceptions to this rule:
- `required` whose entire purpose is to ensure that the field is *not*
empty
- `validateStandardSchema` which should subject all values including
empty ones to the specified standard schema. It is up to the schema to
decide whether an empty value is valid or not
- `validate`/`validateAsync` which leaves it up to the user's custom
validation logic to decide if an empty value is valid.
PR Close#63456
This commit introduces an experimental version of a new signal-based forms API for Angular. This new API aims to explore how signals can be leveraged to create a more declarative, intuitive, and reactive way of handling forms.
The primary goals of this new signal-based approach are:
* **Signal-centric Design:** Place signals at the core of the forms experience, enabling a truly reactive programming model for form state and logic.
* **Declarative Logic:** Allow developers to define form behavior, such as validation and conditional fields, declaratively using TypeScript. This moves logic out of the template and into a typed, testable schema.
* **Developer-Owned Data Model:** The library does not maintain a copy of data in a form model, but instead read and write it via a developer-provided `WritableSignal`, eliminating the need for applications to synchronize their data with the form system.
* **Interoperability:** A key design goal is seamless interoperability with existing reactive forms, allowing for incremental adoption.
* **Bridging Template and Reactive Forms:** This exploration hopes to close the gap between template and reactive forms, offering a unified and more powerful approach that combines the best aspects of both.
This initial version of the experimental API includes the core building blocks, such as the `form()` function, `Field` and `FieldState` objects, and a `[control]` directive for binding to UI elements. It also introduces a schema-based system for defining validation, conditional logic, and other form behaviors.
Note: This is an early, experimental API. It is not yet complete and is subject to change based on feedback and further exploration.
Co-authored-by: Kirill Cherkashin <kirts@google.com>
Co-authored-by: Alex Rickabaugh <alxhub@users.noreply.github.com>
Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
Co-authored-by: Dylan Hunn <dylhunn@gmail.com>
Co-authored-by: Michael Small <michael-small@users.noreply.github.com>
PR Close#63408
The `FormArrayDirective` will allow to have a `FormArray` as a top-level form object.
* `NgControlStatusGroup` directive will be applied to the `FormArrayDirective`
* `NgForm` will still create a `FormGroup`
Fixes angular#30264
BREAKING CHANGE: This new directive will conflict with existing FormArray directives or formArray inputs on the same element.
PR Close#55880
Enables users to add an array of FormControls to a FormArray using its existing .push() method,
instead of pushing each new FormControl one by one triggering events along the way.
PR Close#57102
Simplifies destruction logic by relying directly on the injector's destroyed state.
Eliminates unnecessary retrieval of a separate destroy reference
PR Close#62738
We defer the update until after rendering
is complete for two reasons: first, to avoid repeatedly calling
`writeValue` on every option element until we find the selected one
(could be the very last element). Second, to ensure that we perform the
write after the DOM elements have been created (this doesn't happen
until the end of change detection when animations are enabled).
This is needed to efficiently set the select value when adding/removing options. The
previous approach resulted in exponentially more `_compareValue` calls than the number
of option elements (issue angular#41330).
Finally, this PR fixes an issue with delayed element removal when using the animations
module (in all browsers). Previously when a selected option was removed (so no option
matched the ngModel anymore), Angular changed the select element value before actually
removing the option from the DOM. Then when the option was finally removed from the DOM,
the browser would change the select value to that of the first option, even though it
didn't match the ngModel (issue angular#18430). Note that this is still
somewhat of an application problem when using `ngModel`. The model value
still needs to be updated to a valid value when the selected value is
deleted or it will be out of sync with the DOM.
Fixes#41330, fixes#18430.
PR Close#61949
Instead of dev-infra maintaining a custom ESBuild + Terser pipeline that
tries to emulate the Angular CLI, we are switching the bundling core
tests to a new rule that really leverages the Angular CLI.
This involves some file renames and small adjustments. In addition, we
leverage the updated symbol tracking rule to output new goldens that can
work with multiple bundle files (as generated by the Angular CLI;
especially with defer and its "lazy" chunks).
PR Close#61566
This change also decorelate the `reset` type argument from `TValue` by adding a 3rd generic parameter to `AbstractControl`.
This improves the typings overall.
PR Close#55860
These helpers are often imported by various tests throughout the
repository, but the helpers aren't exported/exposed from the public
entry-point; even though they confusingly reside in there.
This commit fixes this, and moves the helpers into
`packages/private/testing`. This is a preparation for the `ts_project`
migration where we don't want to leverage deep imports between packages.
PR Close#61472
This aligns with how angular/components marks their hidden APIs.
`@nodoc` has been broken since the switch to adev, this change should
properly hide the APIs again.
PR Close#61194
We don't need this tooling anymore because we are already validating
that there are no circular dependencies via the `ng-dev` tooling that
checks `.ts` files directly.
Also these tests never actually failed to my knowledge.
PR Close#61156
This commit moves zoneless from experimental to developer preview.
* Update tag on provider API
* Remove "experimental" from provider name
* Move documentation from "experimental features" to "Best practives ->
Performance" (at least temporarily until there is a better place)
BREAKING CHANGE: `provideExperimentalZonelessChangeDetection` is
renamed to `provideZonelessChangeDetection` as it is now "Developer
Preview" rather than "Experimental".
PR Close#60748
Currently, only forms created with `FormGroupDirective` emit events on
form submission and resetting. This commit extends this behavior to
Template-driven forms also.
Related to #58894
PR Close#60887
Adds the `markAllAsDirty` method to the `AbstractControl` class. This method will mark the control and all its
descendants as dirty.
I pretty much just duplicated the behaviour and tests of `markAllAsTouched`.
Fixes#55990
PR Close#58663
Node.js v18 will reach End-of-Life on April 30, 2025, and will no longer be supported in Angular v20.
Node.js Release Schedule: https://github.com/nodejs/release#release-schedule
BREAKING CHANGE:
- Angular no longer supports Node.js v18.
- Node.js versions 22.0 to 22.10 are also no longer supported.
Before upgrading to Angular v20, ensure the Node.js version is at least 20.11.1.
For the full list of supported versions, visit: https://angular.dev/reference/versions
PR Close#60545
In the cases multiple updates with `emitEvent:false` were requests, `shouldHaveEmitted` wasn't passed correctly and and requests updates were trashed.
This commit fixes this.
Fixes#56999
PR Close#57098
In this commit, we remove `_checkFormPresent` because it is a no-op in production. We move its body directly into `ngOnChanges` to eliminate the redundant method from the prototype. Previously, `_checkFormPresent` was being called with no body in production each time `ngOnChanges` was executed.
PR Close#59741
Previously, using `Validators.required`, `Validators.minLength` and `Validators.maxLength` validators don't work with sets because a set has the `size` property instead of the `length` property. This change enables the validators to be working with sets.
PR Close#45793
The `_checkParentType` bodies are wrapped with `ngDevMode`, meaning they act as no-ops in production. We can wrap the actual calls to `_checkParentType` with `ngDevMode` to prevent calling no-op functions in production
PR Close#59489
When setting `"useDefineForClassFields": false`, static fields are compiled within a block that relies on the `this` context. This output makes it more difficult for bundlers to treeshake and eliminate unused code.
PR Close#58297
In order to investigate the performances of SSR, this commit introduces a benchmark suite which will measure several step of the rendering.
PR Close#57647
This commit makes the host bindings of `NgControlStatus[Group]`
compatible with `OnPush` components. Note that this intentionally _does not_
expose any new APIs in the forms module. The goal is only to remove
unpreventable `ExpressionChangedAfterItHasBeenCheckedError` in the forms
code that developers do not have control over.
PR Close#55720
With this change, If an async validator that should have emitted was cancelled by a non-emitting validator, the status change will be reported on the `AbstractControl.events` observable.
This issue can happen when a `FormControl` is added to a `FormGroup` and a FormGroupDirective/FormControlDirective trigger a non-emitting validation (which cancels the initial validator execution).
Note: The behavior remains the same of the existing `statusChanges` observable as the change was too breaking to land in G3.
fixes: angular#41519
PR Close#55134
This commit deprecates the aliases for the control events to ease the changes in G3
A follow-up commit will remove those deprecated entries.
PR Close#55698
Since we aren't using clang anymore, we can remove the comments and the workarounds that were in place to prevent it from doing the wrong thing.
PR Close#55750
When a formControlName is used without a parent formGroup, an error is
logged in the console. Before this commit, there was no information
about which control had the issue. Now, it's reported and the
troubleshoot is much faster.
PR Close#55397
This commit introduces a new method to subscribe to on every `AbstractControl` subclass.
It allows to track value, pristine, touched and status changes on a given control.
Fixes#10887
PR Close#54579
This change aligns the stability of `ComponentFixture` with that of
`ApplicationRef`, preventing confusing differences between the two as
more APIs start using the `PendingTasks` that may not be tracked by
`NgZone`.
BREAKING CHANGE: `ComponentFixture.whenStable` now matches the
`ApplicationRef.isStable` observable. Prior to this change, stability
of the fixture did not include everything that was considered in
`ApplicationRef`. `whenStable` of the fixture will now include unfinished
router navigations and unfinished `HttpClient` requests. This will cause
tests that `await` the `whenStable` promise to time out when there are
incomplete requests. To fix this, remove the `whenStable`,
instead wait for another condition, or ensure `HttpTestingController`
mocks responses for all requests. Try adding `HttpTestingController.verify()`
before your `await fixture.whenStable` to identify the open requests.
Also, make sure your tests wait for the stability promise. We found many
examples of tests that did not, meaning the expectations did not execute
within the test body.
In addition, `ComponentFixture.isStable` would synchronously switch to
true in some scenarios but will now always be asynchronous.
PR Close#54949
The `RadioControlRegistry` was only provided in a module, providedIn: 'root' fixes that issue.
Fixes#54117
Co-authored-by: sr5434 <118690585+sr5434@users.noreply.github.com>
PR Close#54130
A lot of our tests are wrapped in `{}` which serves no purpose, aside from increasing the nesting level and, in some cases, causing confusion. The braces appear to be a leftover from a time when all tests were wrapped in a `function main() {}`. The function declaration was removed in #21053, but the braces remained, presumably because it was easier to search&replace for `function main()`, but not to remove the braces at the same time.
PR Close#52239
This adds `generate_api_docs` targets to all of the packages for which we publish api reference docs. One known issue here is that any type information that comes from another package (e.g. router depending on core) currently resolve to `any` because the other sources are not available in the program. This can be tackled in a follow-up commit.
This commit also updates the install patch for `@angular/build-tools` to use the local version of compiler-cli.
PR Close#52034
Non typed forms allow to pass null to nested groups when calling `formGroup.reset()`, this commit prevent an undefined access.
fixes#20509
PR Close#48830
The `Writable` type is usefull when we want overwrite readonly properties and we still want to maintain code navigation/reference. It should be use instead of `any` type assertions for example.
PR Close#49754
BREAKING CHANGE: Node.js v16 support has been removed and the minimum support version has been bumped to 18.13.0.
Node.js v16 is planned to be End-of-Life on 2023-09-11. Angular will stop supporting Node.js v16 in Angular v17. For Node.js release schedule details, please see: https://github.com/nodejs/release#release-schedule
PR Close#51755
Currently internally Angular has some customized tsconfig files, because we don't align with the tsconfig of the rest of g3. These changes enable `noImplicitReturns` and `noPropertyAccessFromIndexSignature` to align better with the internal config.
PR Close#51728
We enabled a lint rule internally to require that multi-provided
`InjectionToken`s have a `readonly` array type, the tokens in this
PR do not follow this rule and are causing lint violations.
Fixes#51124
PR Close#51125
According to the HTML specification most attributes are defined as strings, however some can be interpreted as different types like booleans or numbers. [In the HTML standard](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes), boolean attributes are considered `true` if they are present on a DOM node and `false` if they are omitted. Common examples of boolean attributes are `disabled` on interactive elements like `<button>` or `checked` on `<input type="checkbox">`. Another example of an attribute that is defined as a string, but interpreted as a different type is the `value` attribute of `<input type="number">` which logs a warning and ignores the value if it can't be parsed as a number.
Historically, authoring Angular inputs that match the native behavior in a type-safe way has been difficult for developers, because Angular interprets all static attributes as strings. While some recent TypeScript versions made this easier by allowing setters and getters to have different types, supporting this pattern still requires a lot of boilerplate and additional properties to be declared. For example, currently developers have to write something like this to have a `disabled` input that behaves like the native one:
```typescript
import {Directive, Input} from '@angular/core';
@Directive({selector: 'mat-checkbox'})
export class MatCheckbox {
@Input()
get disabled() {
return this._disabled;
}
set disabled(value: any) {
this._disabled = typeof value === 'boolean' ? value : (value != null && value !== 'false');
}
private _disabled = false;
}
```
This feature aims to address the issue by introducing a `transform` property on inputs. If an input has a `transform` function, any values set through the template will be passed through the function before being assigned to the directive instance. The example from above can be rewritten to the following:
```typescript
import {Directive, Input, booleanAttribute} from '@angular/core';
@Directive({selector: 'mat-checkbox'})
export class MatCheckbox {
@Input({transform: booleanAttribute}) disabled: boolean = false;
}
```
These changes also add the `booleanAttribute` and `numberAttribute` utilities to `@angular/core` since they're common enough to be useful for most projects.
Fixes#8968.
Fixes#14761.
PR Close#50420
This commit updates the minimum supported Node version across packages from 16.13.0 -> 16.14.0 to ensure compatibility with dependencies.
PR Close#49771
Previously, this PR cleaned up a bug introduced by #48679. However, since that PR needed to be rolled back, this PR now just checks in the test, to prevent that issue from re-occurring in the future.
PR Close#49693
This commit updates parts of the FW to be ES2022 complaint.
These changes are needed to fix the following problems problems with using properties before they are initialized.
Example
```ts
class Foo {
bar = this.buz;
constructor(private buz: unknown){}
}
```
PR Close#49559
This commit updates parts of the FW to be ES2022 complaint.
These changes are needed to fix the following problems problems with using properties before they are initialized.
Example
```ts
class Foo {
bar = this.buz;
constructor(private buz: unknown){}
}
```
PR Close#49332
The private util `isObservable` was actually just testing the same thing as`isSubscribable()`. As the implementation is closer to the function's name, let's only keep ``isSubscribable`.
PR Close#49295
BREAKING CHANGE: Node.js v14 support has been removed
Node.js v14 is planned to be End-of-Life on 2023-04-30. Angular will stop supporting Node.js v14 in Angular v16. Angular v16 will continue to officially support Node.js versions v16 and v18.
PR Close#49255