Repository navigation
perf(forms): avoid creating unused field injectors - #71737
Merged
Merged
Conversation
JeanMeche
reviewed
Oct 30, 2026
| untracked(() => { | ||
| for (const key of this.node.logicNode.logic.getMetadataKeys()) { | ||
| if (key.create) { | ||
| runInInjectionContext(this.node.structure.injector, () => { |
Member
There was a problem hiding this comment.
Can we add a short comment that mention accessing the injector actually instantiates one.
Contributor
Author
There was a problem hiding this comment.
Added a short comment explaining that accessing structure.injector creates the field injector. Thanks!
JeanMeche
approved these changes
Oct 30, 2026
Create field injectors only when managed metadata needs an injection context, and destroy only injectors that have already been created. Plain metadata and built-in synchronous rules otherwise allocate an unused injector per field, including during removal. Preserve eager managed metadata creation, the shared field injection context, and teardown of asynchronous validators. Fixes angular#71623
splincode
force-pushed
the
perf/signal-form-field-injectors
branch
from
October 30, 2026 14:39
0e0faaf to
8259122
Compare
JeanMeche
approved these changes
Oct 30, 2026
5 tasks done
Member
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Checklist
PR Type
What is the current behavior?
Fields create an injector as soon as they have any metadata, even when every key is plain metadata. Built-in rules such as
required(),min(), anddebounce()therefore allocate injectors they never use. Removing a field without an injector also creates one just to destroy it.Fixes #71623.
What is the new behavior?
Enter the field's injection context only for a managed key's
createcallback, and destroy only an existing field injector. Managed factories still run eagerly, in order, outside the reactive context, and share the same injector for a field.Does this PR introduce a breaking change?
No public API or scheduling changes. Field removal still destroys managed metadata and cancels pending HTTP validation.
Validation
ng-dev format, Prettier, andgit diff --checkpass.Production-build check
An order-entry page renders 1,000 rows, with a
required()SKU input and amin()quantity input in each row. Both optimized builds use the same application and instrumentation; one has these two changes transplanted into Angular Forms 22.2.3 (CLI/build 22.2.4). Node 24.22.1, Chromium 158.0.8117.0, seven alternating baseline/patched pairs in fresh browser contexts.This saves about 0.90 MiB of live JS heap in that fixture. These are post-GC JS heap deltas, not peak memory or process RSS.
Median create-to-stable time was 259.8 / 254.4 ms; removal-to-stable was 60.8 / 56.3 ms. Samples overlap, so the concrete result is the avoided allocation and memory reduction, not a general latency guarantee. A separate 50-row control also preserved input count, editing/model propagation, validation, and row removal in all seven pairs. No page errors occurred.
The benchmark counts
Injector.createcalls using identical instrumentation in both builds. The application checks input rendering, required validation, edit propagation and successful removal. The installed Forms bundle was restored after building. The benchmark/support files are outside this PR.