This change adds a new that allows environments that cannot support inline TCBs (such as the language service or source-to-source transforms where TS compilation and emit are downstream) to still perform template type checking.
Instead of inlining the TCB into the original source file when non-exported symbols are referenced, we now copy the file content to the .ngtypecheck.ts shim file and generate the external TCB there, if requested by the inlining mode. This preserves the local scope of the original file while keeping the original file unmodified.
External links in the update guide opened inconsistently. Override
marked's link renderer to add `target="_blank" rel="noopener noreferrer"`
to external anchors and apply the `external-link-with-icon` mixin for
the icon. Convert raw HTML and bare URLs in recommendations.ts to
markdown so they all flow through the renderer.
The GHSA-x288-3778-4hhx patch requires `allowedHosts` on
`CommonEngine` or SSR silently falls back to CSR. Add a checklist
item to the v21 update guide.
all necessary info is already available in the tcb meta objects. environments without full ts program no longer need a reflectionhost for tcb generation
- `CombinedControl.value` and `InteropNgControl.value` getter now return
`unknown` instead of `any`, matching the actual `ReadonlyFieldState<unknown>`
return type of `controlValue()`.
- Remove redundant `as any` cast in `cvaControlCreate`: `parent` is typed as
`FormField<unknown>`, so `state().controlValue` is `WritableSignal<unknown>`
and accepts `unknown` directly.
becomes input + linkedSignal
When a component has both a model() property and a conflicting output property (e.g., foo model + fooChange output), this migration converts the model() to an input() + linkedSignal() pattern to avoid naming conflicts.
Fixes#67340
Focus the target element using `focus({preventScroll: true})` after scrolling, so the browser doesn’t adjust the scroll position when applying focus.
Fixes#65938
Add the NG01902 (Orphan field in signal forms) documentation page
to the Error Encyclopedia and change the ORPHAN_FIELD_PROPERTY
error code to -1902 so Angular's RuntimeError automatically appends
a link to angular.dev/errors/NG01902 in the thrown error message.
AOT was generating an array that was ordered as signal queries first, then the decorator queries.
Aligning JIT with AOT fixes the issue illustrated by the test.
fixes#68404
The example already uses the signal-based input() but still declares
items with the @ContentChildren decorator. Convert to the signal-based
contentChildren() query for consistency.
The commit introduces a new function to assist users who want to lazy load services and use the DI system to create them.
Example:
```ts
import {injectAsync} from 'angular/core';
class MyCmp {
someSvc = injectAsync(() => import('..'));
async onClick() {
(await this.someSvc()).handleClick();
}
}
```
Removes the `checkTwoWayBoundEvents` flag since the code it generates is quite breaking and we never got the chance to enable it. Also it caused our tests to misrepresent how the compiler behaves for actual users.
Currently, the exec() utility uses childProcess.spawn() with shell: true. This commit changes the spawn option to shell: false to prevent OS command injection vulnerabilities and quotes the benchmark target in the github action.
Two issues caused browser test failures after the event replay fix:
1. `markEventHandledForElement` used the event object as a WeakMap key, but
`DebugElement.triggerEventHandler` can pass null or primitive values as the
event argument. Added an early return for non-object values.
2. Registering a separate `domListener` closure with `renderer.listen` instead of
`wrappedListener` caused `DebugElement.triggerEventHandler` to invoke the
handler twice: once via `this.listeners` (which holds `wrappedListener`) and
once via Zone.js's `eventListeners` (which holds the unwrapped `domListener`).
The existing dedup logic in `triggerEventHandler` checks if the unwrapped
Zone.js listener is already in `invokedListeners`, but with two different
function objects that check always fails.
Replaced the `domListener` wrapper with a property (`__ngNativeEl__`) stored
directly on `wrappedListener`. `wrapListenerIn_markDirtyAndPreventDefault` reads
this property and calls `markEventHandledForElement` when the listener fires,
while `renderer.listen` receives the same `wrappedListener` function that
Angular stores in `lCleanup`, preserving the dedup invariant.
When `withEventReplay()` is enabled and a component hydrates before the
application becomes stable (e.g. while a pending HTTP request is in
flight), a user interaction on the hydrated element triggers both the
real DOM listener registered by Angular and the jsaction replay path.
This causes the event handler to be invoked twice.
The root cause is that `listenToDomEvent` registers the same
`wrappedListener` both as a stashed jsaction handler (via
`stashEventListenerImpl`) and as a native DOM listener (via
`renderer.listen`). When the user interacts after hydration but before
app stability, jsaction queues the event because no dispatcher is
registered yet. Once the app stabilises and `initEventReplay` runs,
jsaction replays the queued event through `invokeListeners`, which
calls the stashed handler a second time.
The fix tracks dispatched `(event, element)` pairs in a
`WeakMap<Event, WeakSet<Element>>`. The native DOM listener wrapper
records each pair via `markEventHandledForElement`, and `invokeListeners`
skips replay for any pair already present. Keying by element (rather
than event alone) preserves incremental hydration behaviour, where
jsaction legitimately replays the same event on a different element
(the deferred block content) from the one that originally triggered
hydration.
Fixes#67328
In the context of AOT tests, component with defer blocks no longer throw on instanciation if the component is not overridden (with `overrideComponent`)
Prior to this change, all components with a `@defer` block would throw if `compileComponents` was not invoked.
In none-JIT apps, this change makes `compileComponents()` uneccesary.