Skip to content

Commit

Permalink
fix(router): Ensure ActivatedRouteSnapshot#title has correct value (a…
Browse files Browse the repository at this point in the history
…ngular#47481)

ActivatedRouteSnapshot data gets mutated in the resolve phase of the Router. The title is assigned as part of this.
As a result, the title must be a getter in order to pick up the value that was note available during the class creation.

fixes angular#47459

BREAKING CHANGE: The title property is now required on ActivatedRouteSnapshot

PR Close angular#47481
  • Loading branch information
atscott authored and dylhunn committed Sep 27, 2022
1 parent 002ee32 commit 6a2bb17
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 3 deletions.
2 changes: 1 addition & 1 deletion goldens/public-api/router/index.md
Expand Up @@ -74,7 +74,7 @@ export class ActivatedRouteSnapshot {
queryParams: Params;
get root(): ActivatedRouteSnapshot;
readonly routeConfig: Route | null;
readonly title?: string;
get title(): string | undefined;
// (undocumented)
toString(): string;
url: UrlSegment[];
Expand Down
52 changes: 52 additions & 0 deletions packages/compiler-cli/src/ngtsc/scope/src/global.ts
@@ -0,0 +1,52 @@
/**
* @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
*/

import ts from 'typescript';

import {Reference} from '../../imports';
import {DirectiveMeta, MetadataReader, MetadataRegistry, MetaKind, NgModuleMeta, PipeMeta} from '../../metadata';
import {ClassDeclaration} from '../../reflection';

export class GlobalDirectiveRegistry implements MetadataRegistry, MetadataReader {
constructor() {}

knownDirectives = new Map<ClassDeclaration, DirectiveMeta>();
knownNgModules = new Map<ClassDeclaration, NgModuleMeta>();

get knownStandaloneDirectives(): Map<ClassDeclaration, DirectiveMeta> {
return new Map([...this.knownDirectives].filter(([k, v]) => v.isStandalone));
}

registerNgModuleMetadata(mod: NgModuleMeta): void {
this.knownNgModules.set(mod.ref.node, mod);
}

registerDirectiveMetadata(directive: DirectiveMeta): void {
this.knownDirectives.set(directive.ref.node, directive);
}

registerPipeMetadata(pipe: PipeMeta): void {}

getDirectiveMetadata(ref: Reference<ClassDeclaration>): DirectiveMeta|null {
if (this.knownDirectives.has(ref.node)) {
return this.knownDirectives.get(ref.node)!;
}
return null;
}

getNgModuleMetadata(ref: Reference<ClassDeclaration>): NgModuleMeta|null {
if (this.knownNgModules.has(ref.node)) {
return this.knownNgModules.get(ref.node)!;
}
return null;
}

getPipeMetadata(ref: Reference<ClassDeclaration>): PipeMeta|null {
return null;
}
}
6 changes: 5 additions & 1 deletion packages/router/src/router_state.ts
Expand Up @@ -308,7 +308,11 @@ export class ActivatedRouteSnapshot {
_queryParamMap!: ParamMap;

/** The resolved route title */
readonly title?: string = this.data?.[RouteTitleKey];
get title(): string|undefined {
// Note: This _must_ be a getter because the data is mutated in the resolvers. Title will not be
// available at the time of class instantiation.
return this.data?.[RouteTitleKey];
}

/** @internal */
constructor(
Expand Down
14 changes: 13 additions & 1 deletion packages/router/test/page_title_strategy_spec.ts
Expand Up @@ -9,7 +9,7 @@
import {DOCUMENT} from '@angular/common';
import {Component, Inject, Injectable, NgModule} from '@angular/core';
import {fakeAsync, TestBed, tick} from '@angular/core/testing';
import {Router, RouterModule, RouterStateSnapshot, TitleStrategy} from '@angular/router';
import {NavigationEnd, Router, RouterModule, RouterStateSnapshot, TitleStrategy} from '@angular/router';

import {provideRouterForTesting} from '../testing/src/provide_router_for_testing';

Expand Down Expand Up @@ -99,6 +99,18 @@ describe('title strategy', () => {
tick();
expect(document.title).toBe('resolved title');
}));

it('can get the title from the ActivatedRouteSnapshot', async () => {
router.resetConfig([
{
path: 'home',
title: 'My Application',
component: BlankCmp,
},
]);
await router.navigateByUrl('home');
expect(router.routerState.snapshot.root.firstChild!.title).toEqual('My Application');
});
});

describe('custom strategies', () => {
Expand Down

0 comments on commit 6a2bb17

Please sign in to comment.