Is there any npm module/ other way like React-Helmet that allows us to change page title as we route through our Angular application?
PS: I am using Angular 5.
Is there any npm module/ other way like React-Helmet that allows us to change page title as we route through our Angular application?
PS: I am using Angular 5.
You have a TitleService in Angular 5. Inject it in your component's constructor, and use the setTitle() method.
import {Title} from "@angular/platform-browser"; .... constructor(private titleService:Title) { this.titleService.setTitle("Some title"); } Here are the docs from Angular: https://angular.io/guide/set-document-title
private and the this so stop it adding to the component this valueIn Angular v14, there is a built-in strategy service for collecting the title from the route based on the primary router outlet, and setting the browser's page title.
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AboutComponent } from './about.component'; import { HomeComponent } from './home.component'; const routes: Routes = [ { path: 'home', component: HomeComponent, title: "'My App - Home' // <-- Page title" }, { path: 'about', component: AboutComponent, title: "'My App - About Me' // <-- Page title" } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } See here
Here is tested way to set page title on Angular 8 but you can use it on Angular 5 as well. Once you set this you have to just set title on route file and all will set automatically.
import { Component } from '@angular/core'; import { ActivatedRoute, Router, NavigationEnd } from '@angular/router'; import { Title } from '@angular/platform-browser'; import { filter, map } from "rxjs/operators"; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent { constructor (private router: Router, private activatedRoute: ActivatedRoute, private titleService: Title) { this.router.events.pipe( filter(event => event instanceof NavigationEnd), map(() => { let child = this.activatedRoute.firstChild; while (child) { if (child.firstChild) { child = child.firstChild; } else if (child.snapshot.data && child.snapshot.data['title']) { return child.snapshot.data['title']; } else { return null; } } return null; }) ).subscribe( (data: any) => { if (data) { this.titleService.setTitle(data + ' - Website Name'); } }); } } And on route file you can do something like this:
const routes: Routes = [ { path: 'dashboard', component: DefaultDashboardComponent, data: { title: 'Dashboard' } } ]; F5), it doesn't display the right title as it should be. Any idea to fix it?this.titleService.setTitle(your_variable_with_title_string)Here's a non-recursive solution that also fixes the replication of titles in case of path-less or component-less nested routes.
Make sure to add Angular's Title service to your application: https://angular.io/guide/set-document-title.
Then in your app.component.ts
import { Component, OnInit } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { NavigationEnd, Router } from '@angular/router'; import { filter } from 'rxjs/operators'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit { constructor( private readonly router: Router, private readonly titleService: Title ) { } ngOnInit() { this.router.events.pipe( filter((event) => event instanceof NavigationEnd) ).subscribe(() => { this.titleService.setTitle(this.getNestedRouteTitles().join(' | ')); }); } getNestedRouteTitles(): string[] { let currentRoute = this.router.routerState.root.firstChild; const titles: string[] = []; while (currentRoute) { if (currentRoute.snapshot.routeConfig.data?.title) { titles.push(currentRoute.snapshot.routeConfig.data.title); } currentRoute = currentRoute.firstChild; } return titles; } getLastRouteTitle(): string { let currentRoute = this.router.routerState.root.firstChild; while (currentRoute.firstChild) { currentRoute = currentRoute.firstChild; } return currentRoute.snapshot.data?.title; } } Accessing the specific routeConfig.data prevents the repetition of the inherited title attribute.
And in main-routing.module.ts or any of your other routing files:
const routes: Routes = [ { path: '', component: MainComponent, data: { title: 'MyApplication' }, children: [ { path: '', canActivateChild: [AuthGuard], children: [ { path: 'dashboard', loadChildren: () => import('../dashboard/dashboard.module').then(m => m.DashboardModule), data: { title: 'Dashboard' } }, { path: 'settings', loadChildren: () => import('../settings/settings.module').then(m => m.SettingsModule), data: { title: 'Settings' } } ] } ] } ]; I would prefer to add a wrapper class just to make sure that i will not change everywhere if import {Title} from "@angular/platform-browser"; changed in the upcoming release :) ... Maybe Something called "AppTitleService"
import { Injectable } from '@angular/core'; import { Title } from '@angular/platform-browser'; @Injectable({ providedIn: 'root' }) export class AppTitleService { constructor(private titleService: Title) { } getTitle() { this.titleService.getTitle(); } setTitle(newTitle: string) { this.titleService.setTitle(newTitle); } } // Component.ts import { Component, OnInit } from '@angular/core'; import { Router, NavigationEnd, ActivatedRoute } from '@angular/router'; import { filter } from 'rxjs/operators'; @Component({ selector: 'app-component', templateUrl: './component.html', styleUrls: ['./component.css'] }) export class Component implements OnInit { constructor(private router: Router, private route: ActivatedRoute) { } path: string[] = []; pathTitle: string; ngOnInit() { this.router.events.pipe( filter(event => event instanceof NavigationEnd) ).subscribe((event: NavigationEnd) => { this.path = event.url.substring(1).split('/'); // URL => stackOverflow this.pathTitle = this.route.root.firstChild.snapshot.data.title; // data.title => stack Overflow }); } // app-routing.module.ts const routes: Routes = [ { path: 'stackOverflow', component: Component, data: {title: 'stack Overflow'} } ]; Today is 2022-10-14. Answers above including Angular's own doc are very good, but not the easiest/lightest, and sometimes tedious.
We have hundreds of components/pages, Angular 12+, title and <meta name="description" ...> are typically static, so the best way is hardcode without adding any packages:
<head> <title...> <meta ...> ... </head> While Chrome and Edge, including META SEO inspector show all title and meta from index.html and components, browsers display only the first title in the order of discovery.
Solution:
<title> and <meta> of index.html to bottomReasons:
<head> is no longer mandatory in HTML5 whom was released in 2014<title> and <meta> can be placed anywhere<title>, first discovered will be used for display so component/page’s shows<meta>, all are used.With one simple adjustment inside of index.html, Wa-La, all pages display the corresponding title. No need to introduce any new component/package and update hundreds of files, and SEO 100!
BTW, this is not just for Angular, apply to all others including .NET, Vue and React.