Use async/await in animation acceptance tests and share the animation frame helper. Remove the obsolete fakeAsync frame utility and its Bazel dependency.
The disabled toolbar example marks its alignment radio group `disabled`.
Toolbars are soft-disabled by default, so the widgets only receive
`aria-disabled` and stay clickable, which the guide describes as focusable
but unavailable.
Since #70516 moved selection into the app, each radio also carries a
`(click)="alignment.set(...)"` handler that never checks the disabled state.
Clicking a disabled radio therefore changes the selection. Before that change
the radios had no app listener and the library ignored clicks on disabled
items.
Drop the three click handlers from the permanently disabled group in the
basic, material and retro variants, the same way the disabled redo button in
the same template already has none.
The client decides whether a position in a TypeScript file belongs to a supported `@Component` field (`template`, `styles`, ...) with a lightweight `ts.Scanner` loop before forwarding requests to the language server and the HTML/CSS providers. `ts.Scanner#scan` doesn't handle template substitutions on its own: the `}` closing a `${...}` substitution is reported as a plain `CloseBraceToken` and the rest of the template literal is scanned as the start of a new one, swallowing everything up to the next backtick. As a result, once a template literal with substitutions appears in a file, every position after it is considered to be inside a template string, and TypeScript completions get polluted with HTML completions.
The scanner is now driven the same way the TypeScript parser does it, re-scanning the brace that closes a substitution as a `TemplateMiddle`/`TemplateTail` token.
In addition, the property assignment context previously only ended at a terminator found at the top level of the file, so it leaked from `template:` to every string literal that followed it inside the same decorator and class body. The context now ends at the first terminator found at the nesting depth where it was entered. Two kinds of strings were only recognized thanks to that leak, and only when declared after `template`: inline `styles` and the arguments of `@HostBinding`/`@HostListener` decorators, both of which the language service supports. They are now recognized explicitly, regardless of where they appear.
Fixes#65494
The `required()` validator treats `null`, `undefined`, `''`, `false` and `NaN` as empty, but the API
docs never defined "empty" at all and the validation guide listed only `null` and `''`.
Top-level Symbol() calls in router_resource.ts lacked /* @__PURE__ */
annotations, preventing bundlers like esbuild and Rollup from tree-shaking
the module and its dependencies when provideRouter() is used without
withRouterResources().
Fixes#70696
The `CSP_NONCE` example in the security guide imports `bootstrapApplication`
from `@angular/core`, which does not export it, so copying the snippet fails
with "has no exported member named 'bootstrapApplication'". It is exported from
`@angular/platform-browser`, which is where every other example in the docs
imports it from.
Keep `CSP_NONCE` on `@angular/core` and import `bootstrapApplication` from
`@angular/platform-browser`.
The Googler check moved out of `pack-and-upload-artifact` and into the
pull request labeling action, which runs in a privileged
`pull_request_target` context where secrets are available. The build job
runs on `pull_request` and never receives secrets, so it no longer needs
`angular-robot-key` or `triggering-label`, and the `bypassed_for_forks`
placeholder can go away. Bumps the dev-infra pins to pick up that change.
Previously, rollback recovery waited on hasValueOrResolved(), which checked whether the resource already had a value. This caused resources with values (e.g. defaultValue, existing values from prior navigations, or streamed emissions) to unfreeze prematurely while still in a loading state during rollback.
This change updates the rollback recovery check in the transactional snapshot effect to be solely determined by !source.isLoading().
Step 11 of the first-app tutorial ships a `housing-location.ts` whose heading
reads `{{ housingLocatio()).name }}`, a misspelled property followed by an
extra parenthesis, so the step's source fails to compile with a template
parser error. Step 10 reuses this directory as its answer.
It came in with #61686, which migrated the tutorial to signal inputs. Use
`housingLocation().name`, matching the rest of the template and every other
step.
The aria menubar guide content is 2.9 MB, over the 2 MiB
maxResponseBodySize set in #69379. The fetch fails during prerender,
the navigation error handler redirects to /404, and the prerendered
page is saved as that redirect. Opening /guide/aria/menubar directly
or refreshing it lands on the 404 page.
Raise the limit to 4 MiB. The combobox guide was also within 27 KB of
the old limit.
The `:not(.docs-card-container) .docs-card` selector was meant to give
standalone cards a vertical margin, but no element has the
docs-card-container class, so it matched every card. Cards in a grid
without a header picked up the 1rem margin on top of the 1.25rem grid
gap, leaving rows 52px apart while columns are 20px apart (for example on
/reference/migrations, /guide/i18n and /roadmap).
Apply the margin only to cards that are not direct children of a card
grid.
In @angular/ssr 22.2.0-next.7, beasties was made an external dependency rather
than vendored internally. Because hoist: false is configured and preserveSymlinks
is enabled, rules_js does not hoist beasties into adev/node_modules and
dev-app/node_modules.
This commit adds beasties as an explicit dependency for both adev and dev-app
so that it can be resolved during bundling.
This reverts commit ed8f16c078.
This caused a breakage in TGP because the changes (correctly) produced new
diagnostics on ` @for (a of (x | async) || []; track a) {` x is `any`. This
results in `<any> | async` which produces `unknown | null` and then the `@if`
finally narrows this down to `{}` which isn't iteratable.
Restore ChangeDetectionStrategy.OnPush that was removed in a previous commit, causing tests to fail due to ExpressionChangedAfterItHasBeenCheckedError.
Update createEsbuildAngularOptimizePlugin to use the new options object signature for JavaScriptTransformer.transformFile, and update the @angular/build dependency to 22.2.0-next.7.
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.
When noUncheckedIndexedAccess is not enabled, indexed accesses do not include undefined in the type. This relaxes the check for nullish coalescing similarly to optional chaining. Fixes#70655fixes#70655
`buildHeaderElement` interpolated the header into a string that is then parsed
as HTML, so markup in a header became an element instead of text. The ten
captions on https://angular.dev/guide/i18n/translation-files read
`messages.fr.xlf ()`, having turned `(<trans-unit>)` into an empty element.
These are the only two headers in the guides containing markup.
#65848 renamed `messages.fr.xlf.html` to `messages.fr.xlf` and, in the same
lines, reverted `region=` back to `visibleRegion=`, undoing #65530 from two
weeks earlier. `visibleRegion` is not read by the tokenizer, and `xlf` is not
in `REGION_MATCHERS`, so both halves of the snippet handling broke at once.
Since then https://angular.dev/guide/i18n/translation-files has shown the same
78 line file ten times where the prose describes single `<trans-unit>`
elements, and https://angular.dev/guide/i18n/manage-marked-text three times,
each carrying the `#docregion` scaffolding into the rendered code.
Mapping `xlf` to the html matcher also cleans up
https://angular.dev/guide/i18n/example, which renders the whole file on purpose
but leaked 27 marker lines into it.
`docs-code.spec.mts` already loads this fixture but only asserted the block
existed, so it stayed green throughout. It now also asserts no marker survives.
When generating type check blocks for `@for` loops, a non-null assertion is appended to the collection expression. If the collection expression is a compound expression (e.g. binary or logical operations like a && b), the lack of outer parentheses caused the ! to bind only to the rightmost operand (a && b!), leading to typecheck errors such as TS2532.
This wraps the expression via .wrapForTypeChecker() before appending !.
Add @modelcontextprotocol/server to devDependencies in @angular/core to extract its LICENSE file via a genrule in the third_party directory, and remove the previously checked-in package.json.
Replace `sed -i` in-place substitution with direct stdout redirection from the source file to destination outputs. `sed -i` behaves differently between GNU and BSD sed, which caused build failures on macOS.
`CodeBlock` picked `github-light` only when the theme was exactly `light`, but
`Theme` also has `auto`, the value for anyone who has not opened the theme
menu, and `theme()` is `null` during prerendering. Both fell to `github-dark`,
so the samples in the Signals, Control Flow and Deferrable Views tabs on
https://angular.dev rendered on a dark slab inside a light page.
Resolve the theme in `ThemeManager`, which already owns the `auto` translation.
`resolvedTheme` returns `light` when `theme()` is `null`, which happens only
during prerendering, so `window` is never read. The device scheme moves into a
signal so `auto` also reacts to OS scheme changes.
Prevent Angular DevTools from crashing when elements matching [ng-version] in the DOM do not belong to the host application (such as third-party custom elements or browser extensions). ng.getComponent returns null for these elements, which previously caused ng.getDirectiveMetadata to throw.
providing the snapshot is a bit incompatible with how things are meant to work here.
The snapshot is only accurate during the setup and would be out of date
on followup navigations
The substitution regex `\./(.+)/third_party/domino/bundled-domino` was used to rewrite the relative execroot path emitted by Rollup for the domino external import into `../third_party/domino/bundled-domino.mjs`.
However, `ng_package` runs `text_replace` across all generated package files, including `.map` files which are serialized on a single line. The `\./` pattern unintentionally matched the `./` inside `"../../"` in the `sources` array, and the greedy `.+` wildcard matched across the rest of `sources` and the `"sourcesContent": [` declaration up to the domino import within the first source file's content. This corrupted `init.mjs.map` and `_server-chunk.mjs.map` by destroying `sourcesContent` and populating `sources` with raw file contents.
This commit updates the substitution regex to use a negative lookbehind `(?<!\.)` to prevent matching `../` sequences, and restricts the path characters to valid filesystem path characters `[a-zA-Z0-9_./-]+` rather than `.+`.
Fixes#70625
The `query()` usage notes contain this code sample:
query(':self, .record:enter, .record:leave, @subTrigger', [...])
TypeScript's JSDoc parser treats whitespace followed by `@subTrigger` as the
start of a new tag, even inside a fenced code block. The extraction ends up with
a 145 character `@usageNotes` cut off mid-sample and a phantom `@subTrigger` tag
holding the remaining 2453 characters, which the renderer discards.
The result is that https://angular.dev/api/animations/query ends mid-line inside
that code block. The "Entering and Leaving Elements" and "Usage Example"
sections never render, and the two links pointing at the first of them, one in
the same JSDoc and one in guide/animations/complex-sequences, both dead-end.
Move the `@` token to the front of the selector so it follows a quote rather
than a space. Order within a comma separated selector list is irrelevant, and
the sample's own prose describes it as a set of tokens merged into one string.
The rendered page grows from 7109 to 21269 bytes and gains the
`entering-and-leaving-elements` and `usage-example` anchors. Rebuilding
//adev/src/assets:content with and without the change alters 1 of 1565 pages.
Previously, blocking router resources waited on hasValueOrResolved(), which checked whether the resource already had a value. This caused blocking resources with initial values (e.g. defaultValue), streamed early emissions, or existing values during route reloads to resolve prematurely while still in a loading state.
This change updates the blocking resolution check to be solely determined by !underlyingRes.isLoading().
Use whenStable instead of manual change detection so integration tests exercise scheduled rendering. Await asynchronous callbacks and remove redundant timer waits to keep assertions within the test lifecycle.
The tutorials README documents how tutorial content, routes and the shared
common project are laid out, and several of those statements no longer match
the pipeline.
Links: eight links were written as `/src/content/...`. GitHub rewrites a
root-relative markdown link to `/<owner>/<repo>/blob/<branch>/<path>`, so they
resolved to `angular/angular/blob/main/src/content/...` and returned 404. They
now use the repo-root path, matching the convention already used in
`adev/src/app/editor/README.md`.
Routes: the step examples claimed the number prefix is dropped, giving
`/tutorials/learn-angular/components-in-angular`. `routes.mts` emits the step
directory name verbatim and uses the number only for ordering, and the
production sitemap lists only the numbered form, so the two examples and the
bullet describing the step URL are corrected.
Common project: `adev/src/content/tutorials/common` was removed in #53511 and
the shared project now lives in `adev/shared-docs/pipeline/tutorials/common`.
The section also documents the per-tutorial `common` directory that
`tutorial_index.mts` applies on top of the shared one, and drops the reference
to `app.module.ts`, which the standalone common project does not have.
Update dependencies: the script covered five of the eight projects that carry
a package-lock.json. `signals/common`, `signal-forms/common` and the shared
common are added; all eight are already updated together by lock file
maintenance.
The README is excluded from `generate_guides`, so no rendered page changes.
Building `//adev/src/content/tutorials/...` with and without this change
produces byte-identical output across all 213 generated files.
`docs-alert` was the only marked extension in the pipeline without a `start`
hook, so marked never cut `inlineText` short at an alert and swallowed any
directive that was not at the start of the inline source. Writing the alert on
its own line without a blank line before it left the literal text in the body.
On https://angular.dev/guide/http/testing two alerts render as boxes and a
third shows as `IMPORTANT:` in the paragraph text. Also affects
https://angular.dev/errors/NG3003 and the first step of the first app tutorial.
The same renderer handles JSDoc, so one API page changes too:
https://angular.dev/api/upgrade/static/downgradeModule has three `NOTE:`
continuation lines inside bullets that now render as alerts.
`docs-video` and `docs-pill` already declare `start` the same way.
`visibleLines="26-131"` is not valid JSON, so `expandRangeStringValues` threw
and returned an empty list. The rendered attribute was empty, and the viewer
treats that as no range at all, so no ExampleViewer was created:
https://angular.dev/tutorials/first-app/08-ngFor showed all 128 lines of
`home.ts` with no collapse and no expand control, including the `@for` block
the reader has not written yet.
The file is 128 lines and the property this step adds ends at 127. The sibling
snippet on the same page already uses the `[start,end]` form.
The marker was written `# docregion`, but the hash matcher expects
`# #docregion`, one hash for the comment and one for the marker. It was not
recognised, so it was never stripped and rendered as the first line of the
Apache config on https://angular.dev/guide/i18n/deploy.
The sibling `nginx.conf`, shown on the same page, already has the correct form.
Replaces the remaining hand-rolled deferred promise implementations
with the native `Promise.withResolvers()` API and removes the now
unused helper and import.
Follow-up to #69739.
OnPush is now the default change detection strategy, so the explicit test configuration is no longer needed.
Updates outdated Bazel test targets to use the zoneless configuration.
`watchPreferredColorScheme` applied the OS scheme unconditionally, while
`setTheme`, `loadThemePreference` and the bootstrap script in index.html all
only follow it for `auto`. Choosing Light and then letting the OS switch to
dark repainted the site dark while the theme menu still reported Light, until
a reload restored it.
`isKnownRoute` exempted both families behind TODOs waiting on route extraction
for those packages. That extraction had already landed when the TODOs were
written in #66254: cdk pages since #60853 and aria pages since the cross-repo
workflow. `defined-routes.json` carries 70 `api/cdk` and 39 `api/aria` routes
today, and all 45 such link targets in the guides resolve, so the build stays
green without the exemptions.
The gap was not theoretical. `guide/aria/select.md` and
`guide/aria/multiselect.md` linked `api/cdk/overlay/CdkConnectedOverlay`, which
has never been a route, and it shipped as a 404 for six months. Link validation
landed four months into that and said nothing, because of this exemption. It
took a user filing #68914 and an outside contributor fixing it in #68915.
Pointing an existing `api/cdk` link at a symbol that does not exist passes the
build today and fails it with this change.
`relative-link.pipe.spec.ts` and `is-active-navigation-item.pipe.spec.ts`
arrived with this package's BUILD file in #57132, but no test target came with
them and `lib` excludes `**/*.spec.ts`, so nothing has ever compiled them.
`bazel query` reports both as not declared in the package.
They pass unmodified. `getRelativeUrl` has no other coverage in the repo, and
both pipes are used by the search dialog, the navigation list and the not
found page.
`getTagSinceVersion` matched `\d+(\.\d+)?` anywhere in the tag comment. That
works for `@developerPreview`, `@experimental` and `@stable`, whose comment is
only a version, but `@deprecated` also carries a message, so any number in the
prose won. https://angular.dev/api/common/getLocaleCurrencyCode reads
"deprecated since v4217", taken from "a map of locale to ISO 4217 currency
codes", and eighteen sibling pages take v18 from the "i18n" in "relying on the
`Intl` API for i18n".
Anchoring the match, and allowing the `since`/`from`/`as of` prefixes the
comments use, leaves all seventy-nine correctly versioned comments untouched.
`generate_manifest` carries its own copy of the regex, so the API list badges
had the same values.
Those `@angular/common` comments never stated a version, so they now say `18.0`
explicitly, the release `d34c033902` (#54483) first shipped in, matching the
`@deprecated 18.0` already on `FormatWidth` in the same file. Nine tags in that
file had no version at all and were showing no badge; they are from the same
commit and now say `18.0` too.
`app.component.spec.ts` has been in the tree since the initial DevTools commit
in 2020, but no revision of `shell-browser/src/app/BUILD.bazel` has ever listed
it, and that file has no globs. Two of the three specs in the directory are
wired up; this one has never run.
It could not run as written. The setup declared `AppComponent` and imported
`RouterModule.forRoot([])`, neither of which applies now that the component is
standalone and injects `MessageBus` and `DEEP_LINK_INSTANCE_ID`.
It now stubs the `chrome.devtools` surface that `ngOnInit` reads and covers
both lifecycle hooks: the listeners registered on init, the two performance
track emits, the three guards on the deep link listener, and the listeners
removed on destroy.
Follow-up to #70570, which fixed the same problem in ng-devtools-backend.
`WARNING:` is not an `AlertSeverityLevel`, so the paragraph rendered as plain
body text on https://angular.dev/guide/forms/signals/cross-field-logic while
the three other alerts on the page rendered normally. `CRITICAL` is the level
kitchen-sink.md defines for warning the reader off a footgun.
The five `@Component({/* ... */})` collapses are the formatter's. The file has
drifted out of prettier since it landed, and `ng-dev format changed --check`
checks whole changed files.
Queries could already read `ElementRef`, `TemplateRef` and
`ViewContainerRef` from a matched node via the `read` option, but not the
node injector. Getting it required a helper directive on the element.
`{read: Injector}` now returns the node injector of the matched element,
so a component can resolve tokens as they are seen from that element. This
is useful when wrapping third-party components that project templates and
expect directives inside those templates to inject the host component.
Works for `@ViewChild`/`@ContentChild` and the signal-based
`viewChild`/`contentChild`.
Fixes#47760
Add support for the new `@boundary` and `@error` control flow blocks in the Angular Language Service.
This includes:
- Updating outlining spans to handle boundary blocks correctly.
- Adding classification visitor methods for semantic tokens.
- Adding template target visitor methods for navigation and hover support.
- Updating the TextMate grammar to recognize `@boundary` and the `when` clause.
PR Close#70463
Add the runtime primitives `ɵɵboundaryCreate` and `ɵɵboundaryUpdate` to
the core instructions, which handle synchronous view destruction and
provide the `ON_ERROR` interceptor hooks.
Also include the initial compiler AST representations for the new syntax
including the Lexer tokenization and HTML Parser integration. This lays
the foundational structure for `@boundary` prior to code generation.
Co-authored-by: Matthieu Riegler <kyro38@gmail.com>
PR Close#70463
Implement error interception during refreshView and provide onError callback options in ViewContainerRef for programmatic rendering and encapsulation of boundary errors.
PR Close#70463
In `@for` blocks, tracking callable objects by reference (e.g. signal forms `FieldTree`, signals, or custom callable objects) is a valid pattern when tracking by object identity. Previously, `UninvokedTrackFunctionCheck` (NG8115) flagged any property read whose type has call signatures, regardless of whether the target was an actual track function expecting arguments or a method reference.
This commit updates `UninvokedTrackFunctionCheck` to only emit a diagnostic when the target expression has call signatures that declare parameters (functions/methods expecting arguments like `(item)` or `(index, item)`) or is a method declaration. Callable objects without parameters accessed as properties are now recognized as tracked values and not flagged as uninvoked track functions.
Fixes#70207
When a route is detached for `RouteReuseStrategy`, its component and child
`RouterOutlet`s stay alive and keep referencing the `ChildrenOutletContexts`
they were created with. `detachAndStoreRouteSubtree` used to call
`onOutletDeactivated()`, which swaps that object's Map for an empty one, so
after re-attaching, the inner outlet and the router read from two different
context trees and deeper child routes (e.g. an `edit` route under a reused
list) never rendered.
Now, on detach: take the child contexts map as-is (destruction still calls
`onOutletDeactivated()` to prune) and give the `OutletContext` a fresh
`ChildrenOutletContexts`. This keeps the detached component rendering from
its stored map once re-attached, and prevents whatever activates next in
the same parent outlet (e.g. a sibling tab) from mutating or wiping that
stored map.
Fixes#57285
`supported-apis.spec.ts` was added in #60585, in a commit that also edited the
`ts_test_library` three lines below the `srcs` it was left out of. No revision
of that BUILD file has ever listed it, so it has not run since March 2025. The
target goes from 12 specs to 14.
Wiring it up alone would not have worked. Every `*IsSupported` helper calls
`ngDebugClient()`, which throws when `window.ng` is undefined, and the old
`expect(supported).toBeTruthy()` set no `ng` at all. It now stubs `ng` and
checks the flag set and that each flag tracks its own debug API.
`ng-debug-api.spec.ts` adds an `[ng-version]` root and did not remove it, which
`getAppRoots()` then picks up in the other file under jasmine's random
ordering, so it now clears the DOM in its own `afterEach`.
`glob` matches the sibling `directive-forest/component-tree` target and keeps
the next spec in this directory from being dropped the same way.
The node-by-node walk used to resolve a token through an embedded view
injector forces the `Self` flag on every node injector lookup. Combined
with `SkipSelf`, that lookup can never match, so every node between the
injection point and the view boundary was skipped and the token was
resolved from the custom injector instead of the nearest parent node.
Clear `SkipSelf` once the starting node has been checked, so only that
node is skipped. This also makes the strip at the embedded view injector
redundant.
Fixes#70547
Five more defects in the example viewer, following #70508.
The DOM was queried before Angular rendered it. `setCodeLinesVisibility()`
walks the rendered lines but ran synchronously on tab change, so it measured
the outgoing tab and the incoming file showed in full. The selected tab was
also lost when the code block was hidden and reshown, because the recreated
tab group had no `[selectedIndex]` while `snippetCode` survived, leaving the
strip and the code disagreeing.
`expandable` was computed once at startup by counting hidden DOM nodes, so a
collapsed tab offered no way to expand it, and recomputing that count on tab
change would drop the control whenever the block was expanded, since nothing
is hidden then. Both paths now share one rule: a file is expandable when it
has a `visibleLinesRange` and either the block is expanded or the range
actually hides lines, so a range that covers its whole file still gets no
inert control.
Array indices were also mixed with 1-based line numbers: the gap check tested
`index - 1` for the preceding line, drawing a `...` separator inside
contiguous ranges, and the gutter tested `index` while the code tested
`index + 1`, shifting every line number by one.
Five new specs cover these, using the comma-separated range format the
pipeline emits; each fails with its fix reverted. The tab label also moves
from 0.8125rem to 0.875rem to match the code beside it.
Update the template indexer to discover and record pipes used in template expressions.
This associates template pipe identifiers with their target pipe class declarations, enabling indexers and language tooling to properly resolve and cross-reference pipes.
The static `ɵprov` field emitted on `@Injectable()` classes uses `ɵɵInjectableDeclaration<T>`.
When a subclass extends a generic `@Injectable()` base class with contravariant parameters
(such as callback/transformer methods depending on generic type parameters), TypeScript's
static side inheritance check (`typeof Sub extends typeof Super`) fails with `TS2417` because
`ɵɵInjectableDeclaration<Sub>` is not assignable to `ɵɵInjectableDeclaration<Super<any>>`.
Using `any` (`o.DYNAMIC_TYPE`) in `createInjectableType` avoids strict variance checks on
static inheritance for internal Ivy definitions and aligns with other Ivy declaration types.
When navigation items contain external URLs (e.g. https:// links for documentation or third-party resources), mapNavigationItemsToRoutes previously registered them as Angular Router route definitions. In recent versions of @angular/build, the static prerender worker asserts that discovered routes do not return empty content during SSG prerendering, causing production builds and CI adev-deploy to fail on these routes.
This change filters out external links in mapNavigationItemsToRoutes so only valid internal application paths are registered as Angular routes.
When an abstract base class has something like a lifecycle hook, Angular
compiles it as a directive with no selector. If a pipe extends that base
class, the pipe picks up the base class's directive definition through
normal class inheritance.
The NgModule dev-mode checks then got confused by that inherited
definition and reported the pipe as a broken directive:
- "Directive SomePipe has no selector, please add it!"
- or, if the base class was a default (standalone) abstract directive,
"SomePipe is marked as standalone and can't be declared..."
Both only happened in tests (TestBed), not when running the app.
Now the selector check and the standalone check both look at the pipe's
own definition and ignore a directive definition that only came from a
base class.
Fixes#36427
Make the SVG animation security context depend on the tag name instead of the namespace the element was created in. An animation element declared outside an `<svg>` is created in the HTML namespace, but still animates once it ends up inside an SVG subtree, so to and `attributeName` bindings were reaching the DOM unvalidated.
Fixes #70490
Removes experimental tags and stabilizes withAutoCleanupInjectors, AutoCleanupInjectorsFeature,
and RouteReuseStrategy cleanup methods, while re-exporting withExperimentalAutoCleanupInjectors as deprecated.
Preserve hasUAVisualTransition through Location and the Router navigation pipeline. This prevents withViewTransitions from starting an author transition after the browser has already performed one, including across redirects and when using experimental platform navigation.
In recent conventional-changelog version updates, the underlying
@conventional-changelog/git-client dropped support for the raw `grep` and
`extendedRegexp` options, causing all monorepo commits to be included in the
zone.js changelog. Additionally, without `tagPrefix: 'zone.js-'`, conventional-changelog
treated monorepo release tags as release boundaries, generating changelog sections
for every intermediate monorepo release.
This change:
- Configures `tagPrefix: 'zone.js-'` and filters commits by `scope === 'zone.js'`
via `writerOpts.transform` in the gulp changelog task.
- Fixes the PR creation link in the release script to use the cross-fork
compare URL format on GitHub.
- Fixes the release commit SHA lookup in `cutReleaseWorkflow`.
- Updates zone.js release documentation.
Two tutorial steps list a file in `openFiles` that does not exist, and a
missing entry is dropped without complaint. `first-app/05-inputs` asks for
`housinglocations.ts` when the file is `housinglocation.ts`; since
`hiddenFiles` is everything not in `openFiles`, the interface that step
teaches was marked hidden rather than opened.
`signals/5-component-communication-with-signals` asks for
`quantity-selector.ts`, which exists in neither `src` nor `answer`. Every
openFiles entry in the tutorials now resolves.
The code block lives inside `@if (showCode())`, so hiding it destroys the DOM and
showing it builds a fresh copy with no hidden lines. Nothing reapplied
`setCodeLinesVisibility()`, so a collapsed block came back showing the whole file.
Reapply it once the block is rendered again. It already branches on `expanded()`,
so a block that was expanded stays expanded.
`reactive-forms` and `form-validation` were excluded from `embeddable` in
full, with a TODO to fix them. The examples are fine; four partial snapshots
quoted by the guides are not, since each shares the final template of the
component it precedes, which references members it does not have yet.
Excluding those four files clears all 30 errors and puts 23 app files back in
the preview pool, so both guides' final components can be previewed for the
first time.
The block quotes a `.ts` file but declares `language="html"`, and shiki's
HTML grammar does not know Angular control flow, so the whole `@for` line
renders unstyled. Use `angular-ts`, matching the other blocks in the
tutorial.
The TextMate grammars for inline templates and inline styles ended the
string at the first occurrence of the delimiter, so an escaped delimiter
(e.g. \`) terminated the highlighting prematurely and the rest of the
template was no longer highlighted as HTML.
Escape sequences are now consumed before delegating to the HTML/CSS
grammars, so escaped delimiters no longer end the string. Also removes
the stray pipes from the string delimiter character class, which
unintentionally matched a literal '|'.
Fixes#65493
Signal forms can safely assume `{readOnlyHint: false, untrustedContentHint: false}` given their context. A form _must_ alter page DOM (or else how would a user interact with it) and is therefore definitionally side-effectful. We also assume returned content is trusted, given it is currently hard-coded or derived from application errors.
In the future, we might want to consider cases where application errors are explicitly untrusted or where application developers choose to customize the response text with something which might be untrusted. For now, that's out of scope and we'll worry about it when a compelling use case arises.
This updates `declareExperimentalWebMcpTool` to allow annotations to be provided to opt-in the tool explicitly into being read only or returning untrusted content.
Extended template diagnostic interpolated_signal_not_invoked previously
only checked symbols with kind === SymbolKind.Expression. When a signal
is aliased via @let (or a template variable), getSymbolOfNode returns
a LetDeclarationSymbol (or VariableSymbol), causing the diagnostic to
skip checking uninvoked usages of signal aliases in interpolations and
bindings.
This commit updates interpolated_signal_not_invoked to also check
LetDeclarationSymbol and VariableSymbol, using the usage site's AST
name span for reporting the diagnostic.
Closes#70476
Three ways a callout could be misparsed:
- A title quoted with `'` or a backtick was dropped, leaving an empty
heading. Two callouts lose their title on angular.dev today, on
guide/forms/template-driven-forms and guide/i18n/prepare. The first has
to use single quotes because its title contains `"pristine"`.
- A title containing `>` was dropped, because the attribute capture
stopped at the first `>` even inside a quoted value.
- The severity was matched anywhere in the tag, so a title such as
"Why this is important" silently rendered an important callout.
Scan attributes with quoting in mind, accept all three quote characters
as #69268 did for docs-code-block, and match the severity flags against
the tag with attribute values removed. The i18n callout also spelled the
attribute `header`, which the extension has never read.
`input.svg` and `output.svg` are leftovers that #70335 missed. Their
last reference was deleted by #54829 when the AIO guides were removed,
and nothing in the repo mentions them or their directory. Removing both
empties `adev/src/assets/images/guide/inputs-outputs`.
Replaces the temporary `promiseWithResolvers` polyfill with the
native `Promise.withResolvers()` API in test files and Updates the TypeScript configuration to include the `es2024.promise`.
RESOURCE_COMPLETED_BEFORE_PRODUCING_VALUE had no guide, no JSDoc on
RxResourceOptions.stream, and — since the code was positive rather
than negative — could never get an auto-linked docs page even if one
existed. Flip it to -991, add the NG0991 reference page, and document
the "stream must emit a value or an error before completing"
requirement on stream's JSDoc and in the RxJS interop guide.
Also documents and tests that an unguarded template read of an
errored resource's .value() propagates to the global ErrorHandler,
and recommends guarding with .hasValue() as defense in depth.
httpResource can throw the same error, but for a different reason:
its internal request Observable isn't something app code writes
directly, so an empty completion there is almost always an
HttpInterceptor swallowing the response (catchError(() => EMPTY))
rather than a stream authored in the resource() call itself. The page
covers both APIs with guidance matched to what's actually going on
for each.
There's a short window where Angular can't send errors to ErrorHandler
yet: while it's still building the root module or root component. It
needs that root instance to look up the ErrorHandler you provided, so
an error thrown before it exists just becomes a normal uncaught error
instead.
This mostly bites people using Angular elements, where a custom element
tag already sitting in the page gets upgraded (and its component built)
the moment you call customElements.define, which can happen very early.
Added a short section to the error handling guide explaining why this
happens and the usual ways around it: setTimeout, APP_BOOTSTRAP_LISTENER,
or moving element registration to ngDoBootstrap.
Fixes#29211
Associate each recommendation with its checkbox so assistive technology has a descriptive label and non-interactive text toggles the control. Keep embedded links independently operable.
Only touch tsconfig files for targets that use an Angular builder, including community ones like Nx, so non-Angular projects in mixed workspaces are left alone.
Fixes#69837
`copyEventListenerOptions` copied the caller's options with `{...options}`
before forwarding to the native `addEventListener`. Object spread only
copies own enumerable data properties, whereas the native call reads
each dictionary member via WebIDL — a plain `[[Get]]` per member, which
invokes accessors and ignores enumerability. The copy was therefore
lossy in a way the native call is not:
- `Object.defineProperty(opts, 'passive', { get })` (the shape used by
MDN's passive-listener feature test) — the getter was never invoked,
so libraries that use the feature test fall back to the legacy boolean
and register every listener as non-passive.
- `Object.defineProperty(opts, 'capture', { get: () => true })` — the
listener was silently registered on the bubbling phase.
- `Object.defineProperty(opts, 'once', { get: () => true })` — the
listener fired on every dispatch.
`signal` was already special-cased for `AbortController.prototype.signal`
after #54142; that patch generalises the workaround to every recognised
member.
The copy itself was the correct fix for #54142 (frozen/readonly options)
and is preserved. The fix reads each recognised member from the source
via `[[Get]]` when the spread did not, which recovers accessors and
non-enumerable properties without double-invoking any getter. The list
of recognised members is hoisted to module scope so it isn't allocated
on every `patchEventTarget` invocation.
The call site is reordered to `buildEventListenerOptions(
copyEventListenerOptions(...))` so the passive-events code path also
spreads a normalised data object rather than the caller's raw input.
Fixes#70431
Co-authored-by: Matthieu Riegler <kyro38@gmail.com>
The debounce() utility scheduled a setTimeout when using a numeric wait value, but the scheduled timer was never cleared when a new value arrived, the observable threw, or the injector was destroyed. This caused pending timers to fire after invalidation and leak beyond the owner's lifecycle.
Refactor timer cancellation into a dedicated helper and track the pending timer id so any stale timer is cleared on new values or teardown. Adds tests covering cancellation on new values, errors, and injector destruction.
Under the WHATWG URL standard, HTTP and HTTPS URLs lacking an authority
(e.g., `http:/path` or `http:path`) resolve as relative paths when resolved
against an origin of the same scheme. Previously, `relativeUrlsTransformerInterceptorFn`
treated any URL with a scheme as an absolute URL, bypassing base resolution in SSR
and allowing Node fetch to parse the path as a cross-origin host.
This commit updates SSR URL resolution and the HTTP interceptor to ensure
HTTP(S) URLs without an authority are resolved against the current origin,
preventing unexpected origin changes and aligning SSR with browser behavior.
Fixes#70447
Fixes that signal forms were overridding the `name` input of the CVA with an auto-generated one. This can break directives like `mat-radio` that assign their own `name`.
Fixes#69677.
The 34 pages using `<docs-decorative-header>` render their title
through `getPageTitle()` without passing the markdown file path, so
the "Edit this page" link is silently dropped. Every other page keeps
it. Compare https://v19.angular.dev/guide/components, which still has
the pencil, against https://angular.dev/guide/components, which does
not.
`filePath` was required until #63536 made it optional, so API
descriptions with no editable source could render a title without a
link. That removed the compile error forcing the decorative header to
supply it, and the argument was lost with nothing to catch it.
Pass the path again and cover both header variants with tests, since
the edit link had no coverage at all.
An unclosed `<docs-step>` or `<docs-card>` produces no error, just wrong
output, so the two cases fixed in the previous commit went unnoticed for
years.
Validate that the paired custom elements balance before parsing. A
mismatch now names the file and the counts instead of silently
swallowing a section.
`reactive-forms.md` and `app-shell.md` each open a `<docs-step>` that is
never closed. The tokenizers match up to the *next* closing tag instead
of failing, so the unclosed tag pulls in the content that follows and
leaves the block after it unparsed in the output.
On https://angular.dev/guide/forms/reactive-forms the "Creating nested
form groups" section shows raw source: "To create a nested group in
`profileForm`, add a nested `address` element", backticks and all. Its
heading is swallowed into the unclosed step instead of being an h3, and
the page renders 20 of its 21 steps. On
https://angular.dev/ecosystem/service-workers/app-shell the third step
is missing entirely.
`reactive-forms.md` has been broken since #52536, `app-shell.md` since
#55550.
The tutorial's example files shifted without the README line references
following, so several steps point at the wrong code. In two cases every
requested line is past the end of the file; on the inputs step this renders
as a code block with no lines visible at all.
Both indices point past the end of their block, so the intended lines are
never highlighted. The reactive forms block renders with no highlight at all,
and the first example in output-interop.md highlights nothing while its
sibling block is unaffected.
The fork guide link 404s since GitHub retired help.github.com, and the
rules_nodejs debugging docs moved to the bazel-contrib org. Also points the
git setup link straight at its current home rather than through a redirect.
jsperf.com now returns 410 for every benchmark. Two of them still exist on the
successor site and are repointed at jsperf.app; the other five are gone, so the
links are removed and the performance claims they backed are kept.
Foreign components imported via foreignImports and created by the
ɵɵforeignComponent instruction were previously rendered eagerly in the
creation phase (rf & 1) of the template. This restricted which
properties could be passed to foreign component props, as parent-bound
inputs (@Input(), input(), input.required()), properties initialized in
ngOnInit(), and pull-based view queries (viewChild()) were not yet
initialized at creation time.
This change defers foreign component rendering to run as a view effect
during the update pass:
- Update ɵɵforeignComponent in core to schedule component rendering via
createViewEffect (executed in runEffectsInView during refreshView),
executed with setActiveConsumer(null) to prevent reactive context
leakage and destroyed immediately upon first execution.
- Update ɵɵforeignComponent to strictly accept props as a factory function
(() => props) or null.
- Update the compiler template pipeline to wrap foreign component props
in an arrow function closure (() => ({ ... })).
- Hoist creation-time foreign content projection instructions
(ɵɵforeignContent, ɵɵforeignContentFn) into creation-phase variable
declarations before ɵɵforeignComponent so creation-time context is
captured safely.
When `@Component.deferredImports` maps blocks to arrays of dependencies, an explicitly
deferred dependency might be defined in multiple blocks (e.g. `block1: [CmpA], block2: [CmpA]`).
Previously, these were appended to the `explicitlyDeferredTypes` array without deduplication.
When generating the `setClassMetadataAsync` wrapper for development mode, the compiler
used this array to generate callback parameters for dynamic imports. This resulted in
duplicate parameter names in the callback signature `(CmpA, CmpA) => { ... }`, which
causes an `Uncaught SyntaxError` when V8 parses the module in strict mode.
This commit deduplicates `explicitlyDeferredTypes` in the `ngtsc` component handler, and
adds a secondary deduplication check in the `r3_class_metadata_compiler` generator to
ensure duplicate parameter names are never emitted.
Update the template indexer to discover and record bound directive inputs (property bindings, static text attributes) and outputs (event bindings).
This associates template binding identifiers with their target directive or component class declarations, enabling indexers and language tooling to properly resolve and cross-reference bound directive inputs and outputs.
The common tutorial scaffold's .gitignore opens with a link to
help.github.com/ignore-files/, which 404s. That directory is copied into every
tutorial and playground, so the dead link ships to anyone who opens one.
When compiling under standalone compilation, generated runtime static declarations (static ɵfac and static ɵprov) are emitted into preprocessed TypeScript files and visible to the compiler during semantic typechecking.
When a subclass extends a base class where the subclass is not structurally subtype-compatible with the superclass (such as differing generic type constraints, contravariant method parameters, or EventEmitter<this>), TypeScript's class static side heritage check (TS2417) fails because ɵɵFactoryDeclaration<T> and ɵɵInjectableDeclaration<T> structurally referenced the instance type T.
This change updates ɵɵFactoryDeclaration to return any instead of T, and sets factory return and value types in ɵɵInjectableDeclaration to any. This decouples static side inheritance from T, resolving TS2417 errors across subclassed components and injectables while preserving .d.ts metadata indexing and assignability to ɵɵdefineInjectable. This brings ɵfac and ɵprov into alignment with other Ivy declarations (ɵcmp, ɵdir, ɵpipe, ɵinj), which already treat their generic parameters as phantom metadata.
Foreign components are only supported in AOT mode. Using them in
JIT mode previously resulted in silent failures or confusing runtime
errors (such as unknown element errors or crashed template ingestion).
This commit adds explicit validation in JIT compilation:
- Throws an error during component compilation if `foreignImports` is
specified on `@Component`.
- Throws an error during standalone import verification if a foreign
component is mistakenly passed to `@Component.imports`.
For external templates (using `templateUrl`), primary diagnostics are
reported against the synthetic `ts.SourceFile` representing the HTML
template document. However, secondary related messages (such as those in
`foreign_component.ts` and `oob.ts`) were explicitly passing the
component's TypeScript file as `sourceFile`.
Because the character offsets (`start` and `end`) originate from the
HTML template AST, associating them with the TypeScript source file
caused IDEs and CLI diagnostics to map HTML offsets onto the `.ts` file,
resulting in corrupt or out-of-bounds source locations.
This commit resolves the issue by:
1. Making `sourceFile` optional in `makeTemplateDiagnostic` and related
checker interfaces (`TemplateTypeChecker`, `TemplateContext`).
2. Defaulting `relatedMessage.sourceFile` to the template's source file
(`sf` for external/indirect templates, or the component `.ts` file
for direct inline templates) when not explicitly provided.
3. Removing explicit `sourceFile: this.sourceMapping.node.getSourceFile()`
mappings from `foreign_component.ts` and DOM element checks in
`oob.ts`, allowing them to automatically resolve to the template file.
4. Adding unit test coverage for external templates encountering foreign
component conflicts with related messages.
This started from a real production crash trace:
TypeError: Cannot read properties of null (reading 'nextSibling')
Traced through a minified bundle back to siblingAfter() in
packages/core/src/hydration/node_lookup_utils.ts. It walks forward a
fixed number of DOM siblings during hydration, based on how many the
server rendered. Its only guard, validateSiblingNodeExists(), is
gated behind ngDevMode and stripped from production builds. If the
client DOM has fewer real siblings than the server-serialized data
expects — client/server render diverged, or something outside
Angular (a browser extension, an injected script) removed a node —
the loop ran fully unguarded in production: currentNode went null
partway through, and the next iteration's currentNode.nextSibling
threw a raw, uncoded TypeError instead of a coded, debuggable one.
Reproduced first, not just theorized: added a test that server-
renders a @for loop with 3 items, removes 2 of them before hydration
runs, and confirmed it actually throws that exact TypeError against
the original code before touching anything.
Fix: add a null check at the top of the loop, matching the pattern
already used for the existing NG0500/NG0502 hydration checks in
element.ts — the dev-mode check runs first as before (a no-op in
production), and the new check is a pure safety net that only fires
once that dev-mode check has already been compiled away. In dev mode
this is provably dead code, so nothing about dev behavior changes.
Coded as NG0501 (HYDRATION_MISSING_SIBLINGS, already existed).
While investigating, found and fixed two more call sites with the
exact same shape of bug — a dev-only check that leaves production
completely unguarded:
- navigateToNode() (same file): walks an explicit "firstChild /
nextSibling" navigation path recorded for nodes that can't rely on
simple sibling-walking (e.g. content right after an <ng-content>
slot). Its only guard was also ngDevMode-only. Added the same
production safety net, both mid-loop (the raw-crash case) and
post-loop (where it could previously return null silently despite
the function's return type promising a non-null RNode). New code:
NG0509 (HYDRATION_MISSING_NODE_ON_PATH).
- getParentRElement() (render3/node_manipulation.ts): typed its
tNode parameter as always non-null, but a real production trace
showed it can be null at runtime, crashing on tNode.parent with no
useful information. Widened the type to TNode | null and added a
guard that throws a coded error instead. New code: NG0510
(PARENT_NODE_NOT_FOUND).
Unlike the other two, this one isn't gated behind ngDevMode at all
in the original code — it reproduces unconditionally. Decoded the
actual production minified stack trace against this source
(matched every frame character-for-character) to find the real
cause instead of guessing: an @if/@switch branch's content is its
own embedded template with its own TView, built lazily the first
time that branch renders. If an error interrupts that first pass —
here, a hydration mismatch on the branch's second child, after the
first child's TNode was already created — TView.firstCreatePass
still gets flipped to false in render.ts's `catch` block before the
error propagates, permanently marking that TView as corrupted.
Unlike a component's TView (rebuilt from scratch next time via
getOrCreateComponentTView()'s incompleteFirstPass check), nothing
rebuilds an embedded view's TView. The next time that exact branch
is selected again, its instructions read straight from the
corrupted tView.data instead of creating a fresh TNode, and any
node past the interruption point is still null.
The real test added for this (full_app_hydration_spec.ts) exercises
that whole chain for real: a hydration mismatch on an @if branch's
second child, then re-entering the same branch. Confirmed it throws
the exact byte-for-byte production error message ("Cannot read
properties of null (reading 'parent')") without the fix, and the
coded NG0510 with it. This replaces an earlier synthetic unit test
that just cast a variable to null to bypass the type system rather
than reproducing the actual failure.
Note: a more thorough fix would rebuild the corrupted embedded TView
itself (mirroring getOrCreateComponentTView()), which does make the
crash disappear — but doing that surfaces a second, separate bug:
the aborted first attempt's partially-created LView/DOM is never
torn down, so the retried branch's content gets appended alongside
leftover orphaned nodes instead of replacing them, corrupting
content silently instead of crashing loudly. That's a materially
bigger fix (proper LView cleanup after a mid-creation exception) and
is intentionally out of scope here; this commit keeps the narrower,
safe fix (a clear coded error) rather than trading a loud crash for
silent data corruption.
Along the way, deduplicated describeDomNode(), which existed as two
near-identical private copies (one in this file's own NG0500 check,
one in platform-browser's dom_renderer.ts insertBefore check) and
moved it into core's hydration/error_handling.ts, shared via the
private ɵdescribeDomNode export.
That function was then removed from every production code path
entirely, per review feedback: computing a human-readable node
description is debug-oriented work that shouldn't ship unconditionally
in production bundles, regardless of how cheap it is — production
should get the bare coded error only, the same as everywhere else in
this file. All three fixes above follow that: ngDevMode && 'message'
in production, no computed description. Confirmed the removal
actually shrinks output, not just assumed it: regenerated all 8
affected bundling symbol goldens, and each one shows exactly one
symbol removed — "describeDomNode" — and nothing else.
When `onlyExplicitDeferDependencyImports` is enabled, dependencies that are used exclusively inside a `@defer` block but are provided eagerly (i.e., in the `imports` array but omitted from `deferredImports`) were incorrectly dropped from the generated `dependencies` array.
This occurred because the template binder intentionally omits directives used only inside defer blocks from the eager directives list, and the defer block resolver only tracks dependencies explicitly listed in `deferredImports`.
This commit fixes the issue by ensuring that any template dependency used within a defer block that is not deferred is explicitly preserved as an eager dependency, ensuring it is available at runtime.
An example-config.json marked a directory as a doc example and told aio's
example tooling which boilerplate to use and what to run against it. #56496
removed that tooling in June 2024. Eight of these fifteen files are empty and no
code has read any of them since; the name survives only in the zip and
StackBlitz exclude lists, which skip it rather than open it.
Implement component highlighting on change detection cycles along with embedding
the component-specific data to the directive/component tree explorer.
Closes#59057
PR Close#70254
Add the ability to automatically destroy created highlights by a provided TTL;
Add the option for outline style of the highlight overlays;
Add `prefer-inset` label position;
PR Close#70254
The `built-in-directives` and `structural-directives` example apps are no
longer referenced. Two guide rewrites moved their pages to inline code fences
and dropped the last references without removing the apps: #69134 for
structural directives, and #69822 for built-in directives, whose page is gone
entirely. Both apps sat inside the `embeddable` glob, so they were still
compiled as preview components on every build.
The example e2e suites are excluded from every consumer by design: previews
ignore `*.e2e-spec.ts` and `*.po.ts`, and the zip and StackBlitz pipelines
both list the suffix in `EXCLUDE_FILES`. They could not run in any case,
since every spec declares a `driver` and never assigns it before calling
`driver.get('')`, left over from an abandoned protractor to selenium
migration.
The eslint configuration in the same directory is also unused. Nothing in the
repository depends on eslint, and `tsconfig.eslint.json` extends a path that
no longer exists.
`guide/http/security` and `reference/concepts` both still exist as content but
neither is routed, so each falls through to the 404 shell.
`guide/http/security` was navigable from November 2023 until #54365 removed its
entry in February 2024. #55029 then copied its XSRF sections into the security
guide, which already covered XSSI, and #55060 repointed the remaining links, so
it now redirects to `/best-practices/security`.
`reference/concepts` was added by #54365 and removed by #58694 in November
2024, leaving it navigable for nine months without a redirect. The same commit
also removed `guide/ngmodules`, the route its only card linked to, so both now
redirect to `/guide/ngmodules/overview`, alongside the four `guide/ngmodules/*`
paths already redirected there.
Both pages are removed, along with the Bazel package that existed only to build
the concepts page, and the stale `llms-list.md` entry for the HttpClient page.
The security guide takes its place in that list, so `llms-full.txt` keeps its
XSSI coverage and picks up CSP, Trusted Types and sanitization with it.
The `msapplication` tile configuration serves no purpose now, so the
`browserconfig.xml` it points at, the five tile images and the two meta tags
in `index.html` are removed together.
`shared-docs/icons/twitter.svg` is also unused, left behind when the footer
moved to X and Bluesky. Its three siblings in that directory are all still
referenced.
The `pack-and-upload-artifact` action from `dev-infra` recently added conditional steps that require `triggering-label` and `angular-robot-key` to be passed. Without these inputs, the internal action steps were silently skipped, resulting in no artifact being uploaded. This caused the subsequent deploy workflow to fail when it couldn't find the `adev-preview` artifact.
Use a custom logger that prefixes the messages with `[Angular DevTools]`;
Add ability for dev-only log messages;
Add Angular-DevTools-specific `Error`;
Implement \`toggleWatchSignal(id)\` in \`signal_debug.ts\` to enable toggling reactive watch listeners on individual signal graph nodes.
- Create reactive \`Watch\` instances using \`createWatch\` to log signal value/state changes to the console when active.
- Use \`WeakRef\` mapping and \`FinalizationRegistry\` (\`watchCleanupRegistry\`) so active watches do not retain strong references to signal nodes or prevent garbage collection.
- Expose \`watched\` status on \`DebugSignalGraphNode\` and publish \`toggleWatchSignal\` onto \`window.ng\` in development mode.
- Add acceptance tests covering watch activation, signal mutation logging, manual disposal, GC cleanup, and safe invalid ID handling.
Store transfer state and serialization callbacks in null-prototype dictionaries, and only read values that belong to the store. This keeps special state keys from changing lookup behavior or exposing inherited cache entries.
Fixes#70265
When a derived class inherits from a base class, TypeScript enforces that static
properties on the derived class are assignable to those on the base class.
Because `ɵɵFactoryDeclaration<T, ...>` had a return type of `T`, static `ɵfac`
members across inheritance hierarchies could result in type incompatibilities
(for example, when dealing with generics or differing class shapes where the
derived factory return type is not compatible with the base class factory).
Updating the return type of `ɵɵFactoryDeclaration` from `T` to `any` avoids
these static type conflicts across inheriting classes.
The semantics of optional chaining changes when there are extra parenthesis. We need to make sure that we do not introduce some unnecessary ones.
fixes#70143
Two headings on the same page can generate the same anchor id, and every
link to it then resolves to whichever comes first. Nothing caught this
because the route manifest keeps anchors in a `Set`, so a repeat collapses
into one entry before any check runs, and the existing link validation only
asks whether an anchor exists, which a duplicate satisfies.
The generator now inspects each page's headings while they are still an
ordered list and fails with the offending pages and anchors. The scanning
and the duplicate check move to `heading.mts` so they are covered by tests
next to `getIdFromHeading`, which should keep this from coming back.
Eight guides have headings that generate the same anchor id twice or more.
Because an id resolves to the first element that claims it, the table of
contents lists two entries pointing at the same place, the second section
cannot be reached from the ToC at all, and the copy link button on the
later heading hands out a URL for the earlier one.
On `guide/forms/reactive-forms` the two "Display the component" steps sit
5176px apart and both ToC entries scroll to the first.
Give the later heading on each page an explicit id with the `{#custom-id}`
syntax the pipeline already supports. Only later duplicates are changed, so
every anchor that resolves today keeps pointing at the same heading.
Most of these came over from the angular.io docs and were orphaned as adev
rewrote or dropped the guides that used them. Two are newer: a devtools
screenshot that was added but never referenced, and the logo left behind
when the Firebase Studio launcher was removed. All of them are still
copied into the build and served from angular.dev.
No markdown, template, stylesheet, TypeScript file or build target
references any of them, by filename or through a path built at runtime,
and the images are globbed into the build rather than listed, so nothing
else needs updating. Removes 78 files, 5.15 MB in total, and empties
nineteen directories.
The link to the Wikipedia article on abstract syntax trees spelled the
underscores as asterisks, which 404s. The same link two paragraphs
earlier in this guide is correct.
Five redirects sent people to the home page instead of a guide, because
their target no longer exists: the four `guide/ngmodules/*` entries point
at `/guide/ngmodules`, which has no route, and
`guide/animations/transitions-and-triggers` had a plural in a target that
is registered as `transition-and-triggers`.
Two more resolved only after a second hop, redirecting to a path that is
itself a redirect.
The spec only checked that a `redirectTo` starts with a slash, which all
seven satisfied, so it now also checks that the target is a real page and
that it is not another redirect.
`stableTypeOrdering` was recently enabled in the internal builds. While there aren't any breakages in Angular, these changes enable it so we can catch potential issues earlier.
The regular expressions in `_scopeAnimationRule` expect an `animation` or `animation-name` property to be preceded by whitespace or a semicolon, and its value to end at a semicolon. Minified CSS breaks both assumptions. Inside an at-rule the property follows a `{`, and the last declaration of a block has no trailing semicolon, so the closing `}` lands inside the captured value. The keyframe name is then left unscoped while the `@keyframes` rule itself is renamed, so the animation does nothing in a production build.
Accept `{` as a leading boundary and stop the value at `}`. The prefix is written back unchanged, and a declaration value cannot contain an unescaped `}`.
Fixes#70316
The readonly rule previously used 'when' in configOrLogic to determine if a configuration object was provided. Under property renaming/minification, the string literal property lookup fails and causes the rule to fall back to being permanently readonly.
This change switches to the dot-access form (configOrLogic?.when), matching hidden() and ensuring property renaming works correctly.
Currently the standalone migration only copies symbols referenced from the NgModule metadata into the main file. Top-level `registerLocaleData` calls, and the default imports they depend on, are dropped silently when the module file is pruned, which breaks locale resolution at runtime with a "Missing locale data" error.
Copies top-level `registerLocaleData` calls from the file of the bootstrapped module into the main file, and adds support for carrying over default imports which were previously skipped silently by the reference resolution.
Fixes#50886
The quick fix and completion auto-import always generated the module specifier with double quotes, ignoring the user's quote preference and the style used in the file.
The generated import now respects the `quotePreference` from the TypeScript user preferences. When the preference is `auto` (or absent), the style is inferred from the first existing import in the file, mirroring TypeScript's own behavior. Files with no imports
keep the previous double-quote default.
Fixes#67108
The `embeddable` glob excludes nine example directories. Four of them,
`testing`, `ssr`, `resolution-modifiers` and `dependency-injection`, were
removed by #66753 and #61686 without updating this file, so those patterns
match nothing.
Three of the four sit under a TODO about examples that do not compile. Those
were deleted rather than fixed, which leaves `reactive-forms` and
`form-validation` as the only two the note still applies to.
`llms-list.md` feeds the generated `llms-full.txt`. Three of its DI
entries were left behind by a page rename: two named files that no longer
exist and one named a copy that had been superseded. The generator warns
on an unreadable file and carries on, so the build stayed green while
`llms-full.txt` quietly lost three DI guides and shipped a stale fourth.
`llms.txt` linked the same old URLs, which only reach their pages through
a redirect, plus `guide/hybrid-rendering`, which redirects to `/guide/ssr`
and duplicates the line above it.
Also removes `creating-injectable-service.md`, left behind by that rename
and reachable from nothing.
The new home page in #63662 stopped rendering `HomeAnimationComponent`
and its directory was left behind. The animation that runs today lives
in `features/home/animation`, which replaced it.
Nothing references the class, its `adev-home-animation` selector, or any
file in the directory, and no stylesheet or build target pulls it in.
`getApiNavigationItems` cast the manifest to `any` behind a TODO waiting
on #66252, which was closed without merging, so the three `any`s it
guarded were never going away on their own.
The real `ApiManifestPackage` cannot be imported here: navigation
entries are built as a standalone `ts_project` so the route generator
can consume them at build time, and it has no dependency on the app
tree. Declaring the handful of fields this file reads gets rid of the
`any`s without touching the build graph.
`DiagnosticsState` carried a TODO to move off `BehaviorSubject` once
zoneless was turned on. adev has been zoneless for a while now, with no
zone.js dependency and an empty `polyfills` array, so the condition is
met and the TODO can go.
Both consumers now derive from the signal instead of subscribing. The
Console tab badge becomes a `computed`, and so does the code editor's
error list: the diagnostics are produced by a `linter()` configured with
a 400ms delay rather than arriving as a stream, so the rxjs chain was
only adding a further second before they were displayed. The errors box
stays dismissable through a `linkedSignal`, which reverts as soon as the
diagnostics change.
The angular.dev origin was declared twice as a local `ANGULAR_DEV`
const and hardcoded inline in two more places. It now lives once in
`core/constants/links.ts`, next to `ANGULAR_LINKS`.
When Angular type-checks host bindings and template class bindings
(`[class.foo]="expr"`), the Type Check Block (TCB) emits the binding
as a standalone expression statement (e.g. `(expr);`). This verifies that
the expression itself is syntactically and semantically valid (e.g. properties
exist on the component instance), but does not constrain the
expression to `boolean` because Angular evaluates class bindings using standard
JavaScript truthiness.
In classic `ngtsc`, the runtime Ivy instructions (`ɵɵdefineComponent`) were
generated only during the JS emit phase, after TypeScript type-checking had
completed. Thus, `tsc` never validated the arguments passed to `ɵɵclassProp`.
Under standalone commpilation, Ivy definitions are generated
directly into the TypeScript AST and type-checked by `tsc`. This causes `tsc`
to check the emitted `ɵɵclassProp('foo', expr)` call against the instruction's
declared signature. Because `ɵɵclassProp` was strictly typed as
`boolean | undefined | null`, any valid truthy non-boolean expression
(e.g., `1`, `items.length`, or non-empty strings) produces a `TS2345` compiler error.
At runtime, `ɵɵclassProp` delegates to `checkStylingProperty`, which evaluates
the value via truthiness (`!!value`). Widening the parameter type to `any`
aligns the instruction's type signature with Angular's binding semantics and
prevents type-checking failures during in-place compilation.
When @Component.deferredImports is defined as an object mapping block
names to dependency arrays, deduplicate entries when aggregating block
imports into the component-level flattened scope. This prevents
duplicate directive/component collisions in DirectiveMatcher when
multiple @defer blocks share dependencies.
Enforces that components, directives, and pipes in @Component.deferredImports mapped to specific block keys are only used within their designated @defer blocks during template type checking, reporting out-of-band diagnostics when used eagerly or across mismatched blocks.
Allow `@Component.deferredImports` to be an object mapping block names to arrays of dependencies, and support `@defer (name blockName)` syntax to configure block-specific dependency lists in both standard and local compilation modes.
This enables more targeted dependency chunk generation during local compilation instead of over-eagerly loading all deferred imports together.
This forwards the `AbortSignal` natively supplied by Chrome 153.0.8009.0's update to `registerTool` executions down into the Angular context. This helps tools gracefully handle execution cancellations initiated by agents or users, preventing unnecessary background work and leaking resources.
We combine the injector/component destruction abort signals with the native WebMCP execution cancellation signals so the underlying tools correctly cancel long-running requests without producing invalid states.
Justification:
https://groups.google.com/a/chromium.org/g/chrome-ai-dev-preview/c/9291sjhIRz0/m/RFuyRrs5AAAJ
Introduces support for route-level resources via the `resources` property on route definitions, enabled with `withRouterResources()`.
Router resources provide a reactive, signal-based alternative to resolvers, allowing routes to declare resources tied to route parameters and route lifecycle:
- The `resources` function executes during navigation transitions within an injection context scoped to the route (`_localInjector`).
- It receives a `ResourceContext` containing signals for `params`, `queryParams`, `fragment`, and `data`, alongside the static `snapshot`.
- For newly created routes, `resources` runs once and attaches to `ActivatedRoute.resources`. For reused routes, parameter signals update reactively to trigger new data fetches while keeping resource references stable.
- Wrapped resources (`routerResource`) provide transactional stability: snapshots are frozen during active navigations to prevent UI jitter, unfreezing on `NavigationEnd`.
- On cancelled navigations or errors, rollback recovery retains the frozen snapshot until reverted signals settle, avoiding flashes of loading state. Manual reloads are rejected while frozen.
- Local injectors are automatically cleaned up if navigations are cancelled or rolled back.
Note that this commit only implements non-blocking resources (marked with `nonBlocking()`), deferring blocking resource resolution to future work to keep the initial changeset smaller and less complex.
PR Close#70211
Activated Route Injector feature was developed for resources. This moves the internal
naming (files and vars) to reflect this now that we have landed the core bits of plumbing
and are ready to implement router resources.
PR Close#70211
`routing.svg` and `directives.svg` each carry leftover text after a
`stroke-miterlimit` attribute, which has made them invalid XML since the
angular.dev import. They render only because decorative headers inline
them into the page, where the HTML parser turns the leftovers into empty
attributes, but the files fail anywhere they are parsed as XML,
including when served directly as `image/svg+xml`.
Neither drops anything from the drawing. The `directives.svg` leftover
repeats a path the file already declares.
Avoid the leak by unsubscribing from the profiler when the performance track is disabled.
Additionally, drop the duplicated performance track state flag.
`findConnectedBlocks` scans siblings after an `@if` to collect connected
`@else`/`@else if` blocks. Whitespace-only text nodes encountered during
the scan were eagerly added to `processedNodes`, marking them as "do not
emit", before confirming whether a connected block actually followed.
By the time `findConnectedBlocks` runs, `WhitespaceVisitor` has already
converted `&ngsp;` (and ` `) into a plain space character, making
them indistinguishable from insignificant whitespace via `.trim().length`.
When the next sibling was a second, unrelated `@if` instead of `@else`,
the scan stopped but the text node was already silently dropped.
Fix by deferring the `processedNodes` insertion into a pending buffer and
only committing those nodes once a connected block is confirmed to follow.
Fixes#55791
Defer blocks that ask to run during browser idle time with the same
options get grouped into one batch ("bucket") and processed together
the next time the browser is idle. While that batch runs, if one
callback (or the change-detection check right after it) asks to
schedule more idle work for that same batch, Angular used to register
a second idle-callback handle instead of reusing the one already in
flight.
That second handle became orphaned. By the time the batch finished
running, its queue was empty, so Angular threw away the bookkeeping
for it — including the only reference that could have cancelled the
handle. If the app was destroyed before the browser got around to
calling it, it fired anyway, against a scheduler that no longer
existed.
The root cause was a marker (idleId) that IdleScheduler uses to know
"I already have a browser callback pending for this batch, don't
request another one." That marker was being cleared to null right at
the start of processing a batch, before any of its callbacks had
actually run. So a callback that re-entrantly asked to schedule more
work mid-batch saw "nothing pending" and requested a redundant handle.
The fix: leave the marker set for the entire batch instead of clearing
it up front. Only clear it once every callback in the batch has run —
at that point, if there's leftover work, it's safe to request a fresh
handle for it.
Added a test for the basic re-entrant scenario, plus five more for
edge cases: a re-entrant add() into a different batch, and re-entrant
remove() of a sibling callback that hasn't run yet versus one that
already has.
Before: getSignalGraph only exposed debuggableFn on computed and template
nodes, preventing DevTools from linking to source code for other node types.
After: debuggableFn is also populated for linkedSignal, effect, and
afterRenderEffectPhase nodes.
Consolidates deindentation logic into formatCode so both docs-code and
docs-code-block are covered by a single fix. The original deindent
function incorrectly iterated over blank lines instead of non-blank
lines when computing minimum indentation, causing code blocks to render
with excessive leading whitespace. Also fixes region extraction which
had the same trim-before-deindent ordering issue.
When a componentless parent route has children rendered into a named outlet
(e.g. `outlet: 'inner'`), the `canDeactivate` guard received `null` as the
component argument instead of the actual component instance.
The bug was in `deactivateRouteAndItsChildren`: for componentless routes,
each child was passed the same `context` inherited from the parent lookup,
which was `null` when the parent component only registered a named outlet.
The children's actual outlet contexts were never consulted.
The fix adds a `parentContexts: ChildrenOutletContexts | null` parameter so
that for componentless routes, each child is looked up by its own outlet name
in `parentContexts` (e.g. `parentContexts.getContext('inner')`). For
component routes, `context.children` is passed as the new `parentContexts`
on recursion, ensuring correct context resolution across component boundaries.
This re-addresses #34614. A previous fix (#36302) was reverted because it
passed `parentContexts` unchanged through component boundaries; this fix
updates `parentContexts` to `context.children` when descending into a
component route, preventing the wrong contexts from propagating into deeper
componentless levels.
Fixes#34614
We keep getting PRs that target single usages of `hasOwnProperty` and we have ~100 of them. These changes aim to address the issue centrally by swapping out all the instances and adding a lint rule against introducing new ones.
To allow this we'll catch the errors during typechecking and discard it.
As context, when setting `isolatedDeclarations: true` this requires developers to explicitly type every property but the `private` ones. By allowing private properties to be used in templates we discard the actually for explicit typing for template only properties.
When a view has more than one effect scheduled to run, and one of them
destroys the view (e.g. by calling `componentRef.destroy()`), the
remaining effects in that same flush could still run afterward,
against a view that no longer exists. In some cases this crashed
outright with `TypeError: view[EFFECTS] is not iterable`.
Here's why: `runEffectsInView` walks a view's effects in a `for...of`
loop, wrapped in an outer `while` loop that re-checks for any effects
that became dirty as a side effect of ones that already ran. When an
effect destroys its view, `view[EFFECTS]` gets set to `null` as part
of tearing the view down.
First attempt checked for that inside the `for...of` loop, before each
effect runs. That covers a sibling effect later in the *same* pass,
but misses a second case: if the effect that destroys the view *also*
dirties another effect on that same view in the process (e.g. by
writing a signal the sibling depends on), the outer `while` loop sees
`HasChildViewsToRefresh` set and tries to restart — and immediately
crashes re-entering `for (const effect of view[EFFECTS])` on a
now-null value, before the in-loop check ever gets a chance to run.
Reproduced that exact crash with a test first: two effects on one
view, the second one writes a signal the first depends on and then
destroys the view in the same call — confirmed it throws before
touching the fix.
Fixed by checking right after `effect.run()` instead of before it,
covering both cases in one place: the remaining effects in the current
pass, and the loop trying to restart afterward. As soon as one effect
destroys the view, nothing else runs against it again.
This is intentionally narrow in scope. An earlier version of this fix
also tried to guarantee that `onCleanup()` callbacks still ran even
when registered after an effect destroyed its own view. That's been
dropped — destroying your own view and then continuing to register
more work for it isn't something the framework should have to paper
over. If you need to do both, register `onCleanup` first, then
destroy.
Previously, when you passed an object typed like
Record<'a' | 'b', number> into the `keyvalue` pipe, TypeScript would
"forget" that the keys could only ever be 'a' or 'b', and just tell
you the key was a plain `string` instead. So code like this used to
fail to compile, even though it's correct:
```ts
const input: Record<'a' | 'b', number> = {a: 1, b: 2};
const result = pipe.transform(input);
const key: 'a' | 'b' = result[0].key; // error: string is not 'a' | 'b'
```
This happened because the pipe has multiple overloaded versions of
transform(), and TypeScript checks them top to bottom, using the
first one that matches. The "number keys" overload was listed first,
and it happened to also match string-keyed objects by accident, so
it "won" before the correct "string keys" overload ever got a
chance to run.
The fix just reorders those two overloads so the string-keys one is
checked first. Nothing about runtime behavior changes — objects with
actual numeric keys (e.g. Record<1 | 2, string>) still correctly
report their keys as plain `string`, matching what Object.keys()
really returns at runtime.
Prevent arbitrary MetaDefinition properties from writing on* handlers directly to meta elements. Browser events can execute these handlers, including on meta elements rendered in the document body.
In a composite/solution-style workspace (e.g. an Nx monorepo, where an
app's tsconfig.json only contains project references), TypeScript can
never resolve a config file for an HTML file, since HTML files are not
listed in any referenced project. angular/vscode-ng-language-service#2165
worked around this in onDidOpenTextDocument by briefly opening the
sibling TS file so the right project loads when a template is opened
first.
However, getDefaultProjectForScriptInfo - the recovery path used by
getLSAndScriptInfo and onDidChangeTextDocument when a script info has no
configured project - did not receive the same workaround. When an open
template loses its project association (e.g. its component file is
closed and the project graph updates), every subsequent request on the
template fails with "No config file" and returns null indefinitely,
until the user manually reopens the component file.
Apply the same sibling-TS best effort in getDefaultProjectForScriptInfo,
and additionally attach the template's script info to the configured
project of its component when the config lookup still comes back empty
(openClientFile does not repeat the config lookup for already-open
files).
Also skip the sibling lookup when the .ts file does not exist, so
non-component HTML files (e.g. src/index.html) do not trigger an
open/close and config search that cannot succeed.
Fixes#69768
The "Value transformation" section taught readers to hand-roll
transformation with `linkedSignal()` and a manual parse method, even
though `@angular/forms/signals` ships `transformedValue()` for exactly
this case. Readers ended up with a weaker version of a feature the
framework already provides — notably, no parse error reporting.
Rewrite the section around `transformedValue()` and document the parts
the manual pattern could not cover: returning `{error}` from `parse` to
surface parse errors on the field's `errors()`, and `reset()` clearing
them. This also makes good on the cross-reference from the validation
guide, which pointed here for parse error details the section never
covered.
Fixes#70206
Co-authored-by: Matthieu Riegler <kyro38@gmail.com>
Replace unsafe `any` type annotations with `unknown` across the localize
utility layer to improve type safety and catch potential type errors at
compile time rather than at runtime.
Changes in `messages.ts`:
- `ParsedMessage.substitutions`: `Record<string, any>` → `Record<string, unknown>`
- `parseMessage` parameter `expressions`: `readonly any[]` → `readonly unknown[]`
- Local `substitutions` variable: `{[key: string]: any}` → `Record<string, unknown>`
Changes in `translations.ts`:
- `isMissingTranslationError` parameter: `any` → `unknown`, with proper
narrowing (`typeof e === 'object' && e !== null`) before property access
- `MissingTranslationError.type` visibility: `private` → `readonly` to allow
access through the narrowed `unknown` type in the type guard
- `translate` parameter and return type: `readonly any[]` → `readonly unknown[]`
- `makeTemplateObject` cast: `cooked as any` → `cooked as unknown as TemplateStringsArray`
Fix in `mock_message.ts` (test helper):
- `substitutions: []` → `substitutions: {}` — the array literal was only
assignable because the field was typed as `any`; the correct empty value
for a `Record<string, unknown>` is an object literal
NgModel injects ControlContainer with @Host(), which stops the injector at
the component host element boundary. When NgForm lives in a parent component
and ngModel lives in a child component, the injection returns null silently
and the control acts standalone — never registering with the form.
To surface this invisible failure, emit a dev-mode warning (NG01354) when
ngModel's @Host() injection finds nothing but the element Injector can still
reach a ControlContainer further up the hierarchy. The warning identifies the
cross-boundary issue and points developers to the viewProviders fix or the
standalone option.
Adds the NG01354 reference page explaining why the warning fires and providing
two remediation paths: bridging ControlContainer via viewProviders, or opting
out with [ngModelOptions]="{standalone: true}".
Fixes#47580
StateValue and AnimationTransitionNamespace.trigger detect the {value,
params} object form of a trigger binding by calling hasOwnProperty on the
bound value. When that value is an object from untrusted data (for example
a parsed JSON payload) carrying an own hasOwnProperty key, the shadowed
property is called as a method and throws, breaking the animation flush.
Use Object.hasOwn for the check so a shadowing key no longer matters.
Cancel the unread response body before reporting NG02825 when its declared Content-Length exceeds the configured buffer limit. Without cancellation, SSR can finish while the underlying connection remains open.
Add regression coverage for the declared-length rejection path.
The routing header draws a serpentine route with map pins on it but
nobody travelling it. Angie now stands on the top segment, the same way
she stands on the road in the roadmap header.
She needs more headroom than the canvas had, so the viewBox gains 12
units at the top and the illustration carries its own max-height, which
keeps the rest of the drawing at the size it rendered before.
The roadmap header draws a road between a start pin and a destination
pin, but nothing travels it. Angie already appears on the 404 page, the
embedded editor states and the error snack bar, so put her on the road
too.
Decorative headers are inlined into the page so the road can use CSS
variables for dark mode, which means the pose has to be embedded as
paths, with its classes flattened and its ids prefixed to keep them out
of document scope.
Note this asset is also the essentials Next Steps header.
Collapse the section since it takes a significant portion of the vertical space,
whereas most of the times users are exploring the properties leading them to scroll.
Addiotionally, introduce some other minor UI improvements.
Show the missing nav arrows when you select a deeply nested component where
the breadcrumbs path is longer than the container. The nav arrows used to
appear only when the split is resized. Add some other minor improvements.
Before: The View Source button tooltip in signal details static ('View source') even when disabled.
After: The tooltip explains 'Source location is not available for this node' when disabled.
Before: getSignalGraph only exposed debuggableFn on computed and template
nodes, preventing DevTools from linking to source code for other node types.
After: debuggableFn is also populated for linkedSignal, effect, and
afterRenderEffectPhase nodes.
locateOrCreateElementNodeImpl looks up the DOM node for an element
during hydration and immediately checks its nodeType. If the
client-rendered DOM has fewer nodes than the server-rendered HTML,
the lookup returns null, and in production that null flows straight
into the nodeType check and crashes with a raw, uncoded
"Cannot read properties of null (reading 'nodeType')" TypeError.
The dev-mode check that would normally catch this (validateMatchingNode)
already handles a missing node, but it's compiled out of production
builds, so the crash only shows up outside of dev mode.
Add a null check ahead of the nodeType check that throws a coded
RuntimeError using the existing HYDRATION_MISSING_NODE (NG0502) code,
with a descriptive message in dev mode and a cheap fallback in
production. Also add a regression test that removes a server-rendered
element before hydration runs and asserts a coded RuntimeError is
thrown instead of a raw TypeError.
Angular's internal LView/TNode bookkeeping can get out of sync with the
real DOM: manual DOM manipulation, a browser extension, or an edge case in
Angular's own view-insertion/reordering code can all leave Angular believing
a node is still attached at a given position when it isn't. The next time
Angular's renderer calls `insertBefore` relative to that stale reference
node, the native DOM API throws an opaque `NotFoundError` with no indication
of which node or component was involved, making these errors effectively
undebuggable in production:
NotFoundError: Failed to execute 'insertBefore' on 'Node': The node
before which the new node is to be inserted is not a child of this node.
at Node.insertBefore (native)
at DefaultDomRenderer2.insertBefore (packages/platform-browser/src/dom/dom_renderer.ts)
at nativeInsertBefore (packages/core/src/render3/dom_node_manipulation.ts)
at nativeAppendOrInsertBefore (packages/core/src/render3/dom_node_manipulation.ts)
... (called while Angular inserts or moves a view during change detection)
Check the reference node's actual parent against the expected parent before
calling the native `insertBefore`, and throw a descriptive `RuntimeError`
(NG05106) instead, following the same pattern already used for hydration
node mismatches.
Implements persistent breakpoints for Angular signal consumers (computeds and effects) in Angular DevTools using Chrome DevTools Protocol (CDP).
- Adds debugger permission to Chrome and Firefox extension manifests.
- Implements CDP breakpoint orchestration in background script.
- Updates SignalDetailsComponent and SignalGraphPaneComponent with label icon toggle and persistent state sync across reloads.
Exposes the internal fn reference of effect reactive nodes on DebugSignalGraphNode as debuggableFn. This allows Angular DevTools to inspect and set breakpoints on effect callbacks.
VS Code parses notification text for links and permits `command:` URIs. Interpolating the workspace-controlled TSDK path can therefore let a malicious path add a command link to the approval prompt.
Remove the path from the notification instead of attempting to sanitize or escape it. This keeps the prompt static and matches VS Code's TypeScript extension workspace-version approval flow.
Fixes#70176
When reflection metadata is emitted via setClassMetadata, passing readonly arrays or const tuples for the decorators parameter causes TypeScript type checking errors because setClassMetadata previously expected decorators to be a mutable any[] or null.
This change updates the setClassMetadata type signature to accept decorators as readonly any[] or null and casts the parameter internally when mutating the class metadata property.
When components pass arrays or readonly tuples to decorator metadata fields (such as ), typechecking generated decorator reflection metadata causes a TS2322 type mismatch error because previously only accepted mutable .
This change updates to accept , allowing arrays and readonly tuples to be assigned without TypeScript compilation errors.
When hydration locates the DOM node for an ɵɵelementStart/ɵɵdomElementStart
instruction, locateOrCreateElementNodeImpl assumed the located node was
always an Element and called hasSkipHydrationAttrOnRElement(native), which
does native.hasAttribute(...). The check that would normally catch this
class of mismatch, validateMatchingNode, is gated behind `ngDevMode &&` and
is compiled out of production builds. So when a real SSR/hydration
structural mismatch located a Text or Comment node instead of the expected
Element, production builds hit .hasAttribute on a node type that doesn't
have it and crashed with a raw, uncoded TypeError instead of a coded
hydration-mismatch RuntimeError.
Add a cheap, always-on nodeType check ahead of that call. On mismatch it
throws RuntimeError(HYDRATION_NODE_MISMATCH, ngDevMode && '...'), the same
pattern used elsewhere in the codebase, so the descriptive message is only
built in dev mode and production keeps throwing just the bare NG0500 code
without pulling validateMatchingNode's DOM-printing machinery into the
production bundle (verified via the bundling/hydration golden-symbols test,
which is unchanged).
Previously, the release script hardcoded the upstream repository URL as an unauthenticated HTTPS URL (https://github.com/angular/angular.git). Although the script verified that a GITHUB_TOKEN environment variable was present, it only used that token for REST API calls (such as creating the GitHub release) and did not provide it to Git commands. As a result, users who authenticate to GitHub via SSH (and do not have an HTTPS Git credential helper configured) were prompted interactively for GitHub login credentials when pushing the release tag.
This change dynamically resolves the upstream remote name from the user's configured remotes by checking for any remote pointing to angular/angular. When pushing over SSH, Git uses the user's existing SSH credentials. When pushing over HTTPS (or falling back), the script injects GITHUB_TOKEN into the push URL to prevent interactive authentication prompts.
When resolving template symbols for SafePropertyRead in TCBs emitted with optional chaining (strictSafeNavigationTypes: true), SymbolBuilder falls back to finding a TS node matching the AST expression's nameSpan. It then traverses up through parent nodes to find the enclosing expression.
Previously, the traversal loop checked isAccessExpression(node.parent) without verifying whether node was the accessed member name or the expression receiver. When multiple optional navigation expressions are chained (e.g. route?.data?.['icon']), the parent of ((route)?.data) is an access expression where ((route)?.data) is the receiver. Because isAccessExpression was true, the loop continued ascending into the outer access expression, causing symbol resolution for data to erroneously return the symbol and TCB location of icon.
This commit refines the parent traversal condition so that it only climbs into a parent PropertyAccessExpression if node is the accessed name (node.parent.name === node), preventing escape into outer receiver expressions.
animate.leave waits for the longest-running animation before removing an element. When multiple animations use the same keyframe name, their animationend events have the same animationName, so a shorter animation can be mistaken for the longest one and remove the element too early.
Track the longest animation duration returned by getAnimations() and compare it with the duration from event.animation when available. Keep the existing name/property checks as a fallback for older browsers and computed-style detection.
Allow a 1ms tolerance for rounding differences between CSSOM and Web Animations and add a regression test using values equivalent to fractional calc() durations.
This commit adds an implementation of a router resource (not currently exposed for public use) which
defines the behavior of a resource dependent on the Router navigation lifecycle.
This updates the docs to be explicit about `animate.leave` and nested element removal order. It clearly specifies that `animate.leave` will only fire nested animations within the same component template.
closes: #70131
Preserve createUrlTree command semantics, including custom serializer inputs, while keeping the single-leading-slash guarantee at the default serialization boundary.
Expand coverage for command forms, public UrlTree values, secondary outlets, and preserved query parameters and fragments.
Fixes#69700
This allows developers to throw a `RedirectCommand` directly from guards and resolvers to trigger a redirect.
The primary benefit is that we no longer need to pollute the return type of functions that redirect. For example, a deeply nested helper function or a resolver can now simply throw a `RedirectCommand` to short-circuit and redirect, instead of having to return the `UrlTree` or `RedirectCommand` all the way up the call stack.
This aligns with prior art in other modern framework routers (such as Next.js, Remix, and SvelteKit), which commonly use thrown exceptions or special redirect responses to abort execution and trigger immediate redirection.
Scrolling to the top or bottom of a dropdown let the scroll event chain into
the page behind it, causing the whole app to scroll. #70137 fixed this for the
version picker, but the same containment was missing on every other menu.
The API reference filter, the tutorial step list and the update guide's version
dropdowns already had a scroll container, so they only needed
overscroll-behavior.
The social and theme mini-menus had neither a height cap nor overflow, so
overscroll-behavior alone would have been inert on them. They now share the
version picker's max-height/overflow-y/overscroll-behavior, hoisted onto
.adev-mini-menu, which also keeps their items reachable when the viewport is
too short to fit the whole menu.
- Add info tooltips that describe the visualizations and other
parts of the UI
- Improve the frame selector by adding axes labels and a
horizontal line for frames exceeding 60 fps
- Use a precise frame rate calculations instead of approximations
- Introduce improvements to the details panel
- Improve the bar chart visualization
- And more
`getTIcu` and `setTIcu` performed `hasOwnProperty` checks with string
literals (`'currentCaseLViewIndex'` and `'tView'`). Under Closure Compiler
property renaming optimizations, these properties are minified but the
string literals are not, causing property lookups and type discriminations
to fail.
This commit defines closure-safe property constants using
`getClosureSafeProperty` so the property names are renamed consistently.
Scrolling to the top or bottom of the version picker's dropdown list
let the scroll event chain into the page behind it, causing the whole
app to scroll. The search dialog and search history dropdowns already
guard against this with overscroll-behavior: contain; apply the same
fix to the version picker.
Treat MathML-namespaced script elements as scripts during template preprocessing. This prevents scripts nested in MathML HTML integration points from surviving template compilation.
This fixes a regression introduced by #69309 where the format of `serializedId` from a coma separated string, to a JSON representation of an array.
fixes#70104
Use the host element's namespace-aware local name when checking for script
elements. A prefixed SVG script can expose a qualified tag name such as
"x:script" while its local name remains "script".
Recompute the checked state when a reused radio input receives a new value while the form model remains unchanged.
Handle this case for both reactive forms and signal forms, and add regression tests covering reused radio elements.
Updates the nested selector encapsulation logic so that it skips encapsulating child selectors if the parent contains `::ng-deep`. The reasoning is that `:host ::ng-deep { .foo {} }` should behave as `:host ::ng-deep .foo {}`.
This is a second attempt at scoping nested CSS rules after not being able to land #50693. I took some extra precautions to try and avoid the crash that was happening last time.
`DeferredSymbolTracker.lookupIdentifiersInSourceFile` prunes `ts.isTypeNode`
subtrees so that references appearing exclusively inside type annotations
do not keep static import declarations in the emitted JavaScript.
However, `ts.isTypeNode` returns `true` for `ts.ExpressionWithTypeArguments`,
which TypeScript uses to represent both `extends` and `implements` heritage
clauses. An `extends` clause on a class declaration or class expression is a
value position that survives in the emitted JavaScript output.
Because `isTypeNode` returned `true`, references to base classes imported
alongside deferred dependencies were ignored. As a result, the compiler
erroneously marked the static import statement as deferrable and deleted it
from the emitted JavaScript, leaving the `extends <Base>` clause referencing
an undeclared identifier and causing a runtime `ReferenceError`.
This commit ensures that `ExpressionWithTypeArguments` under a class `extends`
clause is not treated as an erasable type node, preserving the static import
whenever a base class is referenced.
Optional chaining was generating expressions with included an extra pair of parenthesis which changed the semantics of the expression and threw an unexpected error from the optional chain non nullable extended diagnostic.
fixes#70085
Remove the supported 'async-await': false option in esbuild-base.config.mts as Angular DevTools is zoneless and does not require downleveling async/await syntax.
Configure esbuild with supported 'async-await': false in esbuild-base.config.mts so esbuild downlevels async functions and async generators natively.
This allows removing the custom Babel plugin @babel/plugin-transform-async-generator-functions and downlevelAsyncGeneratorsIfPresent option.
Prevent AbortController.abort() from executing during Observable teardown when a FetchBackend HTTP request has already completed successfully or errored.
Previously, FetchBackend unconditionally called abort() upon stream termination. When requests completed normally, calling abort() after delivery caused Chromium-based browsers to mark the resolved request as net::ERR_ABORTED in DevTools due to a race condition, leading to missing response body payloads ("Failed to load response data"). By tracking whether the request has already settled—similar to XhrBackend checking for xhr.readyState !== xhr.DONE—we ensure abort() is only called for unsettled, in-flight requests upon unsubscription.
Fixes#70071
Previously, `parseCookieValue` did not strip enclosing double quotes (`DQUOTE`) from quoted cookie values as specified in RFC 6265 Section 4.1.1. In addition, malformed percent-encoding in cookie values caused an unhandled `URIError` when calling `decodeURIComponent`.
Extract the charset parameter from the Content-Type response header in FetchBackend and pass it to TextDecoder when decoding text and json responses. When no valid charset is provided or supported, gracefully fall back to default utf-8 decoding.
Fixes#70061
Fixes#69785
Add an Angular SSR integration test in platform-server verifying that null input values do not render string attributes like value="null" during server-side rendering, while normal non-empty string values like value="hello" are properly preserved.
Inline code inside a link renders as a chip with its own background. Text
decoration propagates from the anchor and cannot be cancelled by a descendant,
so with the chip laid out as a regular inline box the link underline is drawn
straight across it.
Lay the chip out as an atomic inline box, which the propagated decoration is not
drawn across. This applies to the link rule only; code outside of links keeps
`display: inline` for wrapping in multiline paragraphs, and the link rule
already sets `white-space: nowrap`.
A timer wrapper such as a browser extension, policy, or automation harness can block a call and return null instead of a handle. patchTimer then read `.refresh` on that null value and threw synchronously.
Treat a nullish handle as non-refreshable so the patched timer keeps working.
Fixes#70044
Replace synchronous detectChanges calls with zoneless-compatible scheduling and stability waits. Preserve NgComponentOutlet coverage for components declared by NgModules.
Two link issues on the roadmap page:
- "Introduce deferred loading" linked next.angular.dev, the preview build of
this same site, for a page that exists here at guide/templates/defer. Use the
relative path so readers stay on the stable docs.
- "Local template variables" linked "`@let` docs", putting a code chip and a
plain word in one anchor. The anchor's underline crosses the chip and the two
halves render in different colors. Link only the symbol, as the rest of adev
does.
Also on the same page:
- Drop a stray space before a period in the Components card.
- Give the older completion labels the same "Completed in <quarter>" wording the
newer cards already use.
After incremental hydration became tree-shakable, application bootstrap could finish before a routed component activated the runtime. The one-time trigger scan was then skipped, leaving routed and nested hydration triggers uninitialized.
Coordinate application bootstrap with runtime activation and initialize once both have occurred.
Fixes#69908
The `allowSignalWrites` option of `effect()` became a no-op and was
deprecated in v19, but the update guide never mentioned it. Applications
upgrading from v18 or earlier still carry the option at their `effect()`
call sites, where it now only produces a dev-mode warning.
Add an entry to the v19 recommendations instructing those applications to
remove it, mirroring the wording of the deprecation notice on
`CreateEffectOptions`.
Represent withRequestsMadeViaParent() with an internal delegating backend so the interceptor handler can distinguish delegated clients from independent child configurations.
The "Customizing model inputs" section linked "standard input" back to
the inputs guide itself, a dead-end self-link. Point the sentence at the
relevant in-page sections instead (#required-inputs and #input-aliases).
Also merge the two stacked intro TIP callouts into a single tip.
Report unsupported style property binding values in development mode while preserving existing binding behavior. Unwrap trusted style values before appending unit suffixes and link NG0318 warnings to the corresponding error guide.
- r3_factory: use `in` operator instead of `as any` property probing
in isDelegatedFactoryMetadata / isExpressionFactoryMetadata type guards
- r3_deferred_triggers: narrow assignment to
`DeferredBlockTriggers[typeof name]` instead of `as any`
- defer_resolve_targets / reify: drop unnecessary `as any` on
`op.trigger` — `DeferTriggerBase.kind` is present on all union members
- pipe_creation: replace double `as any` with `as {target?: ir.XrefId}`,
and reuse the already-narrowed `slotHandle` variable for the call
- extractor_merger: replace legacy `<any>console` guards with a proper
`typeof console !== 'undefined'` check
Several external links pointed at locations that have moved or gone away:
- ng-packagr, webpack, and node-glob renamed their default branch from
master to main, so the deep links into those repos redirect.
- The npm package pages moved from npmjs.org to www.npmjs.com.
- The Angular Material and CDK docs moved from material.angular.io to
material.angular.dev.
- The semver reference pointed at the defunct semver.io service; point it
at the SemVer spec at semver.org instead.
- The MDN animationiteration event link had a typo (animationitration).
- compiler-cli's metadata/schema.ts was removed after v12; pin the link to
the 12.2.x tag where the file still exists.
- The angularindepth.com article is gone (its domain is parked); link to its
live home on the Angular In Depth Medium publication.
- Google moved the Lighthouse user-timing docs to developer.chrome.com.
Point the links at their current, working locations.
`isEditingDirectiveState` used `!this.selectedNode` (a bare signal
reference, always truthy) instead of `!this.selectedNode()`, so the
guard was dead and ArrowDown with no selection threw a TypeError.
Adds `# main` version comments after SHA references for `angular/dev-infra` GitHub Actions across workflows so that Renovate tracks the main branch and proposes updates when digests change.
Defer the timing hook subscription until the timing API is explicitly enabled. Keep production module loading and development initialization covered by isolated browser test targets.
Several adev example and tutorial files read a signal input as a bare
reference (this.foo) instead of invoking it (this.foo()). Because an
InputSignal is a function object, the bare reference is always truthy
and never yields the underlying value, so the surrounding guard or
binding silently did the wrong thing:
- animations open-close(.1/.3): the `!this.logging` guard in
onAnimationEvent was always false, so the early return never fired.
- form-validation forbidden-name.directive: the `this.forbiddenName`
ternary condition was always truthy, so validation ran even when no
forbidden name was configured.
- first-app steps 12 and 14 housing-location: `housingLocation.photo`
read `.photo` off the signal function (undefined), leaving the
listing image src empty.
Invoke the signals so the examples reflect correct signal-input usage.
Rely on zoneless test scheduling instead of manually triggering change detection. Keep Signals Forms tests aligned with the async-first testing pattern.
Replace broken link with inline code
formatting. The link was pointing to the page itself, as no dedicated block concept page exists at that URL.
corrects 'everytime' to 'every time'.
Update tools/manual_api_docs/blocks/let.md
Co-authored-by: Matthieu Riegler <kyro38@gmail.com>
Two custom properties in the shared-docs styles reference tokens that are
defined nowhere, so the declarations are invalid at computed-value time.
_kbd.scss sets the <kbd> text color with var(---tertiary-contrast); the extra
leading dash points at an undefined property with no fallback, so the color
resolves to the inherited value instead of the intended --tertiary-contrast.
_colors.scss builds --light-pink-to-light-purple-horizontal-gradient from
var(--light-purple), which is not defined anywhere (the sibling token is
--light-violet, defined in the same file), invalidating the gradient. The
gradient is not currently referenced, so this corrects a latent malformed
declaration rather than a visible bug.
Point both at the defined tokens: --tertiary-contrast and --light-violet.
Extend the Angie illustrations to the empty states that were still plain
text: the injector providers "No such providers" result, the injector tree
unsupported-version notice, the two profiler timeline placeholders, and the
router tree unsupported-version notice.
Adds the magnifying-glass and question poses as assets; the orthos-back
and sad poses are already in the tree.
In the light theme block of _colors.scss, --webgl-page-background and
--webgl-gray-unfilled were written on a single line without a separating
semicolon, and --webgl-page-background wrapped --page-background in an
invalid nested var(). As a result --webgl-page-background got a garbage
value and --webgl-gray-unfilled was never defined in the light theme.
Split them into two well-formed declarations and drop the nested var(),
matching the structure already used in the dark theme block.
The retro-themed aria autocomplete examples style the clear button with
var(--win95-gray), var(--win95-dark-gray), var(--win95-light), and
var(--win95-shadow), but none of the three example stylesheets defines
those custom properties. The variables resolve to nothing, so the button
falls back to inherited theme colors — color: var(--win95-shadow) becomes
the light-theme text color in dark mode, leaving the button unreadable.
Define the four variables in :host of each example, matching the values
already used by the sibling aria tree examples, so the clear button keeps
its Windows 95 look in both light and dark themes.
The retro variants of the toolbar, select, and multiselect aria examples
tint the pressed button background with
color-mix(in srgb, var(--retro-button-color) 60%, var(--gray-50)), but
--gray-50 is defined nowhere. Per the CSS spec, a var() with no fallback
pointing at an undefined property invalidates the whole color-mix(), so the
declaration is dropped and the pressed-state tint never applies.
Define a local --retro-pressed-tint token in each example's :host (#fbfbfb,
the value $gray-50 resolved to) with a :host-context(.docs-dark-mode) override
to #151417, and use it for the pressed-state mix. This keeps the example
self-contained rather than depending on adev's global token scope, while
staying theme-aware in both light and dark, following the same token-flipping
pattern used in code-editor.component.scss.
Adds the `isFieldTree` utility that allows users to assert whether a value is a field tree. This is something that has come up on Material recently and will be useful for users as well.
Fixes#69984.
`@HostListener` is not limited to methods — it is equally valid on a property
holding a function, which is the idiomatic way to keep `this` bound:
@HostListener('window:beforeunload', ['$event'])
private onUnload = (event: BeforeUnloadEvent) => {...};
Every existing host-listener compliance case declares the handler as a method,
so the property form was uncovered. This adds a case exercising both a public
and a private function-valued property, one of them with a global (`window:`)
event target, and locks in the emitted chained `ɵɵlistener` calls plus
`ɵɵresolveWindow`.
Verified against all four compliance modes (full, partial/linked,
declaration-only); GOLDEN_PARTIAL.js regenerated via the golden update rule.
Updates the strictTemplates documentation in Angular compiler options to note that the default is true, replacing the reference to the obsolete ng new --strict flag.
Conditionally update the browser DevTools tab name to "Angular & Wiz" if the user is identified as a Google employee via internal corp network detection.
Include the XSRF interceptor in the root token factory so the automatically provided HttpClient retains the documented default protection without requiring provideHttpClient().
Normalize value-specific HttpHeaders deletions before filtering. The string overload previously used String#indexOf and removed shorter values contained within the requested deletion value, potentially widening outgoing request metadata.
Preserve delete-all behavior only when no value is supplied, and cover string, array, and empty-string deletion.
Prevent lazy HttpHeaders and HttpParams clones from reusing value arrays owned by a materialized source. Append and value-specific delete operations previously mutated those shared arrays, violating the immutable API contract and allowing request metadata to bleed into later requests.
Share value arrays until an update mutates a specific header or parameter, then copy only that array. Cover the affected append and delete paths with regression tests that materialize the source first.
Rename Timing API to Performance Track to better reflect the purpose of the actual feature;
Migrate the settings data object to match the new name and drop some redundant keys.
Make runtime URL sanitizer selection namespace-aware so SVG and MathML host bindings match the security schema.
Cover SVG href/xlink:href and MathML href host binding cases, including dynamic hostElement resolution.
PR Close#69558
Host binding sanitization previously used the declaring directive or component selector to choose a compile-time security context. The same host binding can execute on a different concrete element through hostDirectives, inherited host bindings, dynamic directives, or createComponent hostElement usage.
Compute host binding security contexts against possible concrete hosts and defer URL versus ResourceURL selection to runtime when necessary. Resolve dynamic root host TNodes to their native tag before sanitizer and security-sensitive attribute checks.
Fixes angular#69550
PR Close#69558
The control flow migration determines whether an `*ngIf` uses a `then`
and/or `else` clause by regex matching the raw microsyntax string for
the literal keywords `then`/`else`. The regexes only checked that the
keyword was preceded by a non-word character, but not that it was
followed by one.
As a result, a template reference name that merely starts with `then`
(e.g. `else thenBlock`) or `else` was misidentified as the `then`/`else`
keyword itself. This caused the migration to take the wrong code path
(e.g. then+else instead of else-only), which in turn made
`getTemplateName()` compute a `slice(start, end)` with `start > end`,
producing an empty template name. That empty placeholder was never
resolved and was silently emitted as an invalid
`<ng-template [ngTemplateOutlet]=""></ng-template>`, dropping the
original template content without any warning.
Add a negative lookahead `(?![\w\d])` to both regexes so `then`/`else`
are only matched as whole keywords, not as a prefix of a longer
template reference name.
Fixes#69914
Reformats the TEST_CASES.json files touched by the following change so they
satisfy the repo's prettier check (short inputFiles/files arrays collapsed to a
single line). Pure formatting; the parsed JSON is unchanged. Split into its own
commit so the coverage change that follows is easy to review.
We were raising the suggestion about enabling `strictTemplates` when `strictTemplates` is ommitted, however the option is now enabled by default.
Fixes#69905.
## Summary
This PR adds v22.x and v23 release dates. It also changes Angular's release cadence to a yearly cycle.
## Why we are making this change
The community has long requested less frequent major releases due to the impact of breaking changes and upgrades for their projects as well as for enterprise customers. Additionally, a longer release cycle provides increased API stability for developers using agentic workflows, while still delivering a reasonable cadence of API upgrades and migrations.
@@ -13,7 +13,8 @@ When reviewing a pull request for the `angular` repository, follow these essenti
2.**Key Focus Areas**:
- **Comprehensive Reviews**: You **MUST always** perform a deep, comprehensive review of the _entire_ pull request. If the user asks you to look into a specific issue, file, or area of concern, you must investigate that specific area _in addition to_ reviewing the rest of the PR's substantive changes. Do not terminate your review after addressing only the user's focal point.
- **Package-Specific Guidelines**: Check if there are specific guidelines for the package being modified in the `reference/` directory (e.g., `reference/router.md`). Always prioritize these rules for their respective packages.
- **Package-Specific & Topic Guidelines**: Check if there are specific guidelines for the package or topic being modified in the `reference/` directory (e.g., `reference/router.md` or `reference/object_create_null.md`). Always prioritize these rules for their respective areas.
- **Prototype Collision & `Object.create(null)` PRs**: When reviewing PRs that swap `{}` for `Object.create(null)`, consult `reference/object_create_null.md` for technical evaluation criteria and rules.
- **Commit Messages**: Evaluate the quality of commit messages. They should explain the _why_ behind the change, not just the _what_. Someone should be able to look at the commit history years from now and clearly understand the context and reasoning for the change.
- **Code Cleanliness**: Ensure the code is readable, maintainable, and follows Angular's project standards.
- **Performance**: Look out for code that might negatively impact runtime performance or bundle size, particularly in hot paths like change detection or rendering.
# Rules for `Object.create(null)` and Prototype Collision Prevention
This guide outlines the technical rules and evaluation criteria for using `Object.create(null)` versus standard object literals (`{}`) or `Map` in the Angular codebase.
---
## 1. When `Object.create(null)` is Appropriate
Using `Object.create(null)` (or `Map`) is appropriate when **all** of the following conditions are met:
1. The object is used as an **internal key-value lookup map or set**.
2. The keys are **arbitrary or untrusted dynamic strings** (e.g., URL query parameters in `$locationShim`, HTML sanitizer tag sets, or `jsaction` DOM event-type resolvers).
3. Property existence is checked via direct indexing or key checks (e.g., `map[key] !== undefined` or `key in map`), where a key matching an `Object.prototype` member (such as `'toString'`, `'constructor'`, or `'hasOwnProperty'`) causes false positive matches or incorrect behavior.
---
## 2. Handling Public API and Boundary Objects
If an object receives untrusted dynamic keys **and** is exposed to public consumers or third-party code (e.g., `SimpleChanges` in `ngOnChanges`):
- **Do NOT blindly change the object to `Object.create(null)`**: Stripping `Object.prototype` from public objects is a breaking API change. Consumer code calling `.hasOwnProperty()`, `.toString()`, `.valueOf()`, or using string interpolation (`` `${obj}` ``) will fail at runtime (`TypeError: obj.hasOwnProperty is not a function`).
- **Safe Alternatives for Public Objects:**
- **`Object.hasOwn(obj, key)`**: Use `Object.hasOwn` for internal framework property lookups instead of direct index or `in` checks. This prevents prototype collision during internal reads without breaking the object's prototype for consumers.
- **Input Key Sanitization**: Filter or delete dangerous key names (`__proto__`, `constructor`, `prototype`) when populating the object.
- **`Map` or Custom Classes**: For new public APIs requiring key-value stores with dynamic keys, prefer `Map<K, V>` or dedicated classes with explicit `.get()` and `.has()` methods.
- **Deprecation / Breaking Change Process**: If changing a public object's prototype to `null` is unavoidable, it must follow Angular's formal deprecation and major version breaking change process.
---
## 3. When `Object.create(null)` Should NOT Be Used
Do not replace `{}` with `Object.create(null)` in the following scenarios:
1.**Fixed-Shape Structs and DTOs:** Objects with hardcoded static property names (e.g., `let sortedBreakpoints: {breakpoints?: number[]} = {}`). `Object.assign({}, ...)` only copies _own_ enumerable properties, so prototype properties on sources are never copied.
2.**Numeric-Key Maps:** Objects indexed by numbers (e.g., `tasksByHandleId: {[id: number]: Task}`). Numeric keys do not collide with `Object.prototype` string members.
3.**Reference Sentinels:** Objects used purely for reference identity checks (e.g., `const EMPTY_OBJECT = {}` or `const IN_PROGRESS_RESOLUTION = {}`).
4.**Internal Compiler AST and Visitor State:** Temporary objects with internally generated keys where untrusted user input cannot poison key names.
5.**Hot Performance Paths and Size-Critical Bundles:** Standard `{}` literals use V8 fast hidden classes and monomorphic inline caching. `Object.create(null)` forces V8 dictionary mode and increases minified bundle size (e.g., in inline polyfills like `event-dispatch-contract` or SSR hydration bundles).
This document explains how the Signal Forms system hooks into the Angular compiler and runtime to provide seamless type-checking and efficient updates.
## 1. Compiler-CLI Integration (Type Checking)
## 1. Compiler Integration (Type Checking)
The `packages/compiler-cli` package contains specific logic to support `[formField]`. This is primarily handled in `src/ngtsc/typecheck/src/ops/signal_forms.ts`.
The `packages/compiler` package contains specific logic to support `[formField]`. This is primarily handled in `src/typecheck/ops/signal_forms.ts`.
### Key Mechanisms:
@@ -22,7 +22,7 @@ The `packages/compiler-cli` package contains specific logic to support `[formFie
### Relevant Files:
-`packages/compiler-cli/src/ngtsc/typecheck/src/ops/signal_forms.ts`: The core logic for `TcbNativeFieldOp` and `SignalFormFieldOp`.
-`packages/compiler/src/typecheck/ops/signal_forms.ts`: The core Signal Forms type-checking logic, including `TcbNativeFieldOp`.
## 2. Core Runtime Integration
@@ -30,8 +30,8 @@ The `packages/core` package provides the low-level instructions that power the `
### Key Mechanisms:
- **`ɵngControlCreate` Hook**: The `FormField` directive defines a special method `ɵngControlCreate`.
- **`ɵɵcontrol` Instructions**: When the compiler sees `ɵngControlCreate`, it emits:
- **`ɵngControlCreate` Hook**: The `FormField` directive defines a special method `ɵngControlCreate`, which causes the compiler to install `ɵɵControlFeature`.
- **`ɵɵcontrol` Instructions**: A `formField` binding causes the compiler to emit:
-`ɵɵcontrolCreate`: Called during the creation phase.
-`ɵɵcontrol`: Called during the update phase.
- **`ControlDirectiveHost`**: These instructions provide the directive with a `ControlDirectiveHost`. This is a privileged interface that allows the `FormField` directive to:
@@ -43,4 +43,4 @@ The `packages/core` package provides the low-level instructions that power the `
### Relevant Files:
-`packages/core/src/render3/instructions/control.ts`: Implementation of `ɵɵcontrol` instructions.
-`packages/forms/signals/src/directive/form_field_directive.ts`: The directive that implements the hook.
-`packages/forms/signals/src/directive/form_field.ts`: The directive that implements the hook.
| [f0a271c7bd](https://github.com/angular/angular/commit/f0a271c7bd4be000c52592034573cda9866f26bb) | fix | do not flag callable objects with zero parameters in uninvoked track function check |
| [bd9b45b5cc](https://github.com/angular/angular/commit/bd9b45b5cc1dd904cc4a5de45f6de8e1564b70b6) | feat | allow reading `Injector` from a view or content query |
| [70756b506c](https://github.com/angular/angular/commit/70756b506c508faa908fe8917e1f6937bcd6df77) | fix | apply SkipSelf to only the starting node in embedded views |
### language-service
| Commit | Type | Description |
| -- | -- | -- |
| [f4a5650ed9](https://github.com/angular/angular/commit/f4a5650ed9c71a8ee1dbd3003e13900464827757) | feat | add support for `@boundary` blocks ([#70463](https://github.com/angular/angular/pull/70463)) |
| [3064f3f1dc](https://github.com/angular/angular/commit/3064f3f1dccd78177bf3b86f8ea231102884f0d7) | feat | expose router resources in public API |
| [094bce9e3d](https://github.com/angular/angular/commit/094bce9e3defe7e9fd56f951641f3bb74248d06a) | fix | determine blocking state solely by resource loading status |
| [aea121e532](https://github.com/angular/angular/commit/aea121e53217a7f74d2a8e4a7a6bf0f3a96b7c49) | fix | do not flag callable objects with zero parameters in uninvoked track function check |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [c19b948ef8](https://github.com/angular/angular/commit/c19b948ef8d34e5a15e41cd5a88cdd4bda848f8f) | fix | apply SkipSelf to only the starting node in embedded views |
| [91a2bf8425](https://github.com/angular/angular/commit/91a2bf84250f9b0a28a8a68e1eded6e9506bc5e9) | feat | support annotations in WebMCP tool declarations |
| [0904f90b13](https://github.com/angular/angular/commit/0904f90b13ac2a1bad22a2b43fb232957dba44c3) | fix | cancel stale debounce timers to prevent timer leaks |
| [58d536bc83](https://github.com/angular/angular/commit/58d536bc8362f3942c929e58be551d871b00cea7) | fix | don't fail NgModule checks for a pipe that extends a base class |
| [6e299da8cf](https://github.com/angular/angular/commit/6e299da8cfc7df49029d5d8bb7d5739560e1aaa4) | feat | hard-code `readOnlyHint` and `untrustedContentHint` for WebMCP implicit signal forms |
| [38861ac41b](https://github.com/angular/angular/commit/38861ac41bb373870e961f41626de6b6b31120f1) | fix | avoid writing to `name` input on ControlValueAccessor |
| [b3bb36ad87](https://github.com/angular/angular/commit/b3bb36ad87334f7a86908c1b2b4353c1cc514c2e) | fix | resolve HTTP(S) URLs without authority as relative during SSR |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [7137a41223](https://github.com/angular/angular/commit/7137a41223079b4b172aeccb5031347fcc947b79) | feat | stabilize auto cleanup injectors feature |
| [af26a8c521](https://github.com/angular/angular/commit/af26a8c52178a0e972c2841051db44f9d04ac371) | fix | avoid view transitions when the user agent provides one |
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.1.5"></a>
# 22.1.5 (2026-09-02)
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [d90698dae7](https://github.com/angular/angular/commit/d90698dae759f21f29052e521ffaec6c3a79f6d1) | fix | check uninvoked signal aliases in extended diagnostic |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [2ceeb27078](https://github.com/angular/angular/commit/2ceeb27078d890514a1e87b0d99f366d9a4a796f) | fix | cancel stale debounce timers to prevent timer leaks |
| [ed2e401d2d](https://github.com/angular/angular/commit/ed2e401d2da9a6dd5c87006573a8e42934f58f62) | fix | don't fail NgModule checks for a pipe that extends a base class |
| [3b8723ce3d](https://github.com/angular/angular/commit/3b8723ce3d88b2bf871185736bc33e3cf9a189f4) | fix | resolve HTTP(S) URLs without authority as relative during SSR |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [4340a63c52](https://github.com/angular/angular/commit/4340a63c52fca4d08ef258a3c053f50932eab27e) | fix | avoid view transitions when the user agent provides one |
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.2.0-next.4"></a>
# 22.2.0-next.4 (2026-08-26)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [862a0c8ab3](https://github.com/angular/angular/commit/862a0c8ab3403242eeac3b2fb80f7cf63cf0c892) | fix | avoid prototype member collisions |
| [7596548e9b](https://github.com/angular/angular/commit/7596548e9b836a5d12e966bf296a4dbba044b78f) | fix | use locale NaN symbol in number formatting |
| [83f7695b2e](https://github.com/angular/angular/commit/83f7695b2e4e1cab347f7817da4ae4262dade415) | fix | throw coded RuntimeErrors instead of crashing when hydration/rendering can't find an expected DOM node |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [1e35de536d](https://github.com/angular/angular/commit/1e35de536d8332dfcbb3d89d01ca84b50aa56bd2) | fix | use dot-access for readonly rule configuration |
| [60a874c3fb](https://github.com/angular/angular/commit/60a874c3fbedc2ce216bcf783325f4e1291edcb0) | fix | avoid prototype member collisions |
| [e8378dfeab](https://github.com/angular/angular/commit/e8378dfeab39fca5bcdc47126b7859c8fdab7a28) | fix | use locale NaN symbol in number formatting |
| [7546b7a805](https://github.com/angular/angular/commit/7546b7a80549c9620d52692cb0f5cab4c69b546c) | fix | throw coded RuntimeErrors instead of crashing when hydration/rendering can't find an expected DOM node |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [c8eb7f0056](https://github.com/angular/angular/commit/c8eb7f00564beceb21445b1f0374c93854b513d1) | fix | use dot-access for readonly rule configuration |
| [732e505018](https://github.com/angular/angular/commit/732e5050180190df24768ace7aa3e785ca0dcc0a) | fix | replace all hasOwnProperty usages with Object.hasOwn |
| [c658d73210](https://github.com/angular/angular/commit/c658d73210d1a2e808cfe659dddd48e8389086c7) | fix | stop running further effects once one destroys the view mid-flush |
| [c6e4a36be1](https://github.com/angular/angular/commit/c6e4a36be1246e0fbe06d591a69ac79cd74348d2) | fix | throw a descriptive error instead of crashing when a hydration node is missing |
| [4560f4fdcd](https://github.com/angular/angular/commit/4560f4fdcda4d55c9234f71fda2e73938cb55ed6) | fix | throw NG0500 instead of a raw TypeError on element hydration mismatch |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [c819880b91](https://github.com/angular/angular/commit/c819880b91bf5ff1c5aa71afd314ae7cae241065) | fix | report forbidden 2way bindings on when `FormField` is applied |
| [38d093232c](https://github.com/angular/angular/commit/38d093232c6d2fad99f2e361e9f6e31923507658) | fix | warn in dev mode when ngModel cannot reach parent NgForm across component boundary |
| [3f8d9d6ea6](https://github.com/angular/angular/commit/3f8d9d6ea6a0a0e7edbc12baf08b1f6db8c0f6b8) | fix | recover project for external templates in solution-style workspaces |
### platform-browser
| Commit | Type | Description |
| -- | -- | -- |
| [6f9a6bea50](https://github.com/angular/angular/commit/6f9a6bea50944fff894eb5629075b13f8c3090d7) | fix | disallow event handler attributes in Meta |
| [1cb3d606bf](https://github.com/angular/angular/commit/1cb3d606bf5bb01ec9c9e9289703a79f64c76e89) | fix | throw a descriptive error when insertBefore reference node is missing |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [2720362818](https://github.com/angular/angular/commit/2720362818cdeb2a940171e4ab6f21cf78c6a302) | feat | add containsTree as public API |
| [f1a4c85212](https://github.com/angular/angular/commit/f1a4c8521296d7414a1eab84352ca7db966f35e2) | fix | pass correct component to canDeactivate for named outlets in componentless parent routes |
| [44137117b3](https://github.com/angular/angular/commit/44137117b361f5bbad64e3d488e9aefd2f9ec5cb) | fix | replace all hasOwnProperty usages with Object.hasOwn |
| [85f12a5a13](https://github.com/angular/angular/commit/85f12a5a130aa7fd5cd21fce3eb786a2262480e1) | fix | stop running further effects once one destroys the view mid-flush |
| [c04931c88b](https://github.com/angular/angular/commit/c04931c88b1c7959cb95abd28cf5b3614d97e168) | fix | throw a descriptive error instead of crashing when a hydration node is missing |
| [601d1f6576](https://github.com/angular/angular/commit/601d1f6576b296748f56ffb50646bb1808a7c0fc) | fix | throw NG0500 instead of a raw TypeError on element hydration mismatch |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [83450d2924](https://github.com/angular/angular/commit/83450d292443cae811e87ab61020f3ed72d27d77) | fix | report forbidden 2way bindings on when `FormField` is applied |
| [5cb4ea7e35](https://github.com/angular/angular/commit/5cb4ea7e355bb9ea43efa15a03dc38c53d2e4faf) | fix | warn in dev mode when ngModel cannot reach parent NgForm across component boundary |
| [14fbe04612](https://github.com/angular/angular/commit/14fbe04612d29460c5d01d6c65cde579f0a42e95) | fix | recover project for external templates in solution-style workspaces |
### platform-browser
| Commit | Type | Description |
| -- | -- | -- |
| [3ddcb1a101](https://github.com/angular/angular/commit/3ddcb1a10168ead616e523bbe044f4c39ada4cca) | fix | disallow event handler attributes in Meta |
| [640460d606](https://github.com/angular/angular/commit/640460d6065cdcd8fe407c7b35883f81f5045473) | fix | throw a descriptive error when insertBefore reference node is missing |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [abe019d505](https://github.com/angular/angular/commit/abe019d5050532c87179cf46e840965e70a78b1b) | fix | pass correct component to canDeactivate for named outlets in componentless parent routes |
<!-- CHANGELOG SPLIT MARKER -->
<a name="20.3.29"></a>
# 20.3.29 (2026-08-19)
### platform-browser
| Commit | Type | Description |
| -- | -- | -- |
| [7538744c10](https://github.com/angular/angular/commit/7538744c1048701d12e86e992704ea5fa13df4f4) | fix | disallow event handler attributes in Meta |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.21"></a>
# 21.2.21 (2026-08-19)
### platform-browser
| Commit | Type | Description |
| -- | -- | -- |
| [c19a36c2fb](https://github.com/angular/angular/commit/c19a36c2fbc51f41ce974ea1fd87f9f56b3e2444) | fix | disallow event handler attributes in Meta |
| [e9660b1801](https://github.com/angular/angular/commit/e9660b1801a0280fa5ba41e7c330cb6d597199ec) | fix | correctly resolve symbol for SafePropertyRead in chained optional navigation |
| [ec6deea513](https://github.com/angular/angular/commit/ec6deea51329b0c8410c75943d90ce823081e216) | fix | record class extends clause references in DeferredSymbolTracker |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [0df9d5eb65](https://github.com/angular/angular/commit/0df9d5eb6516106188b879536f7f813a9ca8811c) | fix | ensure i18n_util hasOwnProperty checks are safe for property renaming |
| [3b5c798072](https://github.com/angular/angular/commit/3b5c798072a3cf43b5e0ec0b3f4abc18bc6196a5) | fix | keep radio inputs in sync when values change |
| [a4f1a94948](https://github.com/angular/angular/commit/a4f1a949486d20ce2bf9add427eea6629228b28b) | fix | do not encapsulate nested selectors if parent contains ::ng-deep |
| [e8aa222e7d](https://github.com/angular/angular/commit/e8aa222e7d98d2e54c5604107a929a11cb1ef62d) | fix | correctly resolve symbol for SafePropertyRead in chained optional navigation |
| [8d6c925392](https://github.com/angular/angular/commit/8d6c925392c7230fbe6b39abdad05381672631b1) | fix | record class extends clause references in DeferredSymbolTracker |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [8925a4ee49](https://github.com/angular/angular/commit/8925a4ee491aebaf6a1a74880c73dfb20e0a4ba1) | fix | ensure i18n_util hasOwnProperty checks are safe for property renaming |
| [9e37a58e14](https://github.com/angular/angular/commit/9e37a58e14bd7955d40255e42e20aa84fcde02f4) | fix | keep radio inputs in sync when values change |
| [e4c416c20a](https://github.com/angular/angular/commit/e4c416c20a1cb222ce73d29c035452b257380c56) | fix | run root interceptors in the terminal request chain |
| [caf616670f](https://github.com/angular/angular/commit/caf616670fd20d528aa69e0131cc17d60f0cc27d) | fix | run root interceptors in the terminal request chain |
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.2.0-next.1"></a>
# 22.2.0-next.1 (2026-08-07)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [cfe2cda110](https://github.com/angular/angular/commit/cfe2cda1100bb84e2ecf954687126c4d30212d07) | fix | initialize hydration triggers after late runtime activation |
| [6a0789dc7f](https://github.com/angular/angular/commit/6a0789dc7f0ac9d1249c4d9f315e83424867169b) | fix | respect content-type charset in fetch backend text decoder |
| [bb78286e5e](https://github.com/angular/angular/commit/bb78286e5e729d1e1fcfd2733d88351a38083ef5) | fix | run root interceptors in the terminal request chain |
| [280d09b160](https://github.com/angular/angular/commit/280d09b16011e4f53f9e696fd95964943c6dcbfa) | fix | strip RFC 6265 DQUOTE characters and handle URIError in parseCookieValue |
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.1.1"></a>
# 22.1.1 (2026-08-06)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [aa6d3189c1](https://github.com/angular/angular/commit/aa6d3189c141a9247615ed14d856813ea1b95c8d) | fix | initialize hydration triggers after late runtime activation |
| [688a0a7118](https://github.com/angular/angular/commit/688a0a7118215bcab10004a3bf2667773fefdf74) | fix | respect content-type charset in fetch backend text decoder |
| [a13b968451](https://github.com/angular/angular/commit/a13b9684510dfd04e18f3e1d6686d556c051f34e) | fix | run root interceptors in the terminal request chain |
| [32af9b525e](https://github.com/angular/angular/commit/32af9b525efe005998a9cd02aebfcc08e6793cf2) | fix | strip RFC 6265 DQUOTE characters and handle URIError in parseCookieValue |
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.2.0-next.0"></a>
# 22.2.0-next.0 (2026-07-29)
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [d5e8b1ef7a](https://github.com/angular/angular/commit/d5e8b1ef7a02c84d4fd70a6b4d748ead9ff815bf) | feat | allow permanent hidden fields in signal forms |
| [f98547675c](https://github.com/angular/angular/commit/f98547675cd2026bb755671b28dea4e6d48eb7bb) | feat | Namespace CSS variables to the app ([#68846](https://github.com/angular/angular/pull/68846)) |
| [8c8b2f7783](https://github.com/angular/angular/commit/8c8b2f77831236b23ca99ccb62e6197ff43b400d) | feat | Support css var namespacing in properties ([#68846](https://github.com/angular/angular/pull/68846)) |
| [292199aa4d](https://github.com/angular/angular/commit/292199aa4dd4cbf09f14c20bd7049d6ab55d83f4) | fix | permissive whitespace parsing in default never blocks |
| [25c744c4d0](https://github.com/angular/angular/commit/25c744c4d0abd3bfe863ed2d528fea756ec5801b) | fix | support foreign components defined outside top-level scope |
| [5bd00add07](https://github.com/angular/angular/commit/5bd00add071e404ddab318eea82db81b5715a194) | fix | support foreign components inside control flow blocks ([#69674](https://github.com/angular/angular/pull/69674)) |
| [2e442f7876](https://github.com/angular/angular/commit/2e442f787627a1f44d8b6463a1cb979ef6f7e049) | perf | do not emit tag name when control flow root is foreign component ([#69674](https://github.com/angular/angular/pull/69674)) |
| [7c60a98b3c](https://github.com/angular/angular/commit/7c60a98b3ccc2090f3bfb3c76d6207f37a5df0ff) | fix | support import aliases in foreignImports ([#68674](https://github.com/angular/angular/pull/68674)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [124ba10ead](https://github.com/angular/angular/commit/124ba10ead58c9f93b0b74c4102022c4674db1f5) | feat | add custom set option to linkedSignal |
| [eab4847a8b](https://github.com/angular/angular/commit/eab4847a8bd1b47ba630ca9b20c85770f990c717) | feat | Adds deep linking from Performance panel to DevTools |
| [091456a214](https://github.com/angular/angular/commit/091456a2146d81b225fa9322a9ebde24695aaad0) | fix | account for namespaces in host binding sanitization ([#69558](https://github.com/angular/angular/pull/69558)) |
| [b3748e9fe4](https://github.com/angular/angular/commit/b3748e9fe4a4f9f56f9843b448db59674ad01452) | fix | correct container anchor collection order to match DOM layout |
| [11b206b919](https://github.com/angular/angular/commit/11b206b919074149f2db6e3640a55284aecfe48a) | fix | introduce disposal mechanism for Angular views in foreign `@content` |
| [0c07356c5c](https://github.com/angular/angular/commit/0c07356c5c9dc76a532d7d4ecf43d8fce81b405b) | fix | set current tnode in foreign component instruction on reuse |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [bbbd357bd2](https://github.com/angular/angular/commit/bbbd357bd2b29f0b04578e3fa651418df4aefc67) | fix | add utility to assert that value is a field tree |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [e3630c23c5](https://github.com/angular/angular/commit/e3630c23c59445fc8bace5117283f20d6379dded) | feat | add options to allow caching of credentialed and non-cacheable HTTP requests |
| [4f9c824dd9](https://github.com/angular/angular/commit/4f9c824dd9ec4462d29ed07b5e7916be86c19e84) | feat | Typecheck templates which would require inline typecheck blocks ([#68454](https://github.com/angular/angular/pull/68454)) |
| [a99fb915c0](https://github.com/angular/angular/commit/a99fb915c09b352ac67f24e71af84cda30bcbf3e) | fix | account for strictTemplates being enabled by default |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [c75ff0255c](https://github.com/angular/angular/commit/c75ff0255c3d5ee16f94a9969cd611ab2e02d4a2) | feat | add migration from injectable to service |
| [5d5b2ea72d](https://github.com/angular/angular/commit/5d5b2ea72d03ffc2350e66e202cbca0bf491a5f2) | fix | correctly detect `then`/`else` keywords in control flow migration |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [cf9d7fa0f8](https://github.com/angular/angular/commit/cf9d7fa0f8090bae61bb4fa2f50d6f0570412e6e) | fix | harden platform location origin validation during SSR ([#69184](https://github.com/angular/angular/pull/69184)) |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [97a3fd6a55](https://github.com/angular/angular/commit/97a3fd6a55eccabf7c9adfb33f745c8d1da6d3e9) | feat | handle null and undefined inputs in RouterLinkActive |
| [6f80cca0b8](https://github.com/angular/angular/commit/6f80cca0b8af23b37f24fb8ee0229b6901d01df2) | fix | update domino to latest version |
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.0.8"></a>
# 22.0.8 (2026-07-22)
### common
@@ -24,50 +652,6 @@
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.1.0-next.6"></a>
# 22.1.0-next.6 (2026-07-15)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [359fb503b8](https://github.com/angular/angular/commit/359fb503b8f262db7a053febc18c416f966e8185) | fix | avoid prototype lookups in date format caches |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [da52137724](https://github.com/angular/angular/commit/da52137724b8cce79b036026fa1aa07bc730389c) | fix | parsing of an empty template literal interpolation |
| [5bd00add07](https://github.com/angular/angular/commit/5bd00add071e404ddab318eea82db81b5715a194) | fix | support foreign components inside control flow blocks ([#69674](https://github.com/angular/angular/pull/69674)) |
| [2e442f7876](https://github.com/angular/angular/commit/2e442f787627a1f44d8b6463a1cb979ef6f7e049) | perf | do not emit tag name when control flow root is foreign component ([#69674](https://github.com/angular/angular/pull/69674)) |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [ae0ec7315c](https://github.com/angular/angular/commit/ae0ec7315c6fbc45ce40581a08a8243a008a91ec) | fix | re-tag SourceFiles after TsCreateProgramDriver.updateFiles() |
| [2e32872720](https://github.com/angular/angular/commit/2e32872720ee95f24029d20e24e91edbc2054b1e) | fix | preserve intermediate number values in signal forms |
| [2e0cb52dbf](https://github.com/angular/angular/commit/2e0cb52dbf0f519ffc5c99b95e5f4b965fa332a7) | fix | prevent stale CVA writeback during debounce |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [6d043f8657](https://github.com/angular/angular/commit/6d043f86573e317f20039ccec031d6e63c01b656) | fix | prevent interceptor signal reads from leaking into calling reactive contexts |
### localize
| Commit | Type | Description |
| -- | -- | -- |
| [5de0ea5f23](https://github.com/angular/angular/commit/5de0ea5f23f12542ebdd9d28f225e1a224021d92) | fix | build runtime translations map with a null prototype |
| [2a4f582731](https://github.com/angular/angular/commit/2a4f58273112d0ed508fac624e56f56d97e511fa) | fix | use Object.hasOwn for placeholder lookup in translate |
### platform-browser
| Commit | Type | Description |
| -- | -- | -- |
| [fb6b354fbd](https://github.com/angular/angular/commit/fb6b354fbd3ecf6c2cdf211ca054fb44226e587b) | fix | prevent ReDoS in SOURCEMAP_URL_REGEXP |
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.0.7"></a>
# 22.0.7 (2026-07-15)
### common
@@ -156,50 +740,6 @@
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.1.0-next.5"></a>
# 22.1.0-next.5 (2026-07-08)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [af5e4e1131](https://github.com/angular/angular/commit/af5e4e113130427ba8152166b4d904dd177c982e) | feat | Add an error for --global-foo cases ([#68846](https://github.com/angular/angular/pull/68846)) |
| [f98547675c](https://github.com/angular/angular/commit/f98547675cd2026bb755671b28dea4e6d48eb7bb) | feat | Namespace CSS variables to the app ([#68846](https://github.com/angular/angular/pull/68846)) |
| [8c8b2f7783](https://github.com/angular/angular/commit/8c8b2f77831236b23ca99ccb62e6197ff43b400d) | feat | Support css var namespacing in properties ([#68846](https://github.com/angular/angular/pull/68846)) |
| [997b772f28](https://github.com/angular/angular/commit/997b772f28f92de64b416263bb6dc02dc9e7598d) | fix | use regular optional chaining expression for safe function calls in TCBs |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [eb2a8ff63f](https://github.com/angular/angular/commit/eb2a8ff63f858110c6c77289f3c0150e49216624) | fix | apply debugName transform to required signal queries |
| [eac363e92e](https://github.com/angular/angular/commit/eac363e92e00d26137a93d91ab3e2127f3519b1d) | fix | detect uninvoked signals in bound expressions using ternary |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [eab4847a8b](https://github.com/angular/angular/commit/eab4847a8bd1b47ba630ca9b20c85770f990c717) | feat | Adds deep linking from Performance panel to DevTools |
| [fbb705e6fd](https://github.com/angular/angular/commit/fbb705e6fd1f217a9576dfb76b193d08da1a2195) | fix | remove stale model import in model-output migration |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [cbbb1d8ba1](https://github.com/angular/angular/commit/cbbb1d8ba12ca36d27a2523edfbd1466458c5036) | fix | handle outlet named __proto__ in segment group maps |
| [e5c37f21de](https://github.com/angular/angular/commit/e5c37f21de3d9801859641b2ffb180d90b3bc00d) | fix | use safe hasOwnProperty when parsing query params |
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.0.6"></a>
# 22.0.6 (2026-07-08)
### compiler
@@ -228,32 +768,6 @@
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.1.0-next.4"></a>
# 22.1.0-next.4 (2026-07-01)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [311aff05aa](https://github.com/angular/angular/commit/311aff05aa2cd55d49a0c259e7b240266ecee7bd) | fix | use Object.hasOwn in I18nSelectPipe to handle null-prototype and shadowed mappings |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [292199aa4d](https://github.com/angular/angular/commit/292199aa4dd4cbf09f14c20bd7049d6ab55d83f4) | fix | permissive whitespace parsing in default never blocks |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [165995285c](https://github.com/angular/angular/commit/165995285ca95302b2a2f01f5b42645b0a9eb022) | fix | include toSignal in debugName transform |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [74638cab84](https://github.com/angular/angular/commit/74638cab841c553fc85fc23a0e5052931a636531) | fix | improve input writes migration in best effort mode |
| [8d31b82116](https://github.com/angular/angular/commit/8d31b82116c4128a00302abd7c4dfc035c77789e) | fix | support model() signals in downgradeComponent |
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.0.3"></a>
# 22.0.3 (2026-06-25)
### compiler
@@ -363,37 +830,6 @@
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.1.0-next.1"></a>
# 22.1.0-next.1 (2026-06-17)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [a69e56df71](https://github.com/angular/angular/commit/a69e56df718ccf139f0b3f8f528c87a6818d9ba2) | fix | escape anchor fragment in shadow DOM name selector |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [417a4071a7](https://github.com/angular/angular/commit/417a4071a776464d549509ed3aec121dbd2fda5e) | fix | restrict possible event handler check to property names longer than 2 characters |
| [8cf7731468](https://github.com/angular/angular/commit/8cf7731468f2afcfa03cbddefe90b57dff680d30) | fix | guard against DOM clobbering in declareExperimentalWebMcpTool |
| [6b5616b2c7](https://github.com/angular/angular/commit/6b5616b2c7745ff4777f8d2ea038ebd297aeeb38) | fix | preserve leave animation for sibling instances sharing a TNode |
| [28cb15a2bb](https://github.com/angular/angular/commit/28cb15a2bb79a072fce661b14e87954096ee9366) | fix | prevent unsubscribe during emit from throwing off other listeners |
| [327744ac17](https://github.com/angular/angular/commit/327744ac171b30e54ee46d20bb13482fd1904f9a) | perf | detect existing signal dependency without checking all producer links |
| [58efd86c78](https://github.com/angular/angular/commit/58efd86c78f244689b1bad5285aed9418a0772d1) | fix | migration skip any target are not build or test |
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.0.2"></a>
# 22.0.2 (2026-06-17)
### common
@@ -427,49 +863,6 @@
<!-- CHANGELOG SPLIT MARKER -->
<a name="22.1.0-next.0"></a>
# 22.1.0-next.0 (2026-06-10)
## Deprecations
### http
- `HttpClient.jsonp`, `HttpClientJsonpModule`, and related JSONP classes/functions are deprecated. Use standard HTTP requests instead.
### common
| Commit | Type | Description |
| -- | -- | -- |
| [1ad6824d0d](https://github.com/angular/angular/commit/1ad6824d0dd17a0515ad0544cf435d1e3af6982e) | fix | skip transfer cache for uncacheable HTTP traffic ([#69017](https://github.com/angular/angular/pull/69017)) |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [25c744c4d0](https://github.com/angular/angular/commit/25c744c4d0abd3bfe863ed2d528fea756ec5801b) | fix | support foreign components defined outside top-level scope |
| [7c60a98b3c](https://github.com/angular/angular/commit/7c60a98b3ccc2090f3bfb3c76d6207f37a5df0ff) | fix | support import aliases in foreignImports ([#68674](https://github.com/angular/angular/pull/68674)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [124ba10ead](https://github.com/angular/angular/commit/124ba10ead58c9f93b0b74c4102022c4674db1f5) | feat | add custom set option to linkedSignal |
| [b3748e9fe4](https://github.com/angular/angular/commit/b3748e9fe4a4f9f56f9843b448db59674ad01452) | fix | correct container anchor collection order to match DOM layout |
| [11b206b919](https://github.com/angular/angular/commit/11b206b919074149f2db6e3640a55284aecfe48a) | fix | introduce disposal mechanism for Angular views in foreign `@content` |
| [0c07356c5c](https://github.com/angular/angular/commit/0c07356c5c9dc76a532d7d4ecf43d8fce81b405b) | fix | set current tnode in foreign component instruction on reuse |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [c0cbd46bd7](https://github.com/angular/angular/commit/c0cbd46bd77443749444f816de1e0df4ede28c96) | fix | skip transfer cache for fetch credentialed requests ([#69017](https://github.com/angular/angular/pull/69017)) |
| [af04e266cc](https://github.com/angular/angular/commit/af04e266cc9df4502802b565ccbb9928f10f51f5) | refactor | deprecate jsonp support |
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.