Repository navigation
Conversation
Destroy remaining field injectors with the form's chosen injector so pending validation is aborted and managed metadata cleanup runs. Register root and compat structures for teardown while preserving cleanup of removed fields and the lifetime of an explicitly provided external injector. Fixes angular#71622
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?
Destroying a form's component or environment injector leaves its field injectors alive. Pending async validation is not aborted, managed metadata cleanup does not run, and environment-owned validation can start another request after teardown.
Fixes #71622.
What is the new behavior?
The field manager registers one destroy callback on the form's chosen injector and destroys its remaining fields. Root and compat structures join the existing registry. Removed fields are already deleted from that registry, and destroying an unused field does not allocate an injector.
This implements the manager-based approach described in the issue. An explicitly supplied injector remains the owner: destroying the calling component does not end the form's lifetime.
Does this PR introduce a breaking change?
Public API signatures and update scheduling are unchanged. The intended behavior change is cleanup of resources that previously survived their owner.
Validation
On main at
74961c662420ea02a5872bfa1a0ee3284526b887, all eight new lifecycle specs fail. With the fix://packages/forms/signals/test/node:test: 561 specs, 0 failures.//packages/forms/signals/test/web:test_chromium: 362 specs, 0 failures.git diff --checkpass.Coverage includes root and child cleanup before the first effect tick, component teardown, compat field injectors, exactly-once cleanup after removal, aborting pending validation, no validation loads after owner destruction, and the external-injector lifetime.
Local Bazel runs use telemetry opt-out, the system Java CA trust store, and a local test-runner workaround that supplies loopback only if this container rejects
os.networkInterfaces(). No test assertions or Angular behavior are altered by that workaround. The full monorepo suite was not run.The lazy-destroy guard also appears in #71737; it is needed here to avoid creating injectors during owner teardown.
Production-mode memory measurement
Same AOT/ES2022/minified signup fixture built from base
74961c662420ea02a5872bfa1a0ee3284526b887and PR head1726af8bba3b3c1f1a31221698e919071ff66c74, withngDevMode=falseandngJitMode=false. One username input,validateAsync(resource(fetch(...))), and 100 open/close cycles through@if.Seven alternating pairs per scenario, fresh contexts, 20 completed-request warmup cycles. Metric: increase in
Runtime.getHeapUsage().usedSizeover the post-warmup heap, after three forced GCs. Chromium 158.0.8117.0, Linux x64. Medians:Median paired reduction: 3.012 MiB (3,158,788 bytes; observed range 3,157,976–3,159,336 bytes). All 100 pending loads were aborted in every PR sample. Counts describe loader/fetch invocations; Chromium can queue/coalesce local HTTP/1.1 requests.
This is a controlled production-mode fixture, not a deployed application. The completed control and response-release check show retention while validation is pending; this is not a claim of permanent component leakage after responses arrive or of an RSS/peak-memory reduction.
Reproduction bundles, fixture source, raw samples and build hashes:
angular-71754-memory-evidence.zip