Skip to content

Instantly share code, notes, and snippets.

@katowulf
Last active February 3, 2021 15:09
Show Gist options
  • Save katowulf/2145adce2423d1b2f984555a6c1e87fb to your computer and use it in GitHub Desktop.
Save katowulf/2145adce2423d1b2f984555a6c1e87fb to your computer and use it in GitHub Desktop.
Dynamically set page title based on active route in Angular 4
// This can probably be simplified somehow. Not sure why I need to add it in the component to init the service.
import { Component, OnInit } from '@angular/core';
import {TitleService} from "./@core/utils/title.service";
@Component({...})
export class AppComponent implements OnInit {
constructor(private titleService: TitleService) {...}
ngOnInit(): void {
this.titleService.init();
}
}
import ...
@NgModule({
...
providers: [ TitleService ],
})
export class AppModule { ... }
// If a route is declared with a title attribute, it gets used, otherwise we just try to parse the url fragment
// Here's where the title attribute goes
const routes: Routes = [{
path: '',
component: HomeComponent,
data: {title: "My Home Page"},
}, {
path: 'detail/:id',
component: DetailComponent,
}];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ExampleRoutingModule {
}
// Credits and thanks:
// https://toddmotto.com/dynamic-page-titles-angular-2-router-events
// https://stackoverflow.com/questions/34597835/how-to-get-current-route
import { Injectable } from '@angular/core';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser';
const APP_TITLE = 'NoDice!';
const SEPARATOR = ' > ';
@Injectable()
export class TitleService {
constructor(
private router: Router,
private activatedRoute: ActivatedRoute,
private titleService: Title,
) {}
init() {
this.router.events
.filter((event) => event instanceof NavigationEnd)
.map(() => {
let route = this.activatedRoute;
while (route.firstChild) route = route.firstChild;
return route;
})
.filter((route) => route.outlet === 'primary')
.mergeMap((route) => route.data)
.map((data) => {
if ( data.title ) {
// If a route has a title set (e.g. data: {title: "Foo"}) then we use it
return data.title;
} else {
// If not, we do a little magic on the url to create an approximation
return this.router.url.split('/').reduce((acc, frag) => {
if ( acc && frag ) { acc += SEPARATOR; }
return acc + TitleService.ucFirst(frag);
});
}
})
.subscribe((pathString) => this.titleService.setTitle(`${APP_TITLE} ${pathString}`));
}
static ucFirst(string) {
if ( !string ) { return string; }
return string.charAt(0).toUpperCase() + string.slice(1);
}
}
@Gabriel10Velloso
Copy link

Very good

Copy link

ghost commented Feb 7, 2019

working fine,thank You

@meness
Copy link

meness commented May 14, 2019

Thanks. I made a few changes to have more conceptual titles.

import { Injectable } from '@angular/core';

import { map, filter } from 'rxjs/operators';

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

const APP_TITLE = 'Title';
const SEPARATOR = ' > ';

@Injectable({
  providedIn: 'root'
})
export class TitleService {
  constructor(
    private router: Router,
    private activatedRoute: ActivatedRoute,
    private titleService: Title,
  ) { }

  static ucFirst(text: string) {
    if (!text) { return text; }
    return text.charAt(0).toUpperCase() + text.slice(1);
  }

  init() {
    this.router.events.pipe(
      filter((event) => event instanceof NavigationEnd),
      map(() => {
        let route = this.activatedRoute;
        while (route.firstChild) { route = route.firstChild; }
        return route;
      }),
      filter((route) => route.outlet === 'primary'),
      map((route) => route.snapshot),
      map((snapshot) => {
        if (snapshot.data.title) {
          if (snapshot.paramMap.get('id') !== null) {
            return snapshot.data.title + SEPARATOR + snapshot.paramMap.get('id');
          }
          return snapshot.data.title;
        } else {
          // If not, we do a little magic on the url to create an approximation
          return this.router.url.split('/').reduce((acc, frag) => {
            if (acc && frag) { acc += SEPARATOR; }
            return acc + TitleService.ucFirst(frag);
          });
        }
      }))
      .subscribe((pathString) => this.titleService.setTitle(`${pathString} - ${APP_TITLE}`));
  }
}

@guibleme
Copy link

guibleme commented Nov 20, 2019

Just leaving a comment on how this thing saved me today.
Kudos for the op @katowulf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment