diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index 718453baa71..6e4c47aad3e 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -117,6 +117,7 @@ v15 - v18 | `@angular/router` | [`RouterLinkWithHref` directive](#router) | v15 | v17 | | `@angular/router` | [Router writeable properties](#router-writable-properties) | v15.1 | v17 | | `@angular/router` | [Router CanLoad guards](#router-can-load) | v15.1 | v17 | +| `@angular/router` | [class and `InjectionToken` guards and resolvers](#router) | v15.2 | v17 | ### Deprecated features with no planned removal version @@ -203,6 +204,8 @@ In the [API reference section](api) of this site, deprecated APIs are indicated | [`RouterLinkWithHref` directive](api/router/RouterLinkWithHref) | Use `RouterLink` instead. | v15 | The `RouterLinkWithHref` directive code was merged into `RouterLink`. Now the `RouterLink` directive can be used for all elements that have `routerLink` attribute. | | [`provideRoutes` function](api/router/provideRoutes) | Use `ROUTES` `InjectionToken` instead. | v15 | The `provideRoutes` helper function is minimally useful and can be unintentionally used instead of `provideRouter` due to similar spelling. | | [`setupTestingRouter` function](api/router/testing/setupTestingRouter) | Use `provideRouter` or `RouterTestingModule` instead. | v15.1 | The `setupTestingRouter` function is not necessary. The `Router` is initialized based on the DI configuration in tests as it would be in production. | +| [class and `InjectionToken` guards and resolvers](api/router/DeprecatedGuard) | Use plain JavaScript functions instead. | v15.2 | Functional guards are simpler and more powerful than class and token-based guards. | + diff --git a/goldens/public-api/router/index.md b/goldens/public-api/router/index.md index 11230f04e8f..452fca2902e 100644 --- a/goldens/public-api/router/index.md +++ b/goldens/public-api/router/index.md @@ -24,6 +24,7 @@ import { OnChanges } from '@angular/core'; import { OnDestroy } from '@angular/core'; import { OnInit } from '@angular/core'; import { Provider } from '@angular/core'; +import { ProviderToken } from '@angular/core'; import { QueryList } from '@angular/core'; import { Renderer2 } from '@angular/core'; import { SimpleChanges } from '@angular/core'; @@ -111,13 +112,13 @@ export abstract class BaseRouteReuseStrategy implements RouteReuseStrategy { store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void; } -// @public +// @public @deprecated export interface CanActivate { // (undocumented) canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree; } -// @public +// @public @deprecated export interface CanActivateChild { // (undocumented) canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree; @@ -129,7 +130,7 @@ export type CanActivateChildFn = (childRoute: ActivatedRouteSnapshot, state: Rou // @public export type CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable | Promise | boolean | UrlTree; -// @public +// @public @deprecated export interface CanDeactivate { // (undocumented) canDeactivate(component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState: RouterStateSnapshot): Observable | Promise | boolean | UrlTree; @@ -147,7 +148,7 @@ export interface CanLoad { // @public @deprecated export type CanLoadFn = (route: Route, segments: UrlSegment[]) => Observable | Promise | boolean | UrlTree; -// @public +// @public @deprecated export interface CanMatch { // (undocumented) canMatch(route: Route, segments: UrlSegment[]): Observable | Promise | boolean | UrlTree; @@ -237,6 +238,9 @@ export class DefaultUrlSerializer implements UrlSerializer { serialize(tree: UrlTree): string; } +// @public @deprecated +export type DeprecatedGuard = ProviderToken | any; + // @public export type DetachedRouteHandle = {}; @@ -561,7 +565,7 @@ export function provideRoutes(routes: Routes): Provider[]; // @public export type QueryParamsHandling = 'merge' | 'preserve' | ''; -// @public +// @public @deprecated export interface Resolve { // (undocumented) resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | T; @@ -569,7 +573,7 @@ export interface Resolve { // @public export type ResolveData = { - [key: string | symbol]: any | ResolveFn; + [key: string | symbol]: ResolveFn | DeprecatedGuard; }; // @public @@ -611,12 +615,12 @@ export class ResolveStart extends RouterEvent { // @public export interface Route { - canActivate?: Array; - canActivateChild?: Array; - canDeactivate?: Array | any>; + canActivate?: Array; + canActivateChild?: Array; + canDeactivate?: Array | DeprecatedGuard>; // @deprecated - canLoad?: Array; - canMatch?: Array | InjectionToken | CanMatchFn>; + canLoad?: Array; + canMatch?: Array; children?: Routes; component?: Type; data?: Data; diff --git a/packages/router/src/index.ts b/packages/router/src/index.ts index 3490400bc5d..c0dc153765c 100644 --- a/packages/router/src/index.ts +++ b/packages/router/src/index.ts @@ -12,7 +12,7 @@ export {RouterLink, RouterLinkWithHref} from './directives/router_link'; export {RouterLinkActive} from './directives/router_link_active'; export {RouterOutlet, RouterOutletContract} from './directives/router_outlet'; export {ActivationEnd, ActivationStart, ChildActivationEnd, ChildActivationStart, Event, EventType, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationCancellationCode as NavigationCancellationCode, NavigationEnd, NavigationError, NavigationSkipped, NavigationSkippedCode, NavigationStart, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouterEvent, RoutesRecognized, Scroll} from './events'; -export {CanActivate, CanActivateChild, CanActivateChildFn, CanActivateFn, CanDeactivate, CanDeactivateFn, CanLoad, CanLoadFn, CanMatch, CanMatchFn, Data, DefaultExport, LoadChildren, LoadChildrenCallback, NavigationBehaviorOptions, OnSameUrlNavigation, QueryParamsHandling, Resolve, ResolveData, ResolveFn, Route, Routes, RunGuardsAndResolvers, UrlMatcher, UrlMatchResult} from './models'; +export {CanActivate, CanActivateChild, CanActivateChildFn, CanActivateFn, CanDeactivate, CanDeactivateFn, CanLoad, CanLoadFn, CanMatch, CanMatchFn, Data, DefaultExport, DeprecatedGuard, LoadChildren, LoadChildrenCallback, NavigationBehaviorOptions, OnSameUrlNavigation, QueryParamsHandling, Resolve, ResolveData, ResolveFn, Route, Routes, RunGuardsAndResolvers, UrlMatcher, UrlMatchResult} from './models'; export {Navigation, NavigationExtras, UrlCreationOptions} from './navigation_transition'; export {DefaultTitleStrategy, TitleStrategy} from './page_title_strategy'; export {DebugTracingFeature, DisabledInitialNavigationFeature, EnabledBlockingInitialNavigationFeature, InitialNavigationFeature, InMemoryScrollingFeature, NavigationErrorHandlerFeature, PreloadingFeature, provideRouter, provideRoutes, RouterConfigurationFeature, RouterFeature, RouterFeatures, RouterHashLocationFeature, withDebugTracing, withDisabledInitialNavigation, withEnabledBlockingInitialNavigation, withHashLocation, withInMemoryScrolling, withNavigationErrorHandler, withPreloading, withRouterConfig} from './provide_router'; diff --git a/packages/router/src/models.ts b/packages/router/src/models.ts index 201fcdfff6f..4f40dac61bc 100644 --- a/packages/router/src/models.ts +++ b/packages/router/src/models.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {EnvironmentInjector, EnvironmentProviders, InjectionToken, NgModuleFactory, Provider, Type} from '@angular/core'; +import {EnvironmentInjector, EnvironmentProviders, NgModuleFactory, Provider, ProviderToken, Type} from '@angular/core'; import {Observable} from 'rxjs'; import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state'; @@ -38,6 +38,23 @@ import {UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree'; */ export type OnSameUrlNavigation = 'reload'|'ignore'; +/** + * The `InjectionToken` and `@Injectable` classes for guards and resolvers are deprecated in favor + * of plain JavaScript functions instead.. Dependency injection can still be achieved using the + * `inject` function from `@angular/core`. + * + * @deprecated + * @see CanMatchFn + * @see CanLoadFn + * @see CanActivateFn + * @see CanActivateChildFn + * @see CanDeactivateFn + * @see ResolveFn + * @see inject + * @publicApi + */ +export type DeprecatedGuard = ProviderToken|any; + /** * Represents a route configuration for the Router service. * An array of `Route` objects, used in `Router.config` and for nested route configurations @@ -110,7 +127,7 @@ export type Data = { * @publicApi */ export type ResolveData = { - [key: string|symbol]: any|ResolveFn + [key: string|symbol]: ResolveFn|DeprecatedGuard }; /** @@ -517,7 +534,7 @@ export interface Route { * When using a function rather than DI tokens, the function can call `inject` to get any required * dependencies. This `inject` call must be done in a synchronous context. */ - canActivate?: Array; + canActivate?: Array; /** * An array of `CanMatchFn` or DI tokens used to look up `CanMatch()` * handlers, in order to determine if the current user is allowed to @@ -526,7 +543,7 @@ export interface Route { * When using a function rather than DI tokens, the function can call `inject` to get any required * dependencies. This `inject` call must be done in a synchronous context. */ - canMatch?: Array|InjectionToken|CanMatchFn>; + canMatch?: Array; /** * An array of `CanActivateChildFn` or DI tokens used to look up `CanActivateChild()` handlers, * in order to determine if the current user is allowed to activate @@ -535,7 +552,7 @@ export interface Route { * When using a function rather than DI tokens, the function can call `inject` to get any required * dependencies. This `inject` call must be done in a synchronous context. */ - canActivateChild?: Array; + canActivateChild?: Array; /** * An array of `CanDeactivateFn` or DI tokens used to look up `CanDeactivate()` * handlers, in order to determine if the current user is allowed to @@ -544,7 +561,7 @@ export interface Route { * When using a function rather than DI tokens, the function can call `inject` to get any required * dependencies. This `inject` call must be done in a synchronous context. */ - canDeactivate?: Array|any>; + canDeactivate?: Array|DeprecatedGuard>; /** * An array of `CanLoadFn` or DI tokens used to look up `CanLoad()` * handlers, in order to determine if the current user is allowed to @@ -554,7 +571,7 @@ export interface Route { * dependencies. This `inject` call must be done in a synchronous context. * @deprecated Use `canMatch` instead */ - canLoad?: Array; + canLoad?: Array; /** * Additional developer-defined data provided to the component via * `ActivatedRoute`. By default, no additional data is passed. @@ -696,6 +713,8 @@ export interface LoadedRouterConfig { * ``` * * @publicApi + * @deprecated Use plain JavaScript functions instead. + * @see CanActivateFn */ export interface CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): @@ -791,6 +810,8 @@ export type CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSn * ``` * * @publicApi + * @deprecated Use plain JavaScript functions instead. + * @see CanActivateChildFn */ export interface CanActivateChild { canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): @@ -880,6 +901,8 @@ export type CanActivateChildFn = (childRoute: ActivatedRouteSnapshot, state: Rou * ``` * * @publicApi + * @deprecated Use plain JavaScript functions instead. + * @see CanDeactivateFn */ export interface CanDeactivate { canDeactivate( @@ -982,6 +1005,8 @@ export type CanDeactivateFn = * ``` * * @publicApi + * @deprecated Use plain JavaScript functions instead. + * @see CanMatchFn */ export interface CanMatch { canMatch(route: Route, segments: UrlSegment[]): @@ -1112,6 +1137,8 @@ export type CanMatchFn = (route: Route, segments: UrlSegment[]) => * The order of execution is: BaseGuard, ChildGuard, BaseDataResolver, ChildDataResolver. * * @publicApi + * @deprecated Use plain JavaScript functions instead. + * @see ResolveFn */ export interface Resolve { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable|Promise|T; @@ -1196,7 +1223,7 @@ export type ResolveFn = (route: ActivatedRouteSnapshot, state: RouterStateSna * ``` * * @publicApi - * @deprecated Use `CanMatch` instead + * @deprecated Use `CanMatchFn` instead */ export interface CanLoad { canLoad(route: Route, segments: UrlSegment[]): diff --git a/packages/router/src/operators/check_guards.ts b/packages/router/src/operators/check_guards.ts index d832c5aebf9..2de2159766e 100644 --- a/packages/router/src/operators/check_guards.ts +++ b/packages/router/src/operators/check_guards.ts @@ -11,7 +11,7 @@ import {concat, defer, from, MonoTypeOperatorFunction, Observable, of, OperatorF import {concatMap, first, map, mergeMap, tap} from 'rxjs/operators'; import {ActivationStart, ChildActivationStart, Event} from '../events'; -import {CanActivateChild, CanActivateChildFn, CanActivateFn, Route} from '../models'; +import {CanActivateChild, CanActivateChildFn, CanActivateFn, CanDeactivateFn, CanLoadFn, CanMatchFn, Route} from '../models'; import {redirectingNavigationError} from '../navigation_canceling_error'; import {NavigationTransition} from '../navigation_transition'; import {ActivatedRouteSnapshot, RouterStateSnapshot} from '../router_state'; @@ -142,7 +142,8 @@ function runCanActivateChild( getTokenOrFunctionIdentity(canActivateChild, closestInjector); const guardVal = isCanActivateChild(guard) ? guard.canActivateChild(futureARS, futureRSS) : - closestInjector.runInContext(() => guard(futureARS, futureRSS)); + closestInjector.runInContext( + () => (guard as CanActivateChildFn)(futureARS, futureRSS)); return wrapIntoObservable(guardVal).pipe(first()); }); return of(guardsMapped).pipe(prioritizedGuardValue()); @@ -161,8 +162,8 @@ function runCanDeactivate( const guard = getTokenOrFunctionIdentity(c, closestInjector); const guardVal = isCanDeactivate(guard) ? guard.canDeactivate(component, currARS, currRSS, futureRSS) : - closestInjector.runInContext( - () => guard(component, currARS, currRSS, futureRSS)); + closestInjector.runInContext( + () => (guard as CanDeactivateFn)(component, currARS, currRSS, futureRSS)); return wrapIntoObservable(guardVal).pipe(first()); }); return of(canDeactivateObservables).pipe(prioritizedGuardValue()); @@ -180,7 +181,7 @@ export function runCanLoadGuards( const guard = getTokenOrFunctionIdentity(injectionToken, injector); const guardVal = isCanLoad(guard) ? guard.canLoad(route, segments) : - injector.runInContext(() => guard(route, segments)); + injector.runInContext(() => (guard as CanLoadFn)(route, segments)); return wrapIntoObservable(guardVal); }); @@ -213,7 +214,7 @@ export function runCanMatchGuards( const guard = getTokenOrFunctionIdentity(injectionToken, injector); const guardVal = isCanMatch(guard) ? guard.canMatch(route, segments) : - injector.runInContext(() => guard(route, segments)); + injector.runInContext(() => (guard as CanMatchFn)(route, segments)); return wrapIntoObservable(guardVal); });