eference context to demonstrate patterns distinct from legacy examples.
1. Environment Modernization
Angular 22 enforces stricter tooling requirements. Attempting to build with older versions will result in compilation failures.
- Node.js: Upgrade to version 22 or higher.
- TypeScript: Upgrade to version 6.
Implementation:
# Verify Node version
node -v # Must be >= 22.0.0
# Update TypeScript in devDependencies
npm install -D typescript@6
Update your package.json to reflect these constraints:
{
"engines": {
"node": ">=22.0.0"
},
"devDependencies": {
"typescript": "^6.0.0"
}
}
2. Dynamic Component Refactoring
The ComponentFactoryResolver and ComponentFactory classes are removed. Angular 22 allows direct component creation via ViewContainerRef. This simplifies the API and removes the intermediate factory step.
Legacy Pattern (Angular 21):
// Deprecated
constructor(private resolver: ComponentFactoryResolver) {}
loadMetricPanel() {
const factory = this.resolver.resolveComponentFactory(MetricPanelComponent);
this.container.createComponent(factory);
}
Modern Pattern (Angular 22):
// Angular 22
import { ViewContainerRef, ComponentRef } from '@angular/core';
export class DashboardHostComponent {
private container = inject(ViewContainerRef);
private panelRef: ComponentRef<MetricPanelComponent> | null = null;
loadMetricPanel() {
// Direct creation; no factory resolution needed
this.panelRef = this.container.createComponent(MetricPanelComponent);
// Optional: Pass inputs immediately
this.panelRef.setInput('metricId', 'cpu_usage_01');
}
}
Rationale: Removing the factory layer reduces boilerplate and aligns with Angular's signal-based evolution. The setInput method provides a type-safe way to configure dynamic components.
3. Change Detection Strategy Update
The ChangeDetectionStrategy.Default enum value is renamed to Eager to better reflect its behavior. While the functionality remains the same, the rename signals a push toward more explicit performance management.
Implementation:
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'app-live-chart',
template: `...`,
// Renamed from Default to Eager
changeDetection: ChangeDetectionStrategy.Eager
})
export class LiveChartComponent {}
Best Practice: For data-intensive components like charts or tables, migrate to OnPush. This reduces change detection cycles by only checking the component when input references change.
@Component({
changeDetection: ChangeDetectionStrategy.OnPush
})
export class OptimizedTableComponent {
// Ensure inputs are immutable or use signals
@Input() set data(value: Metric[]) {
this._data = value;
this.changeDetectorRef.markForCheck();
}
}
4. Router Configuration and Guards
Angular 22 removes provideRoutes in favor of provideRouter. Additionally, the default parameter inheritance strategy changes to always, and CanMatchFn guards require an updated signature.
Router Provider:
// Angular 22
import { provideRouter } from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter([
{ path: 'metrics', component: MetricsComponent },
{ path: 'settings', component: SettingsComponent }
])
]
};
Parameter Inheritance Strategy:
If your application relies on the previous behavior where child routes only inherited parameters when the parent had no path parameters, you must explicitly configure this.
provideRouter(routes, {
// Restore legacy behavior if needed
paramsInheritanceStrategy: 'emptyOnly'
})
Guard Signature Update:
CanMatchFn now receives the currentSnapshot as the third argument.
import { CanMatchFn, Route, UrlSegment, ActivatedRouteSnapshot } from '@angular/router';
export const authGuard: CanMatchFn = (
route: Route,
segments: UrlSegment[],
currentSnapshot: ActivatedRouteSnapshot // New argument
) => {
const isAuthenticated = checkAuth(currentSnapshot);
return isAuthenticated;
};
5. HTTP Client and Fetch API
Angular 22 switches the internal HTTP engine to the Fetch API. The provideHttpClient() function remains the entry point, but the underlying transport changes.
Implementation:
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [provideHttpClient()]
};
Critical Note on File Uploads: The Fetch API does not support upload.progress events in the same way XHR does. If your application tracks file upload progress, you may need to implement a custom interceptor or use XMLHttpRequest directly for those specific requests until the Angular team provides a Fetch-compatible progress solution.
6. Template Strictness
The template compiler now enforces strict rules for attribute bindings. data-* attributes must use the attr. prefix.
Legacy Pattern:
<!-- Angular 21: This worked but is now invalid -->
<div [data-tracking-id]="metric.id"></div>
Modern Pattern:
<!-- Angular 22: Use attr. prefix -->
<div [attr.data-tracking-id]="metric.id"></div>
Duplicate input bindings are also flagged as errors. Ensure each input is bound only once per element.
Pitfall Guide
-
Node.js Version Mismatch
- Explanation: Angular 22 requires Node 22+. Building with Node 20 or 21 will cause CLI errors or unexpected compilation failures.
- Fix: Use
nvm to switch to Node 22 and verify with node -v before running any Angular commands.
-
Router Parameter Pollution
- Explanation: With
paramsInheritanceStrategy defaulting to always, child routes may receive parent parameters they don't expect. This can cause data services to fetch incorrect data based on stale or irrelevant IDs.
- Fix: Audit nested routes. If the new behavior breaks your logic, explicitly set
paramsInheritanceStrategy: 'emptyOnly' in provideRouter.
-
Fetch Upload Progress Failure
- Explanation: Code relying on
HttpEventType.UploadProgress may stop emitting events because Fetch lacks native upload progress support.
- Fix: For file uploads, consider using
XMLHttpRequest directly or check for community polyfills. Monitor Angular release notes for Fetch progress support updates.
-
data-* Binding Breakage
- Explanation: Templates using
[data-id] will fail compilation or render incorrectly. The compiler no longer treats these as property bindings.
- Fix: Perform a global search for
[data- and replace with [attr.data-.
-
Stale Change Detection Enums
- Explanation: References to
ChangeDetectionStrategy.Default will cause compilation errors due to the rename to Eager.
- Fix: Run a global replace for
ChangeDetectionStrategy.Default to ChangeDetectionStrategy.Eager. Consider migrating to OnPush for performance gains.
-
CanMatchFn Signature Mismatch
- Explanation: Route guards using
CanMatchFn will fail type checking if they do not accept the currentSnapshot parameter.
- Fix: Update all guard functions to include the third argument:
(route, segments, snapshot) => boolean.
-
Lingering ComponentFactoryResolver Imports
- Explanation: Code that imports
ComponentFactoryResolver will fail to compile as the class is removed.
- Fix: Remove all imports of
ComponentFactoryResolver and ComponentFactory. Refactor dynamic component creation to use viewContainerRef.createComponent().
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Legacy Nested Routes | paramsInheritanceStrategy: 'emptyOnly' | Prevents unexpected parameter leakage in complex route trees. | Low; config change only. |
| New Micro-frontend | paramsInheritanceStrategy: 'always' | Simplifies parameter passing; aligns with modern routing patterns. | None; default behavior. |
| High-Frequency Data UI | ChangeDetectionStrategy.OnPush | Reduces change detection cycles; improves rendering performance. | Medium; requires immutable data patterns. |
| Simple Static UI | ChangeDetectionStrategy.Eager | Minimal refactoring; maintains existing behavior with renamed enum. | Low; rename only. |
| File Upload Critical | Custom XHR Interceptor | Fetch API lacks progress events; XHR ensures reliability. | High; requires custom implementation. |
Configuration Template
Use this template for app.config.ts to ensure correct provider setup in Angular 22.
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { provideHttpClient, withFetch } from '@angular/common/http';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes, withComponentInputBinding()),
// Fetch is default in Angular 22, but explicit declaration is clear
provideHttpClient(withFetch())
]
};
Quick Start Guide
- Upgrade Node: Run
nvm install 22 and nvm use 22.
- Update TypeScript: Execute
npm install -D typescript@6.
- Run Migration: Execute
ng update @angular/core@22 @angular/cli@22.
- Fix Templates: Run
ng build and resolve any data-* or duplicate binding errors.
- Verify Runtime: Test router navigation and HTTP requests to ensure behavioral changes are handled.