Commit Graph

38229 Commits

Author SHA1 Message Date
Kam 593f2d7e2e refactor(docs-infra): share the angular.dev origin constant
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`.
2026-08-21 11:50:02 -07:00
Kristiyan Kostadinov f3c093df24 refactor(compiler-cli): add compiler option for enabling source locations
Adds an internal config options that allows us to enable source locations.
2026-08-21 11:45:30 -07:00
root 2ab5ff56de fix(core): preserve namespace for dynamic component hosts
Forward the insertion namespace when creating dynamic component hosts inside SVG or MathML.
2026-08-21 11:41:59 -07:00
Andrew Scott fa2aca969f refactor(router): add support for blocking router resources
Extends router resource integration to support blocking resources during navigation transitions.
2026-08-21 11:38:19 -07:00
Andrew Scott ad3c16562b docs: add guidelines for Object.create(null) PRs to PR review skill 2026-08-21 11:36:46 -07:00
Kristiyan Kostadinov 7ba082e08e fix(core): avoid prototype member collisions
Switches to using an object with a `null` prototype to avoid collisions.
2026-08-21 11:32:44 -07:00
Kristiyan Kostadinov 862a0c8ab3 fix(common): avoid prototype member collisions
Switches to using an object with a `null` prototype to avoid collisions.
2026-08-21 11:32:44 -07:00
Andrew Scott 779f67777d refactor(core): widen ɵɵclassProp value parameter type to any
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.
2026-08-21 11:26:00 -07:00
Konstantin S. 045746c18c docs(core): clarify toObservable synchronization behavior 2026-08-21 11:22:53 -07:00
Alex Rickabaugh 2e2c426e76 fix(compiler-cli): deduplicate deferred imports across multiple blocks
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.
2026-08-21 11:14:47 -07:00
Alex Rickabaugh 7d9f55da11 feat(compiler-cli): scope type-checking of keyed defer blocks
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.
2026-08-21 11:14:47 -07:00
Alex Rickabaugh b06ff75370 refactor(compiler): support block-specific deferredImports mapping
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.
2026-08-21 11:14:47 -07:00
Doug Parker d609cf6e09 release: bump Angular DevTools version to 1.20.0 2026-08-19 13:22:03 -07:00
Jessica Janiuk 0e8a225f59 release: cut the v22.2.0-next.3 release v22.2.0-next.3 2026-08-19 13:18:44 -07:00
Jessica Janiuk 054bc6bc5c docs: release notes for the v22.1.3 release 2026-08-19 13:12:07 -07:00
Jessica Janiuk b7079c590c docs: release notes for the v20.3.29 release 2026-08-19 13:00:49 -07:00
Jessica Janiuk 1dca0588df docs: release notes for the v21.2.21 release 2026-08-19 12:54:15 -07:00
Doug Parker e041f9483e refactor(core): pass AbortSignal to WebMCP tools to handle cancellation
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
2026-08-19 12:41:09 -07:00
Angular Robot 1fb3b58abe build: update cross-repo angular dependencies to v22.2.0-next.2
See associated pull request for more information.
2026-08-19 12:38:52 -07:00
SkyZeroZx 7e39b4aedb docs: update signal forms skill to match the current implementation 2026-08-19 11:07:46 -07:00
Andrew Scott 80a04eba52 refactor(router): add support for non-blocking router resources (#70211)
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
2026-08-19 15:28:08 +00:00
Andrew Scott 99c0f593c4 refactor(router): rename activated route injector feature to resource feature (#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
2026-08-19 15:28:08 +00:00
Kam f35a3961c4 fix(docs-infra): remove stray tokens from adev illustrations
`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.
2026-08-19 08:26:30 -07:00
hawkgs 023a63df8c refactor(devtools): unsubs from profiler when the perf track is disabled
Avoid the leak by unsubscribing from the profiler when the performance track is disabled.
Additionally, drop the duplicated performance track state flag.
2026-08-19 08:26:01 -07:00
Matthieu Riegler c819880b91 fix(forms): report forbidden 2way bindings on when FormField is applied
We were already reporting regular bindings but not 2way.

fixes #70219
2026-08-18 17:08:02 -07:00
Jessica Janiuk da9f3e2118 Revert "fix(compiler): preserve &ngsp; between sibling control flow blocks"
This reverts commit 53fc371142.
2026-08-18 17:03:06 -07:00
arturovt 53fc371142 fix(compiler): preserve &ngsp; between sibling control flow blocks
`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
2026-08-18 15:52:42 -07:00
arturovt c2b14b7ab4 fix(core): prevent orphaned requestIdleCallback handle from re-entrant scheduling
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.
2026-08-18 15:52:07 -07:00
Aleksander Bodurri 0ddbc47e7f fix(core): expose debuggableFn for non-computed signal graph nodes
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.
2026-08-18 15:51:26 -07:00
Sam Severance bb1274d6ac fix(docs-infra): refactor unit test
Replace two toContain assertions with a single toMatch
using a multiline regex, per reviewer suggestion.
2026-08-18 15:45:30 -07:00
Sam Severance 7febf40e0d fix(docs-infra): fix code block deindentation in docs-code and docs-code-block
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.
2026-08-18 15:45:30 -07:00
arturovt f1a4c85212 fix(router): pass correct component to canDeactivate for named outlets in componentless parent routes
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
2026-08-18 15:04:56 -07:00
brysonbw 2720362818 feat(router): add containsTree as public API
Export containsTree from @angular/router to enable direct UrlTree subset matching.
2026-08-18 12:08:17 -07:00
Matthieu Riegler d364c83c44 ci: exclude markdown files from requiring dev-infra review
613c51e wasn't enough to exclude dev-infra
2026-08-18 09:38:45 -07:00
Kristiyan Kostadinov 732e505018 fix(core): replace all hasOwnProperty usages with Object.hasOwn
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.
2026-08-18 09:17:18 -07:00
Angular Robot 48a0fd6e8a feat(compiler): allow template to access private props
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.
2026-08-18 09:01:25 -07:00
arturovt c658d73210 fix(core): stop running further effects once one destroys the view mid-flush
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.
2026-08-18 08:59:13 -07:00
aminesbdev 560d3f0412 docs: update naming-conventions skill 2026-08-18 08:58:44 -07:00
Alon Mishne 292991e2df Revert "fix(router): limit protocol-relative URL handling to serialization"
This reverts commit 435f8b2b8b.
2026-08-18 08:58:13 -07:00
Angular Robot 48831fc5fe build: lock file maintenance
See associated pull request for more information.
2026-08-18 08:54:49 -07:00
Matthieu Riegler 613c51e324 ci: add local skills to the fw-general scope
They were caught by dev-infra which isn't the best scope for the imho
2026-08-18 08:54:16 -07:00
arturovt 46d2cb7ff0 fix(common): preserve literal key union in KeyValuePipe.transform()
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.
2026-08-17 15:01:24 -07:00
Jaime Burgos 6f9a6bea50 fix(platform-browser): disallow event handler attributes in Meta
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.
2026-08-17 14:59:50 -07:00
Shayan 3f8d9d6ea6 fix(language-server): recover project for external templates in solution-style workspaces
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
2026-08-17 14:59:02 -07:00
Nikita Barsukov 182c371d82 docs: use transformedValue in Forms | Custom Controls | Value transformation section
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>
2026-08-17 14:58:14 -07:00
splincode d35c17d393 refactor: correct typos in comments, docs, and error messages
Fix misspellings found across multiple packages:

- `paramters` → `parameters` (utils.spec.ts)
- `directve` → `directive` (typecheck/context.ts)
- `subscriper` → `subscriber` (zone.js rxjs test)
- `swich` → `switch` (adev animation parser test)
- `subscribtion` → `subscription` (forms/abstract_model.ts)
- `lifecyle` → `lifecycle` (ng-devtools-backend hooks)
- `compatability` → `compatibility` (tree-visualizer.ts)
- `indentifier(s)` → `identifier(s)` (compiler-cli shared.ts, i18n_helpers.ts, declaration_only_emission_spec.ts)
- `identifer` → `identifier` (platform-browser shared_styles_host.ts)
- `prcess` → `process` (standalone-migration to-standalone.ts)
2026-08-17 14:57:04 -07:00
splincode b10021c79b refactor(localize): replace any with unknown in messages and translations utils
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
2026-08-17 14:56:31 -07:00
arturovt 38d093232c fix(forms): warn in dev mode when ngModel cannot reach parent NgForm across component boundary
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
2026-08-17 13:59:40 -07:00
Vincent 70b252cac3 test(core): Add test to ensure Angular correctly detects paths when served from file system
This test makes sure that when Angular is served from Electron or just from the file system that the router works correctly.
2026-08-17 13:58:54 -07:00
Jaime Burgos 45ebb127e3 refactor(compiler-cli): add error guide links to diagnostics
Add the error guide URL when a compiler diagnostic uses a negative
marked error code.
2026-08-17 13:58:11 -07:00