diff --git a/modules/@angular/router/src/common_router_providers.ts b/modules/@angular/router/src/common_router_providers.ts index 4f35a4c7af..9e7381557f 100644 --- a/modules/@angular/router/src/common_router_providers.ts +++ b/modules/@angular/router/src/common_router_providers.ts @@ -63,7 +63,7 @@ export function setupRouterInitializer(injector: Injector) { } /** - * A list of {@link Provider}s. To use the router, you must add this to your application. + * An array of {@link Provider}s. To use the router, you must add this to your application. * * ### Example * @@ -73,14 +73,14 @@ export function setupRouterInitializer(injector: Injector) { * // ... * } * - * const router = [ - * {path: '/home', component: Home} + * const config = [ + * {path: 'home', component: Home} * ]; * - * bootstrap(AppCmp, [provideRouter(router)]); + * bootstrap(AppCmp, [provideRouter(config)]); * ``` * - * @experimental + * @stable */ export function provideRouter(_config: RouterConfig, _opts: ExtraOptions): any[] { return [ diff --git a/modules/@angular/router/src/config.ts b/modules/@angular/router/src/config.ts index 6ed672958a..560f6b7712 100644 --- a/modules/@angular/router/src/config.ts +++ b/modules/@angular/router/src/config.ts @@ -9,27 +9,247 @@ import {Type} from '@angular/core'; /** - * @experimental + * `RouterConfig` is an array of route configurations. Each one has the following properties: + * + * - *`path`* is a string that uses the route matcher DSL. + * - `pathMatch` is a string that specifies the matching strategy. + * - `component` is a component type. + * - `redirectTo` is the url fragment which will replace the current matched segment. + * - `outlet` is the name of the outlet the component should be placed into. + * - `canActivate` is an array of DI tokens used to look up CanActivate handlers. See {@link + * CanActivate} for more info. + * - `canDeactivate` is an array of DI tokens used to look up CanDeactivate handlers. See {@link + * CanDeactivate} for more info. + * - `data` is additional data provided to the component via `ActivatedRoute`. + * - `resolve` is a map of DI tokens used to look up data resolvers. See {@link Resolve} for more + * info. + * - `children` is an array of child route definitions. + * + * ### Simple Configuration + * + * ``` + * [{ + * path: 'team/:id', + * component: Team, + * children: [ + * { + * path: 'user/:name', + * component: User + * } + * ] + * }] + * ``` + * + * When navigating to `/team/11/user/bob`, the router will create the team component with the user + * component in it. + * + * ### Multiple Outlets + * + * ``` + * [{ + * path: 'team/:id', + * component: Team + * }, + * { + * path: 'chat/:user', + * component: Chat + * outlet: aux + * }] + * ``` + * + * When navigating to `/team/11(aux:chat/jim)`, the router will create the team component next to + * the chat component. The chat component will be placed into the aux outlet. + * + * ### Wild Cards + * + * ``` + * [{ + * path: '**', + * component: Sink + * }] + * ``` + * + * Regardless of where you navigate to, the router will instantiate the sink component. + * + * ### Redirects + * + * ``` + * [{ + * path: 'team/:id', + * component: Team, + * children: [ + * { + * path: 'legacy/user/:name', + * redirectTo: 'user/:name' + * }, + * { + * path: 'user/:name', + * component: User + * } + * ] + * }] + * ``` + * + * When navigating to '/team/11/legacy/user/jim', the router will change the url to + * '/team/11/user/jim', and then will instantiate the team component with the user component + * in it. + * + * If the `redirectTo` value starts with a '/', then it is a global redirect. E.g., if in the + * example above we change the `redirectTo` to `/user/:name`, the result url will be '/user/jim'. + * + * ### Empty Path + * + * Empty-path route configurations can be used to instantiate components that do not "consume" + * any url segments. Let's look at the following configuration: + * + * ``` + * [{ + * path: 'team/:id', + * component: Team, + * children: [ + * { + * path: '', + * component: AllUsers + * }, + * { + * path: 'user/:name', + * component: User + * } + * ] + * }] + * ``` + * + * When navigating to `/team/11`, the router will instantiate the AllUsers component. + * + * Empty-path routes can have children. + * + * ``` + * [{ + * path: 'team/:id', + * component: Team, + * children: [ + * { + * path: '', + * component: WrapperCmp, + * children: [ + * { + * path: 'user/:name', + * component: User + * } + * ] + * } + * ] + * }] + * ``` + * + * When navigating to `/team/11/user/jim`, the router will instantiate the wrapper component with + * the user component in it. + * + * ### Matching Strategy + * + * By default the router will look at what is left in the url, and check if it starts with + * the specified path (e.g., `/team/11/user` starts with `team/:id`). + * + * We can change the matching strategy to make sure that the path covers the whole unconsumed url, + * which is akin to `unconsumedUrl === path` or `$` regular expressions. + * + * This is particularly important when redirecting empty-path routes. + * + * ``` + * [{ + * path: '', + * pathMatch: 'prefix', //default + * redirectTo: 'main' + * }, + * { + * path: 'main', + * component: Main + * }] + * ``` + * + * Since an empty path is a prefix of any url, even when navigating to '/main', the router will + * still apply the redirect. + * + * If `pathMatch: full` is provided, the router will apply the redirect if and only if navigating to + * '/'. + * + * ``` + * [{ + * path: '', + * pathMatch: 'full', + * redirectTo: 'main' + * }, + * { + * path: 'main', + * component: Main + * }] + * ``` + * + * ### Componentless Routes + * + * It is useful at times to have the ability to share parameters between sibling components. + * + * Say we have two components--ChildCmp and AuxCmp--that we want to put next to each other and both + * of them require some id parameter. + * + * One way to do that would be to have a bogus parent component, so both the siblings can get the id + * parameter from it. This is not ideal. Instead, you can use a componentless route. + * + * ``` + * [{ + * path: 'parent/:id', + * children: [ + * { path: 'a', component: MainChild }, + * { path: 'b', component: AuxChild, outlet: 'aux' } + * ] + * }] + * ``` + * + * So when navigating to `parent/10/(a//aux:b)`, the route will instantiate the main child and aux + * child components next to each other. In this example, the application component + * has to have the primary and aux outlets defined. + * + * The router will also merge the `params`, `data`, and `resolve` of the componentless parent into + * the `params`, `data`, and `resolve` of the children. + * + * This is especially useful when child components are defined as follows: + * + * ``` + * [{ + * path: 'parent/:id', + * children: [ + * { path: '', component: MainChild }, + * { path: '', component: AuxChild, outlet: 'aux' } + * ] + * }] + * ``` + * + * With this configuration in place, navigating to '/parent/10' will create the main child and aux + * components. + * + * @stable */ export type RouterConfig = Route[]; /** - * @experimental + * See {@link RouterConfig} for more details. + * @stable */ export type Data = { [name: string]: any }; /** - * @experimental + * See {@link RouterConfig} for more details. + * @stable */ export type ResolveData = { [name: string]: any }; - /** - * @experimental + * See {@link RouterConfig} for more details. + * @stable */ export interface Route { path?: string; @@ -40,13 +260,13 @@ export interface Route { terminal?: boolean; pathMatch?: 'full'|'prefix'; component?: Type|string; + redirectTo?: string; outlet?: string; canActivate?: any[]; canDeactivate?: any[]; - redirectTo?: string; - children?: Route[]; data?: Data; resolve?: ResolveData; + children?: Route[]; } export function validateConfig(config: RouterConfig): void { diff --git a/modules/@angular/router/src/directives/router_link.ts b/modules/@angular/router/src/directives/router_link.ts index ec6e9fbed2..2ca387a36f 100644 --- a/modules/@angular/router/src/directives/router_link.ts +++ b/modules/@angular/router/src/directives/router_link.ts @@ -13,32 +13,46 @@ import {Router} from '../router'; import {ActivatedRoute} from '../router_state'; import {UrlTree} from '../url_tree'; - - /** * The RouterLink directive lets you link to specific parts of your app. * * Consider the following route configuration: * ``` - * [{ path: '/user', component: UserCmp }] + * [{ path: 'user/:name', component: UserCmp }] * ``` * * When linking to this `User` route, you can write: * * ``` - * link to user component + * link to user component * ``` * - * RouterLink expects the value to be an array of path segments, followed by the params - * for that level of routing. For instance `['/team', {teamId: 1}, 'user', {userId: 2}]` - * means that we want to generate a link to `/team;teamId=1/user;userId=2`. + * If you use dynamic values to generate the link, you can pass an array of path + * segments, followed by the params for each segment. * - * The first segment name can be prepended with `/`, `./`, or `../`. - * If the segment begins with `/`, the router will look up the route from the root of the app. - * If the segment begins with `./`, or doesn't begin with a slash, the router will - * instead look in the current component's children for the route. - * And if the segment begins with `../`, the router will go up one level. + * For instance `['/team', teamId, 'user', userName, {details: true}]` + * means that we want to generate a link to `/team/11/user/bob;details=true`. + * Multiple static segments can be merged into one (e.g., `['/team/11/user', userName, {details: + true}]`). + * + * The first segment name can be prepended with `/`, `./`, or `../`: + * * If the first segment begins with `/`, the router will look up the route from the root of the + app. + * * If the first segment begins with `./`, or doesn't begin with a slash, the router will + * instead look in the children of the current activated route. + * * And if the first segment begins with `../`, the router will go up one level. + * + * You can set query params and fragment as follows: + * + * ``` + * link to user + component + * ``` + * + * RouterLink will use these to generate this link: `/user/bob#education?debug=true`. + * + * @stable */ @Directive({selector: '[routerLink]'}) export class RouterLink implements OnChanges { diff --git a/modules/@angular/router/src/directives/router_link_active.ts b/modules/@angular/router/src/directives/router_link_active.ts index 65cebbdae8..a0a12c8af4 100644 --- a/modules/@angular/router/src/directives/router_link_active.ts +++ b/modules/@angular/router/src/directives/router_link_active.ts @@ -14,17 +14,55 @@ import {containsTree} from '../url_tree'; import {RouterLink} from './router_link'; -interface RouterLinkActiveOptions { - exact: boolean; -} - +/** + * The RouterLinkActive directive lets you add a CSS class to an element when the link's route + * becomes active. + * + * Consider the following example: + * + * ``` + * Bob + * ``` + * + * When the url is either '/user' or '/user/bob', the active-link class will + * be added to the `a` tag. If the url changes, the class will be removed. + * + * You can set more than one class, as follows: + * + * ``` + * Bob + * Bob + * ``` + * + * You can configure RouterLinkActive by passing `exact: true`. This will add the classes + * only when the url matches the link exactly. + * + * ``` + * Bob + * ``` + * + * Finally, you can apply the RouterLinkActive directive to an ancestor of a RouterLink. + * + * ``` + *
+ * Jim + * Bob + *
+ * ``` + * + * This will set the active-link class on the div tag if the url is either '/user/jim' or + * '/user/bob'. + * + * @stable + */ @Directive({selector: '[routerLinkActive]'}) export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit { @ContentChildren(RouterLink) private links: QueryList; private classes: string[] = []; private subscription: Subscription; - @Input() private routerLinkActiveOptions: RouterLinkActiveOptions = {exact: false}; + @Input() private routerLinkActiveOptions: {exact: boolean} = {exact: false}; /** * @internal diff --git a/modules/@angular/router/src/directives/router_outlet.ts b/modules/@angular/router/src/directives/router_outlet.ts index 8f52e964f4..1998f1ac53 100644 --- a/modules/@angular/router/src/directives/router_outlet.ts +++ b/modules/@angular/router/src/directives/router_outlet.ts @@ -12,6 +12,19 @@ import {RouterOutletMap} from '../router_outlet_map'; import {ActivatedRoute} from '../router_state'; import {PRIMARY_OUTLET} from '../shared'; +/** + * A router outlet is a placeholder that Angular dynamically fills based on the application's route. + * + * ## Use + * + * ``` + * + * + * + * ``` + * + * @stable + */ @Directive({selector: 'router-outlet'}) export class RouterOutlet { private activated: ComponentRef; @@ -61,8 +74,10 @@ export class RouterOutlet { } catch (e) { if (!(e instanceof NoComponentFactoryError)) throw e; const componentName = component ? component.name : null; + console.warn( - `No component factory found for '${componentName}'. Add '${componentName}' to the 'precompile' list of your application component. This will be required in a future release of the router.`); + `'${componentName}' not found in precompile array. To ensure all components referred to by the RouterConfig are compiled, you must add '${componentName}' to the 'precompile' array of your application component. This will be required in a future release of the router.`); + factory = snapshot._resolvedComponentFactory; } diff --git a/modules/@angular/router/src/interfaces.ts b/modules/@angular/router/src/interfaces.ts index 1687dfe2e1..c1a1f9dfb2 100644 --- a/modules/@angular/router/src/interfaces.ts +++ b/modules/@angular/router/src/interfaces.ts @@ -12,7 +12,43 @@ import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state'; /** * An interface a class can implement to be a guard deciding if a route can be activated. * - * @experimental + * ### Example + * + * ``` + * class CanActivateTeam implements CanActivate { + * constructor(private permissions: Permissions, private currentUser: UserToken) {} + * + * canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable { + * return this.permissions.canActivate(this.currentUser, this.route.params.id); + * } + * } + * + * bootstrap(AppComponent, [ + * CanActivateTeam, + * + * provideRouter([{ + * path: 'team/:id', + * component: Team, + * canActivate: [CanActivateTeam] + * }]) + * ); + * ``` + * + * You can also provide a function with the same signature instead of the class: + * + * ``` + * bootstrap(AppComponent, [ + * {provide: 'canActivateTeam', useValue: (route: ActivatedRouteSnapshot, state: + * RouterStateSnapshot) => true}, + * provideRouter([{ + * path: 'team/:id', + * component: Team, + * canActivate: ['canActivateTeam'] + * }]) + * ); + * ``` + * + * @stable */ export interface CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): @@ -22,7 +58,43 @@ export interface CanActivate { /** * An interface a class can implement to be a guard deciding if a route can be deactivated. * - * @experimental + * ### Example + * + * ``` + * class CanDeactivateTeam implements CanDeactivate { + * constructor(private permissions: Permissions, private currentUser: UserToken) {} + * + * canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable { + * return this.permissions.canDeactivate(this.currentUser, this.route.params.id); + * } + * } + * + * bootstrap(AppComponent, [ + * CanDeactivateTeam, + * + * provideRouter([{ + * path: 'team/:id', + * component: Team, + * canDeactivate: [CanDeactivateTeam] + * }]) + * ); + * ``` + * + * You can also provide a function with the same signature instead of the class: + * + * ``` + * bootstrap(AppComponent, [ + * {provide: 'canDeactivateTeam', useValue: (route: ActivatedRouteSnapshot, state: + * RouterStateSnapshot) => true}, + * provideRouter([{ + * path: 'team/:id', + * component: Team, + * canActivate: ['canDeactivateTeam'] + * }]) + * ); + * ``` + * + * @stable */ export interface CanDeactivate { canDeactivate(component: T, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): @@ -30,6 +102,34 @@ export interface CanDeactivate { } /** + * An interface a class can implement to be a data provider. + * + * ### Example + * + * ``` + * class TeamResolver implements Resolve { + * constructor(private backend: Backend) {} + * + * resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable { + * return this.backend.fetchTeam(this.route.params.id); + * } + * } + * + * bootstrap(AppComponent, [ + * TeamResolver, + * + * provideRouter([{ + * path: 'team/:id', + * component: TeamCmp, + * resolve: { + * team: TeamResolver + * } + * }]) + * ); + * ``` + * + * You can also provide a function with the same signature instead of the class. + * * @experimental */ export interface Resolve { diff --git a/modules/@angular/router/src/router.ts b/modules/@angular/router/src/router.ts index 42abae452b..6f16a6cea8 100644 --- a/modules/@angular/router/src/router.ts +++ b/modules/@angular/router/src/router.ts @@ -44,7 +44,7 @@ export interface NavigationExtras { /** * An event triggered when a navigation starts * - * @experimental + * @stable */ export class NavigationStart { constructor(public id: number, public url: string) {} @@ -55,7 +55,7 @@ export class NavigationStart { /** * An event triggered when a navigation ends successfully * - * @experimental + * @stable */ export class NavigationEnd { constructor(public id: number, public url: string, public urlAfterRedirects: string) {} @@ -68,7 +68,7 @@ export class NavigationEnd { /** * An event triggered when a navigation is canceled * - * @experimental + * @stable */ export class NavigationCancel { constructor(public id: number, public url: string) {} @@ -79,7 +79,7 @@ export class NavigationCancel { /** * An event triggered when a navigation fails due to unexpected error * - * @experimental + * @stable */ export class NavigationError { constructor(public id: number, public url: string, public error: any) {} @@ -92,7 +92,7 @@ export class NavigationError { /** * An event triggered when routes are recognized * - * @experimental + * @stable */ export class RoutesRecognized { constructor( @@ -105,14 +105,16 @@ export class RoutesRecognized { } /** - * @experimental + * @stable */ export type Event = NavigationStart | NavigationEnd | NavigationCancel | NavigationError; /** * The `Router` is responsible for mapping URLs to components. * - * @experimental + * See {@link RouterConfig) for more details and examples. + * + * @stable */ export class Router { private currentUrlTree: UrlTree; diff --git a/modules/@angular/router/src/router_outlet_map.ts b/modules/@angular/router/src/router_outlet_map.ts index d26f585698..e81ef07172 100644 --- a/modules/@angular/router/src/router_outlet_map.ts +++ b/modules/@angular/router/src/router_outlet_map.ts @@ -9,7 +9,7 @@ import {RouterOutlet} from './directives/router_outlet'; /** - * @experimental + * @stable */ export class RouterOutletMap { /** @internal */ diff --git a/modules/@angular/router/src/router_providers.ts b/modules/@angular/router/src/router_providers.ts index 2d1ff993cb..ac78d9d16c 100644 --- a/modules/@angular/router/src/router_providers.ts +++ b/modules/@angular/router/src/router_providers.ts @@ -25,10 +25,10 @@ import {RouterConfig} from './config'; * } * * const router = [ - * {path: '/home', component: Home} + * {path: 'home', component: Home} * ]; * - * bootstrap(AppCmp, [provideRouter(router)]); + * bootstrap(AppCmp, [provideRouter(router, {enableTracing: true})]); * ``` * * @experimental diff --git a/modules/@angular/router/src/router_state.ts b/modules/@angular/router/src/router_state.ts index ef97031e6d..21d8a583dd 100644 --- a/modules/@angular/router/src/router_state.ts +++ b/modules/@angular/router/src/router_state.ts @@ -32,7 +32,7 @@ import {Tree, TreeNode} from './utils/tree'; * } * ``` * - * @experimental + * @stable */ export class RouterState extends Tree { /** @@ -75,8 +75,7 @@ function createEmptyStateSnapshot(urlTree: UrlTree, rootComponent: Type): Router /** * Contains the information about a component loaded in an outlet. The information is provided - * through - * the params and urlSegments observables. + * through the params, urlSegments, and data observables. * * ### Usage * @@ -84,11 +83,12 @@ function createEmptyStateSnapshot(urlTree: UrlTree, rootComponent: Type): Router * class MyComponent { * constructor(route: ActivatedRoute) { * const id: Observable = route.params.map(p => p.id); + * const data = route.data.map(d => d.user); //includes `data` and `resolve` * } * } * ``` * - * @experimental + * @stable */ export class ActivatedRoute { /** @internal */ @@ -110,6 +110,9 @@ export class ActivatedRoute { } } +/** + * @internal + */ export class InheritedResolve { /** * @internal @@ -138,11 +141,12 @@ export class InheritedResolve { * class MyComponent { * constructor(route: ActivatedRoute) { * const id: string = route.snapshot.params.id; + * const data = route.snapshot.data; * } * } * ``` * - * @experimental + * @stable */ export class ActivatedRouteSnapshot { /** @@ -195,7 +199,7 @@ export class ActivatedRouteSnapshot { * } * ``` * - * @experimental + * @stable */ export class RouterStateSnapshot extends Tree { /** diff --git a/modules/@angular/router/src/url_tree.ts b/modules/@angular/router/src/url_tree.ts index b179a9d152..06d8a150b6 100644 --- a/modules/@angular/router/src/url_tree.ts +++ b/modules/@angular/router/src/url_tree.ts @@ -63,7 +63,7 @@ function containsSegmentHelper( /** * A URL in the tree form. * - * @experimental + * @stable */ export class UrlTree { /** @@ -76,6 +76,9 @@ export class UrlTree { toString(): string { return new DefaultUrlSerializer().serialize(this); } } +/** + * @stable + */ export class UrlSegment { /** * @internal @@ -100,7 +103,7 @@ export class UrlSegment { /** - * @experimental + * @stable */ export class UrlPathWithParams { constructor(public path: string, public parameters: {[key: string]: string}) {} diff --git a/tools/public_api_guard/router/index.d.ts b/tools/public_api_guard/router/index.d.ts index db3c08ef39..87156d842f 100644 --- a/tools/public_api_guard/router/index.d.ts +++ b/tools/public_api_guard/router/index.d.ts @@ -1,4 +1,4 @@ -/** @experimental */ +/** @stable */ export declare class ActivatedRoute { component: Type | string; data: Observable; @@ -9,7 +9,7 @@ export declare class ActivatedRoute { toString(): string; } -/** @experimental */ +/** @stable */ export declare class ActivatedRouteSnapshot { component: Type | string; data: Data; @@ -19,17 +19,17 @@ export declare class ActivatedRouteSnapshot { toString(): string; } -/** @experimental */ +/** @stable */ export interface CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | boolean; } -/** @experimental */ +/** @stable */ export interface CanDeactivate { canDeactivate(component: T, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | boolean; } -/** @experimental */ +/** @stable */ export declare type Data = { [name: string]: any; }; @@ -40,7 +40,7 @@ export declare class DefaultUrlSerializer implements UrlSerializer { serialize(tree: UrlTree): string; } -/** @experimental */ +/** @stable */ export declare type Event = NavigationStart | NavigationEnd | NavigationCancel | NavigationError; /** @experimental */ @@ -48,7 +48,7 @@ export interface ExtraOptions { enableTracing?: boolean; } -/** @experimental */ +/** @stable */ export declare class NavigationCancel { id: number; url: string; @@ -56,7 +56,7 @@ export declare class NavigationCancel { toString(): string; } -/** @experimental */ +/** @stable */ export declare class NavigationEnd { id: number; url: string; @@ -65,7 +65,7 @@ export declare class NavigationEnd { toString(): string; } -/** @experimental */ +/** @stable */ export declare class NavigationError { error: any; id: number; @@ -74,7 +74,7 @@ export declare class NavigationError { toString(): string; } -/** @experimental */ +/** @stable */ export declare class NavigationStart { id: number; url: string; @@ -98,18 +98,18 @@ export interface Resolve { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | any; } -/** @experimental */ +/** @stable */ export declare type ResolveData = { [name: string]: any; }; -/** @experimental */ +/** @stable */ export interface Route { path?: string; pathMatch?: /** @deprecated */ terminal?: boolean; -/** @experimental */ +/** @stable */ export declare class Router { events: Observable; routerState: RouterState; @@ -125,15 +125,15 @@ export declare class Router { /** @experimental */ export declare const ROUTER_DIRECTIVES: (typeof RouterOutlet | typeof RouterLink | typeof RouterLinkActive)[]; -/** @experimental */ +/** @stable */ export declare type RouterConfig = Route[]; -/** @experimental */ +/** @stable */ export declare class RouterOutletMap { registerOutlet(name: string, outlet: RouterOutlet): void; } -/** @experimental */ +/** @stable */ export declare class RouterState extends Tree { fragment: Observable; queryParams: Observable; @@ -141,7 +141,7 @@ export declare class RouterState extends Tree { toString(): string; } -/** @experimental */ +/** @stable */ export declare class RouterStateSnapshot extends Tree { fragment: string; queryParams: Params; @@ -149,7 +149,7 @@ export declare class RouterStateSnapshot extends Tree { toString(): string; } -/** @experimental */ +/** @stable */ export declare class RoutesRecognized { id: number; state: RouterStateSnapshot; @@ -159,7 +159,7 @@ export declare class RoutesRecognized { toString(): string; } -/** @experimental */ +/** @stable */ export declare class UrlPathWithParams { parameters: { [key: string]: string; @@ -177,7 +177,7 @@ export declare abstract class UrlSerializer { abstract serialize(tree: UrlTree): string; } -/** @experimental */ +/** @stable */ export declare class UrlTree { fragment: string; queryParams: {