Search code examples
angulargoogle-analyticsangular-routingpage-title

Dynamic Page Title for both browser display and Google Analytics in Angular


The question is:

How to dynamically and centrally set up Page Titles in Angular App only once for two purposes:

  • to make a title in a browser reflect an actual page visited,
  • to send a particular title to Google Analytics avoiding a boring, single and useless Angular App description applied to all app's pages.

Solution

  • My current solution

    I'v made use of two nice snippets found on Github:

    • first allows for defining Page Title centrally in Router config, just for visual purposes,
    • second provides a solution for sending navigation events to Google Analytics, while within a Single Page Application - in an automatic centralised way or manually. It's received an inspiring ticket commented by amarnathm.

    Well, the ideas presented in those two including the discussion on the issue reported to the latter may be integrated in this way:

    app.routing-module.ts:

    const routes: Routes = [ 
      { path: 'site/contact', component: ContactComponent, data: {title: 'Contact'}},
      ...
      // other paths
    ];
    

    app.module.ts:

    import {BrowserModule} from '@angular/platform-browser';
    import {TitleService} from "./title.service";
    import {GtagModule} from "angular-gtag";
    // ...
    providers: [
        // ...
        TitleService,
        GtagModule.forRoot({ trackingId: 'MY-UA-CODE', trackPageviews: false })        
        // ...   
      ]
    // ...
    

    trackPageviews: false, as proposed by amarnathm, is important as we'll track manually per page. Standard sample code by Jeff defaults to true

    title.service.ts:

    import { Injectable } from '@angular/core';
    import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
    import { Title } from '@angular/platform-browser';
    import { filter, map, switchMap } from 'rxjs/operators';
    
    @Injectable({
      providedIn: 'root'
    })
    export class TitleService {
      default_title = 'My App';
    
      constructor(
        private router: Router,
        private activeRoute: ActivatedRoute,
        private title: Title,
        private gtag: Gtag,
        private env: Environment
      ) { }
    
      boot() {
        this.router.events.pipe(
          filter(event => event instanceof NavigationEnd),
          map(() => this.activeRoute),
          map(route => route.firstChild),
          switchMap(route => route.data),
          map((data) => {
    
            //here goes the GA reporting code            
    
            this.gtag.pageview({
              page_title: data && data.title ? data.title : this.default_title,
              page_path: this.router.url,
              page_location: this.env.host + this.router.url
            });
    
            return data && data.title ? `${data.title} • ${this.default_title}` : this.default_title;
          })
        ).subscribe((current_title) => this.title.setTitle(current_title));
      }
    }
    

    app.component.ts:

    import { Component, OnInit } from '@angular/core';
    import { TitleService } from './title.service';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styles: [],
    })
    export class AppComponent implements OnInit {
    
      constructor(private titleService: TitleService) { }
    
      ngOnInit() {
        this.titleService.boot();
      }
    }
    

    Get rid of any GA/Gtag code you may have in your index.html file. This allows for customised subscription to GA services without relying on the automatic tracking we won't use. In SPA index.html is loaded usually once and without page tracking set on is useless.

    Enhancement proposal:

    I'v got no answer for:

    How to make use of ActivatedRoute parameters and map them to detailed titles used then in Analytics and optionally - for displaying per parameter titles in a browser.