This is a quick demo of one way you can accomplish dynamic page titles in the html head tags with Angular. This should work in most recent versions of Angular (8+) but let me know on Twitter if it doesn’t work or if there are better solutions to this problem. I found it semi-frustrating compared to how easy it is in Vue and React land. This crazy map and filter stuff is needed for nested route structures.
You can define a base title for your application in index.html
.
<!-- index.html -->
<title>My Fancy Application</title>
// app.component.ts
@Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>',
})
export class AppComponent implements OnInit {
constructor(
private titleService: Title,
private activatedRoute: ActivatedRoute,
private router: Router,
) {
ngOnInit() {
// Grab the title from index.html
const appTitle = this.titleService.getTitle();
this.router.events
.pipe(
filter((event) => event instanceof NavigationEnd),
map(() => this.activatedRoute),
map((route) => {
while (route.firstChild) route = route.firstChild;
return route;
}),
filter((route) => route.outlet === 'primary'),
mergeMap((route) => route.data),
)
.subscribe((event) => {
if (event.title) {
return this.titleService.setTitle(`${appTitle} | ${event.title}`);
}
// This is neccessary to unset the more specific title if it was the previous page.
return this.titleService.setTitle(appTitle);
});
}
}
// routes.ts
{
path: '',
component: WrapperComponent,
canActivate: [AuthGuard],
children: [
{
path: 'blog',
component: BlogComponent,
data: { title: 'Blog' },
},
{
path: 'docs',
children: [
{
path: '',
component: DocsComponent,
data: { title: 'Docs' },
},
{
path: 'examples',
component: ExamplesComponent,
children: [
{
path: '',
component: ExamplesListingComponent,
data: { title: 'Examples' },
},
{
path: ':id',
component: ExampleDetailsComponent,
data: { title: 'Examples | Example Details' },
},
],
},
],
},
],
},
Written by Matt Gregg, a UI engineer who lives and works in Minneapolis, MN
Have something to say about this post? Tweet at me @itwasmattgregg