How to change page title with routing in Angular application?

AngularAngular5

Angular Problem Overview


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.

Angular Solutions


Solution 1 - Angular

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

Solution 2 - Angular

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'
        }
    }
];

Solution 3 - Angular

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' }
          }
        ]
      }
    ]
  }
];

Solution 4 - Angular

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);
    }
}

Solution 5 - Angular

// 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'}
       }
    ];

Solution 6 - Angular

In 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

Solution 7 - Angular

For make dynamic changing title we can used angular module @angular/platform-browser and set title used function setTitle function.

For more details please check : platform-browser

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPeterView Question on Stackoverflow
Solution 1 - AngularJohnView Answer on Stackoverflow
Solution 2 - AngularAnkurView Answer on Stackoverflow
Solution 3 - AngularElias RablView Answer on Stackoverflow
Solution 4 - AngularbunjeebView Answer on Stackoverflow
Solution 5 - AngularPavanView Answer on Stackoverflow
Solution 6 - AngularFelixView Answer on Stackoverflow
Solution 7 - Angularuser597View Answer on Stackoverflow