Skip to content

Commit

Permalink
feat(router): expose resolved route title
Browse files Browse the repository at this point in the history
fixes #46825
  • Loading branch information
EmmanuelRoux committed Jul 13, 2022
1 parent e91d624 commit 93e1afa
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 8 deletions.
2 changes: 2 additions & 0 deletions goldens/public-api/router/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export class ActivatedRoute {
get root(): ActivatedRoute;
get routeConfig(): Route | null;
snapshot: ActivatedRouteSnapshot;
get title(): Observable<string | undefined>;
// (undocumented)
toString(): string;
url: Observable<UrlSegment[]>;
Expand All @@ -73,6 +74,7 @@ export class ActivatedRouteSnapshot {
queryParams: Params;
get root(): ActivatedRouteSnapshot;
readonly routeConfig: Route | null;
get title(): string | undefined;
// (undocumented)
toString(): string;
url: UrlSegment[];
Expand Down
8 changes: 1 addition & 7 deletions packages/router/src/operators/resolve_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import {Injector} from '@angular/core';
import {RouteTitle} from '@angular/router/src/operators/route_title';
import {EMPTY, EmptyError, from, MonoTypeOperatorFunction, Observable, of, throwError} from 'rxjs';
import {catchError, concatMap, first, map, mapTo, mergeMap, takeLast, tap} from 'rxjs/operators';

Expand All @@ -16,13 +17,6 @@ import {ActivatedRouteSnapshot, inheritedParamsDataResolve, RouterStateSnapshot}
import {wrapIntoObservable} from '../utils/collection';
import {getToken} from '../utils/preactivation';

/**
* A private symbol used to store the value of `Route.title` inside the `Route.data` if it is a
* static string or `Route.resolve` if anything else. This allows us to reuse the existing route
* data/resolvers to support the title feature without new instrumentation in the `Router` pipeline.
*/
export const RouteTitle = Symbol('RouteTitle');

export function resolveData(
paramsInheritanceStrategy: 'emptyOnly'|'always',
moduleInjector: Injector): MonoTypeOperatorFunction<NavigationTransition> {
Expand Down
14 changes: 14 additions & 0 deletions packages/router/src/operators/route_title.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

/**
* A private symbol used to store the value of `Route.title` inside the `Route.data` if it is a
* static string or `Route.resolve` if anything else. This allows us to reuse the existing route
* data/resolvers to support the title feature without new instrumentation in the `Router` pipeline.
*/
export const RouteTitle = Symbol('RouteTitle');
2 changes: 1 addition & 1 deletion packages/router/src/page_title_strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

import {Injectable} from '@angular/core';
import {Title} from '@angular/platform-browser';
import {RouteTitle as TitleKey} from '@angular/router/src/operators/route_title';

import {RouteTitle as TitleKey} from './operators/resolve_data';
import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
import {PRIMARY_OUTLET} from './shared';

Expand Down
16 changes: 16 additions & 0 deletions packages/router/src/router_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {BehaviorSubject, Observable} from 'rxjs';
import {map} from 'rxjs/operators';

import {Data, ResolveData, Route} from './models';
import {RouteTitle as TitleKey} from './operators/route_title';
import {convertToParamMap, ParamMap, Params, PRIMARY_OUTLET} from './shared';
import {equalSegments, UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';
import {shallowEqual, shallowEqualArrays} from './utils/collection';
Expand Down Expand Up @@ -120,6 +121,8 @@ export class ActivatedRoute {
_paramMap!: Observable<ParamMap>;
/** @internal */
_queryParamMap!: Observable<ParamMap>;
/** @internal */
_title!: Observable<string|undefined>;

/** @internal */
constructor(
Expand Down Expand Up @@ -194,6 +197,14 @@ export class ActivatedRoute {
return this._queryParamMap;
}

/** An Observable of the resolved route title */
get title(): Observable<string|undefined> {
if (!this._title) {
this._title = this.data.pipe(map((d: Data) => d[TitleKey]));
}
return this._title;
}

toString(): string {
return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;
}
Expand Down Expand Up @@ -386,6 +397,11 @@ export class ActivatedRouteSnapshot {
return this._queryParamMap;
}

/** The resolved route title */
get title(): string|undefined {
return this.data[TitleKey];
}

toString(): string {
const url = this.url.map(segment => segment.toString()).join('/');
const matched = this.routeConfig ? this.routeConfig.path : '';
Expand Down
21 changes: 21 additions & 0 deletions packages/router/test/router_state.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {RouteTitle as TitleKey} from '@angular/router/src/operators/route_title';
import {BehaviorSubject} from 'rxjs';

import {ActivatedRoute, ActivatedRouteSnapshot, advanceActivatedRoute, equalParamsAndUrlSegments, RouterState, RouterStateSnapshot} from '../src/router_state';
Expand Down Expand Up @@ -202,6 +203,26 @@ describe('RouterState & Snapshot', () => {
expect(hasSeenDataChange).toEqual(true);
});
});

describe('ActivatedRoute', () => {
it('should get resolved route title', () => {
const data = {[TitleKey]: 'resolved title'};
const route = createActivatedRoute('a');
const snapshot = new (ActivatedRouteSnapshot as any)(
<any>[], <any>null, <any>null, <any>null, data, <any>null, 'test', <any>null, <any>null,
-1, null!);
let resolvedTitle: string|undefined;

route.data.next(data);

route.title.forEach((title: string|undefined) => {
resolvedTitle = title;
});

expect(resolvedTitle).toEqual('resolved title');
expect(snapshot.title).toEqual('resolved title');
});
});
});

function createActivatedRouteSnapshot(cmp: string) {
Expand Down

0 comments on commit 93e1afa

Please sign in to comment.