Use async/await in animation acceptance tests and share the animation frame helper. Remove the obsolete fakeAsync frame utility and its Bazel dependency.
(cherry picked from commit c60c41e29a)
Restore ChangeDetectionStrategy.OnPush that was removed in a previous commit, causing tests to fail due to ExpressionChangedAfterItHasBeenCheckedError.
(cherry picked from commit ae33a5f55e)
OnPush is now the default change detection strategy, so the explicit test and example configuration is no longer needed.
(cherry picked from commit 9e5ae4b710)
Safely return null instead of throwing an unhandled TypeError when getDirectiveMetadata is called with a falsy directive or component instance.
(cherry picked from commit cea6896a0f)
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.
(cherry picked from commit 1bc7e3c2c3)
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.
(cherry picked from commit 49a797f510)
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
(cherry picked from commit 70756b506c)
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
(cherry picked from commit 58d536bc83)
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
(cherry picked from commit 7168bed663)
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.
(cherry picked from commit af26a8c521)
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.
(cherry picked from commit 8975b4346d)
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
(cherry picked from commit f72600eadd)
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.
(cherry picked from commit 0904f90b13)
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.
(cherry picked from commit 38861ac41b)
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.
(cherry picked from commit 2a4903fec1)
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.
(cherry picked from commit 34817da735)
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.
(cherry picked from commit 66d505e287)
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`.
(cherry picked from commit 915a03ae85)
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.
(cherry picked from commit 83f7695b2e)
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
(cherry picked from commit 168a324cce)
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.
(cherry picked from commit 0d7ae16350)
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
(cherry picked from commit bdc09e8183)
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.
(cherry picked from commit 779f67777d)
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
(cherry picked from commit e041f9483e)
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
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.
(cherry picked from commit c2b14b7ab4)
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.
(cherry picked from commit 0ddbc47e7f)
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.
(cherry picked from commit 732e505018)
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.
(cherry picked from commit c658d73210)
This test makes sure that when Angular is served from Electron or just from the file system that the router works correctly.
(cherry picked from commit 70b252cac3)
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.
(cherry picked from commit c6e4a36be1)
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.
(cherry picked from commit 1cb3d606bf)
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.
(cherry picked from commit dc65e3656f)
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.
(cherry picked from commit eee9ef4d09)
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).
(cherry picked from commit 4560f4fdcd)