Upgrading @angular/router 14 to 15
Breaking14.3.0 → 15.2.10Breaking14Security3Platform5Deprecated12New36Changed8Fixed128Other6
BreakingUpgrade verdict
14 breaking changes and 5 platform requirement changes and 3 security fixes.
In plain terms
- Animation keyframe names inside components get a component-specific prefix, so any code that looked them up by their old names will break.
- The `enableIvy` compiler option is gone because Ivy is now the only rendering engine.
- Localization tooling drops the `canParse` method in favor of `analyze`, and the `hint` parameter of `parse` is now required.
- Routed components are created later, when change detection runs, which can affect tests or code relying on exact timing.
Must act Top 5 of 31 that need code or config changes
Node.js requirement changed in 15.0.0-next.6: ^14.15.0 || >=16.10.0 → ^14.20.0 || ^16.13.0 || >=18.10.0.
What you must change
Breaking14
Keyframes names are now prefixed with the component's scope name, so code relying on the original names no longer matches.
Removes the Angular compiler option
enableIvy since Ivy is the only rendering engine.enableIvyRemoves
canParse from all translation parsers in @angular/localize/tools; use analyze instead, and hint in parse is now mandatory.canParseanalyzeparsehint@angular/localize/toolsRouterOutlet no longer instantiates the activated component immediately during navigation; it waits until change detection runs.
RouterOutletrelativeLinkResolution is no longer configurable in the Router.relativeLinkResolutionRenames rawSrc to ngSrc in the NgOptimizedImage directive.
NgOptimizedImagerawSrcngSrcRemoves the enableIvy options.
enableIvyRemoves the deprecated
relativeLinkResolution option.relativeLinkResolutionInvalid constructors for DI may now report compilation errors when the inherited constructor cannot be used for DI.
@Injectable()@Directive()Iframe usages with security-sensitive attributes applied via attribute or property binding may need updates for stricter iframe binding rules.
Iframe usages with
src or srcdoc preceding other attributes may need updates for stricter iframe binding rules.srcsrcdocsetDisabledState is always called when a ControlValueAccessor is attached; opt out via FormsModule.withConfig or ReactiveFormsModule.withConfig.
setDisabledStateControlValueAccessorFormsModule.withConfigReactiveFormsModule.withConfigThe title property is now required on ActivatedRouteSnapshot.
ActivatedRouteSnapshottitleMerges
RouterLinkWithHref into RouterLink.RouterLinkWithHrefRouterLinkSecurity3
Changes URL sanitization to only block javascript: URLs.
Hardens attribute and property binding rules for
<iframe> elements.Hardens rules related to the attribute order on iframe elements.
Platform5
Adds support for TypeScript 4.9.
Drops Node.js 14.[15-19].x and 16.[10-12].x support; supported versions are 14.20.x, 16.13.x and 18.10.x.
TypeScript versions older than 4.8 are no longer supported.
Adds support for Node.js version 18.
Drops support for TypeScript 4.6 and 4.7.
Deprecations to plan for
Deprecated9
Deprecates class and InjectionToken guards and resolvers in favor of plain JavaScript functions using inject from @angular/core.
InjectionTokeninjectDeprecates CanLoad guards in the Router in favor of CanMatch.
CanLoadCanMatchcanLoadcanMatchDeprecates router writable properties in favor of DI providers and router options.
routeReuseStrategytitleStrategyurlHandlingStrategyonSameUrlNavigationparamsInheritanceStrategyurlUpdateStrategycanceledNavigationResolutionmalformedUriErrorHandlerDeprecates public members of Router that are meant to be configured elsewhere.
RouterDeprecates the
DATE_PIPE_DEFAULT_TIMEZONE token in favor of DATE_PIPE_DEFAULT_OPTIONS with a timezone field.DATE_PIPE_DEFAULT_TIMEZONEDATE_PIPE_DEFAULT_OPTIONStimezoneDeprecates passing an NgModule to
providedIn for @Injectable and InjectionToken; use providedIn: 'root' or NgModule.providers.providedIn@InjectableInjectionTokenNgModule.providersDeprecates the bit field signature of
Injector.get() in favor of the new options object.Injector.get()TestBed.inject()Deprecates the
RouterLinkWithHref directive; use the RouterLink directive instead.RouterLinkWithHrefRouterLinkDeprecates
providedIn: NgModule and providedIn: 'any' for injectable providers.providedInAPI changes
Generated by DepDelta from published .d.ts files. Not mixed with prose notes.
- removed:
constructor RouterOutlet(parentContexts: ChildrenOutletContexts, location: ViewContainerRef, name: string, changeDetector: ChangeDetectorRef, environmentInjector: EnvironmentInjector) - removed:
function ɵassignExtraOptionsToRouter(opts: ExtraOptions, router: Router): void - removed:
property ExtraOptions.relativeLinkResolution?: 'legacy' | 'corrected' - removed:
property Router.relativeLinkResolution: 'legacy' | 'corrected' - removed:
property RouterLinkActive.linksWithHrefs: QueryList<RouterLinkWithHref> - removed:
readonly property ActivatedRouteSnapshot.title?: string - removed:
readonly property Router.events: Observable<Event_2> - removed:
readonly property RouterLinkActive.isActive: boolean - changed:
class RouterLink implements OnChanges → class RouterLink implements OnChanges, OnDestroy - changed:
constructor Router(rootComponentType: Type<any> | null, urlSerializer: UrlSerializer, rootContexts: ChildrenOutletContexts, location: Location_2, injector: Injector, compiler: Compiler, config: Routes) → constructor Router() - changed:
constructor RouterLink(router: Router, route: ActivatedRoute, tabIndexAttribute: string | null | undefined, renderer: Renderer2, el: ElementRef) → constructor RouterLink(router: Router, route: ActivatedRoute, tabIndexAttribute: string | null | undefined, renderer: Renderer2, el: ElementRef, locationStrategy?: LocationStrategy | undefined) - changed:
constructor RouterLinkActive(router: Router, element: ElementRef, renderer: Renderer2, cdr: ChangeDetectorRef, link?: RouterLink | undefined, linkWithHref?: RouterLinkWithHref | undefined) → constructor RouterLinkActive(router: Router, element: ElementRef, renderer: Renderer2, cdr: ChangeDetectorRef, link?: RouterLink | undefined) - changed:
constructor RouterLinkWithHref(router: Router, route: ActivatedRoute, locationStrategy: LocationStrategy) → constructor RouterLinkWithHref(router: Router, route: ActivatedRoute, tabIndexAttribute: string | null | undefined, renderer: Renderer2, el: ElementRef, locationStrategy?: LocationStrategy | undefined) - changed:
function provideRouter(routes: Routes, ...features: RouterFeatures[]): Provider[] → function provideRouter(routes: Routes, ...features: RouterFeatures[]): EnvironmentProviders - changed:
method CanDeactivate.canDeactivate(component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree → method CanDeactivate.canDeactivate(component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree - changed:
method RouterLink.onClick(): boolean → method RouterLink.onClick(button: number, ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean): boolean - changed:
method RouterLinkWithHref.ngOnChanges(changes: SimpleChanges): any → method RouterLinkWithHref.ngOnChanges(changes: SimpleChanges): void - changed:
property ExtraOptions.errorHandler?: ErrorHandler → property ExtraOptions.errorHandler?: (error: any) => any - changed:
property Route.canActivate?: Array<CanActivateFn | any> → property Route.canActivate?: Array<CanActivateFn | DeprecatedGuard> - changed:
property Route.canActivateChild?: Array<CanActivateChildFn | any> → property Route.canActivateChild?: Array<CanActivateChildFn | DeprecatedGuard> - changed:
property Route.canDeactivate?: Array<CanDeactivateFn<any> | any> → property Route.canDeactivate?: Array<CanDeactivateFn<any> | DeprecatedGuard> - changed:
property Route.canLoad?: Array<CanLoadFn | any> → property Route.canLoad?: Array<CanLoadFn | DeprecatedGuard> - changed:
property Route.canMatch?: Array<Type<CanMatch> | InjectionToken<CanMatchFn> | CanMatchFn> → property Route.canMatch?: Array<CanMatchFn | DeprecatedGuard> - changed:
property Route.loadComponent?: () => Type<unknown> | Observable<Type<unknown>> | Promise<Type<unknown>> → property Route.loadComponent?: () => Type<unknown> | Observable<Type<unknown> | DefaultExport<Type<unknown>>> | Promise<Type<unknown> | DefaultExport<Type<unknown>>> - changed:
property Route.providers?: Array<Provider | ImportedNgModuleProviders> → property Route.providers?: Array<Provider | EnvironmentProviders> - changed:
property Router.errorHandler: ErrorHandler → property Router.errorHandler: (error: any) => any - changed:
property Router.onSameUrlNavigation: 'reload' | 'ignore' → property Router.onSameUrlNavigation: OnSameUrlNavigation - changed:
property RouterConfigOptions.onSameUrlNavigation?: 'reload' | 'ignore' → property RouterConfigOptions.onSameUrlNavigation?: OnSameUrlNavigation - changed:
property RouterLinkWithHref.target: string → property RouterLinkWithHref.target?: string - changed:
static property RouterLink.ɵdir: i0.ɵɵDirectiveDeclaration<RouterLink, ':not(a):not(area)[routerLink]', never, { 'queryParams': 'queryParams'; 'fragment': 'fragment'; 'queryParamsHandling': 'queryParamsHandling'; 'state': 'state'; 'relativeTo': 'relativeTo'; 'preserveFragment': 'preserveFragment'; 'skipLocationChange': 'skipLocationChange'; 'replaceUrl': 'replaceUrl'; 'routerLink': 'routerLink' }… → static property RouterLink.ɵdir: i0.ɵɵDirectiveDeclaration<RouterLink, '[routerLink]', never, { 'target': 'target'; 'queryParams': 'queryParams'; 'fragment': 'fragment'; 'queryParamsHandling': 'queryParamsHandling'; 'state': 'state'; 'relativeTo': 'relativeTo'; 'preserveFragment': 'preserveFragment'; 'skipLocationChange': 'skipLocationChange'; 'replaceUrl': 'replaceUrl'; 'routerLink': 'routerLink… - changed:
static property RouterLink.ɵfac: i0.ɵɵFactoryDeclaration<RouterLink, [null, null, { attribute: 'tabindex' }, null, null]> → static property RouterLink.ɵfac: i0.ɵɵFactoryDeclaration<RouterLink, [null, null, { attribute: 'tabindex' }, null, null, null]> - changed:
static property RouterLinkActive.ɵdir: i0.ɵɵDirectiveDeclaration<RouterLinkActive, '[routerLinkActive]', ['routerLinkActive'], { 'routerLinkActiveOptions': 'routerLinkActiveOptions'; 'ariaCurrentWhenActive': 'ariaCurrentWhenActive'; 'routerLinkActive': 'routerLinkActive' }, { 'isActiveChange': 'isActiveChange' }, ['links', 'linksWithHrefs'], never, true> → static property RouterLinkActive.ɵdir: i0.ɵɵDirectiveDeclaration<RouterLinkActive, '[routerLinkActive]', ['routerLinkActive'], { 'routerLinkActiveOptions': 'routerLinkActiveOptions'; 'ariaCurrentWhenActive': 'ariaCurrentWhenActive'; 'routerLinkActive': 'routerLinkActive' }, { 'isActiveChange': 'isActiveChange' }, ['links'], never, true, never> - changed:
static property RouterLinkActive.ɵfac: i0.ɵɵFactoryDeclaration<RouterLinkActive, [null, null, null, null, { optional: true }, { optional: true }]> → static property RouterLinkActive.ɵfac: i0.ɵɵFactoryDeclaration<RouterLinkActive, [null, null, null, null, { optional: true }]> - changed:
static property RouterLinkWithHref.ɵdir: i0.ɵɵDirectiveDeclaration<RouterLinkWithHref, 'a[routerLink],area[routerLink]', never, { 'target': 'target'; 'queryParams': 'queryParams'; 'fragment': 'fragment'; 'queryParamsHandling': 'queryParamsHandling'; 'state': 'state'; 'relativeTo': 'relativeTo'; 'preserveFragment': 'preserveFragment'; 'skipLocationChange': 'skipLocationChange'; 'replaceUrl': 'repl… → static property RouterLinkWithHref.ɵdir: i0.ɵɵDirectiveDeclaration<RouterLink, '[routerLink]', never, { 'target': 'target'; 'queryParams': 'queryParams'; 'fragment': 'fragment'; 'queryParamsHandling': 'queryParamsHandling'; 'state': 'state'; 'relativeTo': 'relativeTo'; 'preserveFragment': 'preserveFragment'; 'skipLocationChange': 'skipLocationChange'; 'replaceUrl': 'replaceUrl'; 'routerLink': 'ro… - changed:
static property RouterLinkWithHref.ɵfac: i0.ɵɵFactoryDeclaration<RouterLinkWithHref, never> → static property RouterLinkWithHref.ɵfac: i0.ɵɵFactoryDeclaration<RouterLink, [null, null, { attribute: 'tabindex' }, null, null, null]> - changed:
static property RouterModule.ɵmod: i0.ɵɵNgModuleDeclaration<RouterModule, never, [typeof i1.RouterOutlet, typeof i2.RouterLink, typeof i2.RouterLinkWithHref, typeof i3.RouterLinkActive, typeof i4.ɵEmptyOutletComponent], [typeof i1.RouterOutlet, typeof i2.RouterLink, typeof i2.RouterLinkWithHref, typeof i3.RouterLinkActive, typeof i4.ɵEmptyOutletComponent]> → static property RouterModule.ɵmod: i0.ɵɵNgModuleDeclaration<RouterModule, never, [typeof i1.RouterOutlet, typeof i2.RouterLink, typeof i3.RouterLinkActive, typeof i4.ɵEmptyOutletComponent], [typeof i1.RouterOutlet, typeof i2.RouterLink, typeof i3.RouterLinkActive, typeof i4.ɵEmptyOutletComponent]> - changed:
static property RouterOutlet.ɵdir: i0.ɵɵDirectiveDeclaration<RouterOutlet, 'router-outlet', ['outlet'], {}, { 'activateEvents': 'activate'; 'deactivateEvents': 'deactivate'; 'attachEvents': 'attach'; 'detachEvents': 'detach' }, never, never, true> → static property RouterOutlet.ɵdir: i0.ɵɵDirectiveDeclaration<RouterOutlet, 'router-outlet', ['outlet'], { 'name': 'name' }, { 'activateEvents': 'activate'; 'deactivateEvents': 'deactivate'; 'attachEvents': 'attach'; 'detachEvents': 'detach' }, never, never, true, never> - changed:
static property RouterOutlet.ɵfac: i0.ɵɵFactoryDeclaration<RouterOutlet, [null, null, { attribute: 'name' }, null, null]> → static property RouterOutlet.ɵfac: i0.ɵɵFactoryDeclaration<RouterOutlet, never> - changed:
static property ɵEmptyOutletComponent.ɵcmp: i0.ɵɵComponentDeclaration<ɵEmptyOutletComponent, 'ng-component', never, {}, {}, never, never, true> → static property ɵEmptyOutletComponent.ɵcmp: i0.ɵɵComponentDeclaration<ɵEmptyOutletComponent, 'ng-component', never, {}, {}, never, never, true, never> - changed:
type CanDeactivateFn<T> = (component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot) => Observable<boolean | UrlTree> | Pro… → type CanDeactivateFn<T> = (component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState: RouterStateSnapshot) => Observable<boolean | UrlTree> | Prom… - changed:
type LoadChildrenCallback = () => Type<any> | NgModuleFactory<any> | Routes | Observable<Type<any> | Routes> | Promise<NgModuleFactory<any> | Type<any> | Routes> → type LoadChildrenCallback = () => Type<any> | NgModuleFactory<any> | Routes | Observable<Type<any> | Routes | DefaultExport<Type<any>> | DefaultExport<Routes>> | Promise<NgModuleFactory<a… - changed:
type ResolveData = { [key: string | symbol]: any | ResolveFn<unknown> } → type ResolveData = { [key: string | symbol]: ResolveFn<unknown> | DeprecatedGuard } - changed:
type RouterFeatures = PreloadingFeature | DebugTracingFeature | InitialNavigationFeature | InMemoryScrollingFeature | RouterConfigurationFeature → type RouterFeatures = PreloadingFeature | DebugTracingFeature | InitialNavigationFeature | InMemoryScrollingFeature | RouterConfigurationFeature | NavigationErrorHandlerFeature - added:
class NavigationSkipped extends RouterEvent - added:
const enum NavigationSkippedCode - added:
constructor NavigationSkipped(id: number, url: string, reason: string, code?: NavigationSkippedCode | undefined) - added:
constructor UrlTree(root?: UrlSegmentGroup, queryParams?: Params, fragment?: string | null) - added:
function withHashLocation(): RouterConfigurationFeature - added:
function withNavigationErrorHandler(fn: (error: NavigationError) => void): NavigationErrorHandlerFeature - added:
function ɵafterNextNavigation(router: Router, action: () => void): void - added:
get ActivatedRouteSnapshot.title(): string | undefined - added:
get Router.events(): Observable<Event_2> - added:
get RouterLinkActive.isActive(): boolean - added:
interface DefaultExport<T> - added:
member EventType.NavigationSkipped = 16 - added:
member NavigationSkippedCode.IgnoredByUrlHandlingStrategy = 1 - added:
member NavigationSkippedCode.IgnoredSameUrlNavigation = 0 - added:
method RouterLink.ngOnDestroy(): any - added:
method RouterOutlet.ngOnChanges(changes: SimpleChanges): void - added:
property DefaultExport.default: T - added:
property NavigationBehaviorOptions.onSameUrlNavigation?: Extract<OnSameUrlNavigation, 'reload'> - added:
property NavigationSkipped.reason: string - added:
property RouterLink.href: string | null - added:
property RouterLink.target?: string - added:
property RouterOutlet.name: string - added:
readonly property NavigationSkipped.code?: NavigationSkippedCode | undefined - added:
readonly property NavigationSkipped.type = EventType.NavigationSkipped - added:
static property RouteReuseStrategy.ɵfac: i0.ɵɵFactoryDeclaration<RouteReuseStrategy, never> - added:
static property RouteReuseStrategy.ɵprov: i0.ɵɵInjectableDeclaration<RouteReuseStrategy> - added:
static property UrlHandlingStrategy.ɵfac: i0.ɵɵFactoryDeclaration<UrlHandlingStrategy, never> - added:
static property UrlHandlingStrategy.ɵprov: i0.ɵɵInjectableDeclaration<UrlHandlingStrategy> - added:
type DeprecatedGuard = ProviderToken<any> | any - added:
type NavigationErrorHandlerFeature = RouterFeature<RouterFeatureKind.NavigationErrorHandlerFeature> - added:
type OnSameUrlNavigation = 'reload' | 'ignore' - added:
type RouterHashLocationFeature = RouterFeature<RouterFeatureKind.RouterHashLocationFeature>
- removed:
function setupTestingRouterInternal(urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location_2, compiler: Compiler, injector: Injector, routes: Route[][], titleStrategy: TitleStrategy, opts?: ExtraOptions | UrlHandlingStrategy, urlHandlingStrategy?: UrlHandlingStrategy, routeReuseStrategy?: RouteReuseStrategy): Router - added:
class RouterTestingHarness - added:
get RouterTestingHarness.routeDebugElement(): DebugElement | null - added:
get RouterTestingHarness.routeNativeElement(): HTMLElement | null - added:
method RouterTestingHarness.detectChanges(): void - added:
method RouterTestingHarness.navigateByUrl(url: string): Promise<null | {}> - added:
method RouterTestingHarness.navigateByUrl<T>(url: string, requiredRoutedComponentType: Type<T>): Promise<T> - added:
static method RouterTestingHarness.create(initialUrl?: string): Promise<RouterTestingHarness>