how to change page title in angular2 router
Asked Answered
B

15

60

I am trying to change the page title from the router, can this be done?

import {RouteConfig} from 'angular2/router';
@RouteConfig([
  {path: '/home', component: HomeCmp, name: 'HomeCmp' }
])
class MyApp {}
Beamends answered 5/1, 2016 at 1:26 Comment(5)
Use the Title service. I know there's an answer over there but can't find it.Brien
New link to Title service: angular.io/docs/ts/latest/api/platform-browser/index/…Gentry
See also #38644814Paregoric
I had same problem and the best answer I found was confirmed answer for this question: #38644814Navarre
New link to Title service angular.io/guide/set-document-titleCastle
C
60

The Title service @EricMartinez points out has a setTitle() method - that's all you need to set the title.

In terms of doing it automatically on route changes, as of now there's no built-in way of doing this other than subscribing to Router and calling setTitle() in your callback:

import {RouteConfig} from 'angular2/router';
import {Title} from 'angular2/platform/browser';

@RouteConfig([
  {path: '/home', component: HomeCmp, name: 'HomeCmp' }
])
class MyApp {
    constructor(router:Router, title:Title) {
       router.events.subscribe((event)=>{ //fires on every URL change
          title.setTitle(getTitleFor(router.url));
       });
    }
 }

That said, I emphasize as of now because the router is still under heavy development, and I expect (or at least hope) that we'll be able to do this via RouteConfig in the final release.

EDIT:

As of the release of Angular 2 (2.0.0), a few things have changed:

Comeback answered 5/1, 2016 at 5:31 Comment(4)
Also router.subscribe is now router.events.subscribe in Angular2Cavin
getTitleFor(url) this method returns what ? i.e value of name in router config ?Extranuclear
If I'm reading the docs correctly, getTitleFor() is no longer a thing.Pertain
I found this answer to be a bit more detailedEvers
J
18

Here's my approach which works fine, especially for nested routes:

I use a recursive helper method to grab the deepest available title after a route has changed:

@Component({
  selector: 'app',
  template: `
    <h1>{{title | async}}</h1>
    <router-outlet></router-outlet>
  `
})
export class AppComponent {
  constructor(private router: Router) {
    this.title = this.router.events
      .filter((event) => event instanceof NavigationEnd)
      .map(() => this.getDeepestTitle(this.router.routerState.snapshot.root));
  }

  title: Observable<string>;

  private getDeepestTitle(routeSnapshot: ActivatedRouteSnapshot) {
    var title = routeSnapshot.data ? routeSnapshot.data['title'] : '';
    if (routeSnapshot.firstChild) {
      title = this.getDeepestTitle(routeSnapshot.firstChild) || title;
    }
    return title;
  }
}

This is assuming, that you have assigned page titles within the data property of your routes, like this:

{
  path: 'example',
  component: ExampleComponent,
  data: {
    title: 'Some Page'
  }
}
Jollenta answered 7/11, 2016 at 14:59 Comment(2)
I was looking for the data property at router.routeSnapshot.root.data, but it appears that it can be nested based on how the route is setup. This answer worked for me.Puppet
I had to add this.title = this.getDeepestTitle(this.activatedRoute.snapshot.root); for this to work on the first visit.Quillen
D
13

For Angular 4+:

If you want to use route custom data to define page title for every route path, the following approach will work for the nested routes and with angular version 4+:

You can pass page title in your route definition:

  {path: 'home', component: DashboardComponent, data: {title: 'Home Pag'}},
  {path: 'about', component: AboutUsComponent, data: {title: 'About Us Page'}},
  {path: 'contact', component: ContactUsComponent, data: {title: 'Contact Us Pag'}},

Now, most important in your upper level component (ie AppComponent), you can globally catch the route custom data on every route change and update the page title:

    import {Title} from "@angular/platform-browser";
    import { filter, map } from 'rxjs/operators';
    export class AppComponent implements OnInit {

        constructor(
            private activatedRoute: ActivatedRoute, 
            private router: Router, 
            private titleService: Title
        ) { }

  ngOnInit() {
    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( (title: any) => {
      this.titleService.setTitle(title);
  });
}

The above code is tested against angular verion 4+.

Disseminule answered 11/10, 2017 at 21:42 Comment(1)
Great! Just some more additions. Be sure to add import { BrowserModule, Title } from '@angular/platform-browser'; and set the providers: [Title] in the Module, and also have to add import 'rxjs/add/operator/filter'; import 'rxjs/add/operator/map'; in the Component. Otherwise you can get some errors.Endodontics
E
11

Its really very easy to do this, you can follow therse steps to see the immediate effects:

we provide the Title service in bootstrap:

import { NgModule } from '@angular/core';
import { BrowserModule, Title }  from '@angular/platform-browser';

import { AppComponent } from './app.component';

@NgModule({
  imports: [
    BrowserModule
  ],
  declarations: [
    AppComponent
  ],
  providers: [
    Title
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Then import this service in the component you want:

import { Component } from '@angular/core';
import { Title }     from '@angular/platform-browser';

@Component({
selector: 'my-app',
template:
  `<p>
    Select a title to set on the current HTML document:
  </p>

  <ul>
    <li><a (click)="setTitle( 'Good morning!' )">Good morning</a>.</li>
    <li><a (click)="setTitle( 'Good afternoon!' )">Good afternoon</a>.</li>
    <li><a (click)="setTitle( 'Good evening!' )">Good evening</a>.</li>
  </ul>
  `
})
export class AppComponent {
  public constructor(private titleService: Title ) { }

  public setTitle( newTitle: string) {
    this.titleService.setTitle( newTitle );
  }
}

now click on those links to see the title changing.

you can also use ng2-meta for changing page title and description as well,you can refer to this link:

https://github.com/vinaygopinath/ng2-meta

Elisabetta answered 31/1, 2017 at 6:53 Comment(3)
yeah superb answer, using this we can change title and description as well , No need to use Title service of angular2 extra in your code, thanks for answer.Mcgannon
@PardeepJain Will this solution work even though my app takes 4 seconds to load? I've used ng2-meta and that doesn't work because of my app loading time. (I've tried everything to reduce the loading time)Freewill
What if user enter the page url and press enter OR Refresh the page, will it work. I can see you copy the same code from document :) angular.io/docs/ts/latest/cookbook/set-document-title.htmlIncipit
I
5

Angular 2 provides a Title Service see Shailesh answer is just copy of that code.

I our app.module.ts

import { BrowserModule, Title } from '@angular/platform-browser';
........
providers: [..., Title],
bootstrap: [AppComponent]

Now move to our app.component.ts

import { Title }     from '@angular/platform-browser';
......
export class AppComponent {

    public constructor(private titleService: Title ) { }

    public setTitle( newTitle: string) {
      this.titleService.setTitle( newTitle );
    }
}

Put the title tag on your component html and it will read and set for you.

If you want to know how to set it dynamically and further detail see this article

Incipit answered 14/4, 2017 at 1:28 Comment(0)
E
3
import {Title} from "@angular/platform-browser"; 
@Component({
  selector: 'app',
  templateUrl: './app.component.html',
  providers : [Title]
})

export class AppComponent implements {
   constructor( private title: Title) { 
     this.title.setTitle('page title changed');
   }
}
Electrophorus answered 31/1, 2017 at 7:32 Comment(0)
A
3

This is what I went with:

constructor(private router: Router, private title: Title) { }

ngOnInit() {
    this.router.events.subscribe(event => {
        if (event instanceof NavigationEnd) {
            this.title.setTitle(this.recursivelyGenerateTitle(this.router.routerState.snapshot.root).join(' - '));
        }
    });
}

recursivelyGenerateTitle(snapshot: ActivatedRouteSnapshot) {
    var titleParts = <string[]>[];
    if (snapshot) {
        if (snapshot.firstChild) {
            titleParts = titleParts.concat(this.recursivelyGenerateTitle(snapshot.firstChild));
        }

        if (snapshot.data['title']) {
            titleParts.push(snapshot.data['title']);
        }
    }

    return titleParts;
}
Actaeon answered 2/3, 2017 at 22:19 Comment(2)
Fantastic, only tweak I made was to ensure a title on the HTML document and tweaked this line of your code (note the reverse()), this.title.setTitle(this.title.getTitle().split(' - ')[0] + ' - ' + this.recursivelyGenerateTitle(this.router.routerState.snapshot.root).reverse().join(' - '));.Garrett
Or alternatively, you could use unshift() instead of push() and then reverse().Garrett
G
3

Angular 6+ I have modify the old code using new Pipe() and its working fine.

import { Title } from '@angular/platform-browser';
import { filter, map, mergeMap } from 'rxjs/operators';

...

constructor(
    private router: Router,
    public activatedRoute: ActivatedRoute,
    public titleService: Title,
  ) {
    this.setTitle();
  }

....

setTitle() {
  this.router.events.pipe(
    filter((event) => event instanceof NavigationEnd),
    map(() => this.activatedRoute),
    map((route: any) => {
      while (route.firstChild) route = route.firstChild;
      return route;
    }),
    filter((route) => route.outlet === 'primary'),
    mergeMap((route: any) => route.data)).subscribe((event) => {
      this.titleService.setTitle(event['title']);
      console.log('Page Title', event['title']);
    })
  }
Guilford answered 4/6, 2018 at 9:34 Comment(0)
H
2

Here's the simplest way to change the Title of the page, when pages/views are navigated (Tested as of Angular @2.3.1). Simply apply the following solution to all the views you have and you should be good to go:

Example Code in About Us page/view:

import {Title} from "@angular/platform-browser";

export class AboutUsComponent implements OnInit {

  constructor(private _titleService: Title) {
  }

  ngOnInit() {
    //Set page Title when this view is initialized
    this._titleService.setTitle('About Us');
  }

}

Example Code in Contact Us page/view:

import {Title} from "@angular/platform-browser";

export class ContactusComponent implements OnInit {

  constructor(private _titleService: Title) {
  }

  ngOnInit() {
    //Set page Title
    this._titleService.setTitle('Contact Us');
  }

}
Hemihedral answered 8/5, 2017 at 7:7 Comment(1)
I strongly recommend this answerGeryon
A
2

In the below example:

-We added object of data: { title: 'NAME' } to any routing object.

-We set a basic name ("CTM") for uploading time (when clicking F5 for Refreash..): <title>CTM</title>.

-We added "TitleService" class.

-we handle Routher events by filtering them from app.component.ts.

index.html:

<!DOCTYPE html>
<html>
  <head>
    <base href="/">
    <title>CTM</title>
  </head>

...

app.module.ts:

import { NgModule, enableProdMode } from '@angular/core';
import { BrowserModule  } from '@angular/platform-browser';
import { TitleService }   from './shared/title.service';
...


@NgModule({
  imports: [
    BrowserModule,
..
  ],
  declarations: [
      AppComponent,
...
  ],
  providers: [
      TitleService,
...
  ],
  bootstrap: [AppComponent],
})
export class AppModule { }

enableProdMode();

title.service.ts:

import { Injectable } from '@angular/core';
import { Title }  from '@angular/platform-browser';
import { ActivatedRouteSnapshot } from '@angular/router';


@Injectable()
export class TitleService extends Title {

    constructor() {
        super();
    }


    private recursivelyGenerateTitle(snapshot: ActivatedRouteSnapshot) {
        var titleParts = <string[]>[];
        if (snapshot) {
            if (snapshot.firstChild) {
                titleParts = this.recursivelyGenerateTitle(snapshot.firstChild);
            }

            if (snapshot.data['title']) {
                titleParts.push(snapshot.data['title']);
            }
        }

        return titleParts;
    }

    public CTMGenerateTitle(snapshot: ActivatedRouteSnapshot) {
        this.setTitle("CTM | " + this.recursivelyGenerateTitle(snapshot).join(' - '));
    }

}

app-routing.module.ts:

import { Injectable } from '@angular/core';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { MainComponent } from './components/main.component';

import { Router, CanActivate } from '@angular/router';
import { AuthGuard }          from './shared/auth/auth-guard.service';
import { AuthService }    from './shared/auth/auth.service';


export const routes: Routes = [
  { path: 'dashboard', component: MainComponent, canActivate: [AuthGuard], data: { title: 'Main' } },
];

@NgModule({
    imports: [
        RouterModule.forRoot(routes, { useHash: true })  // .../#/crisis-center/
    ],
    exports: [RouterModule],
    providers: [
..
    ]
})

export class AppRoutingModule { }

export const componentsOfAppRoutingModule = [MainComponent];

app.component.ts:

import { Component } from '@angular/core';
import { Router, NavigationEnd, ActivatedRouteSnapshot } from '@angular/router';
import { TitleService } from './shared/title.service';

@Component({
  selector: 'ctm-app',
  template: `
    <!--<a [routerLink]="['/dashboard']">Dashboard</a>
    <a [routerLink]="['/login']">Login</a>
    <a [routerLink]="['/']">Rooting</a>-->
    <router-outlet></router-outlet>
    `
})
export class AppComponent {

    constructor(private router: Router, private titleService: TitleService) {

        this.router.events.filter((event) => event instanceof NavigationEnd).subscribe((event) => {
            console.log("AppComponent.constructor: Setting HTML document's Title");
            this.titleService.CTMGenerateTitle(this.router.routerState.snapshot.root);
        });

    }


}
Abstinence answered 12/6, 2017 at 9:58 Comment(0)
L
2

Working fine in Angular 6 and 6+ with Pipe and map method instead of using filter

Step1: routing setup

{path: 'dashboard', component: DashboardComponent, data: {title: 'My Dashboard'}},
{path: 'aboutUs', component: AboutUsComponent, data: {title: 'About Us'}},
{path: 'contactUs', component: ContactUsComponent, data: {title: 'Contact Us Page'}},

step2: in your app.module.ts import module

import { BrowserModule, Title } from '@angular/platform-browser';

then in provider add providers: [title]

Step 3 In your main component import

import { Title } from "@angular/platform-browser";
import { RouterModule, ActivatedRoute, Router, NavigationEnd } from "@angular/router";
import { filter, map } from 'rxjs/operators';

constructor(private titleService: Title, private router: Router, private activatedRoute: ActivatedRoute) {

    }

ngOnInit() {

    this.router.events.pipe(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(title => {
        this.titleService.setTitle(title);
    });

}
Lewallen answered 4/1, 2019 at 5:33 Comment(0)
F
0

I can also recommend @ngx-meta/core plugin plugin that I've just released, in the case if you're looking for a method to set page title and meta tags dynamically.

Farmelo answered 11/11, 2016 at 8:48 Comment(0)
G
0

Angular 6+

if route configured as follow :-

Routes = [
     {  path: 'dashboard',
       component: DashboardComponent,
       data: {title: 'Dashboard'}
   }]

**Then in component constructor title can be set as follow :- **

 constructor( private _titleService: Title, public activatedRoute: ActivatedRoute) {
    activatedRoute.data.pipe(map(data => data.title)).subscribe(x => this._titleService.setTitle(x));
   }
Gracegraceful answered 25/6, 2018 at 11:30 Comment(0)
B
0

Simple generic way to set the title:

import { Component } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {

  constructor(
    private activatedRoute: ActivatedRoute,
    private router: Router,
    private titleService: Title
    ) {}

  ngOnInit() {
    this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        const { title } = this.activatedRoute.firstChild.snapshot.data;
        this.titleService.setTitle(title);
      }
    });
  }

}

where title needs to be set on each route like:

{ path: '', component: WelcomeComponent, data: {title: 'Welcome to my app'} }
Baal answered 1/3, 2020 at 17:27 Comment(0)
L
0

Simple, If you are using Angular Version greater than 14.0 you can simply put title in Routes array, Like -

const routes: Routes = [
  {
path: 'home',
component: HomeComponent,
title: "'My App - Home' // <-- Page title"
  },
  {
path: 'about',
component: AboutComponent,
title: "'My App - About Me'  // <-- Page title"
  }
];
Lamellicorn answered 23/11, 2022 at 8:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.