Angular 4+ using Google Analytics

AngularTypescriptGoogle AnalyticsTypescript Typings

Angular Problem Overview


I'm trying to use the Google Analytics with angular 4, but i can't find any @type to ga.js in ts.

For a quick solution I used this in every component:

declare let ga: any;

Following how I resolved it:

Create a function to load the GA dynamically that inserts the GA script with current trackingId and user.

    loadGA(userId) {
        if (!environment.GAtrackingId) return;

        let scriptId = 'google-analytics';

        if (document.getElementById(scriptId)) {
            return;
        }

        var s = document.createElement('script') as any;
        s.type = "text/javascript";
        s.id = scriptId;
        s.innerText = "(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga');ga('create', { trackingId: '" + **environment.GAtrackingId** + "', cookieDomain: 'auto', userId: '" + **userId** + "'});ga('send', 'pageview', '/');";

        document.getElementsByTagName("head")[0].appendChild(s);
    }

Create the service to implement the methods that you will need.

import { Injectable } from '@angular/core';
import { environment } from '../../../environments/environment';

declare let ga: any;

@Injectable()
export class GAService {
    constructor() {
    }
    
    /**
     * Checks if the GA script was loaded.
     */
    private useGA() : boolean { 
        return environment.GAtrackingId && typeof ga !== undefined;
    }
    
    /**
     * Sends the page view to GA.
     * @param  {string} page The path portion of a URL. This value should start with a slash (/) character.
     */
    sendPageView(
        page: string
    ) {
        if (!this.useGA()) return;
        if (!page.startsWith('/')) page = `/${page}`;      
        ga('send', 'pageview', page);
    }


    /**
     * Sends the event to GA.
     * @param  {string} eventCategory Typically the object that was interacted with (e.g. 'Video')
     * @param  {string} eventAction The type of interaction (e.g. 'play')
     */
    sendEvent(
        eventCategory: string,
        eventAction: string
    ) { 
        if (!this.useGA()) return;
        ga('send', 'event', eventCategory, eventAction);
    }
}

Then I finally use the service injected in component.

constructor(private ga: GAService) {}

ngOnInit() { this.ga.sendPageView('/join'); }

Angular Solutions


Solution 1 - Angular

First of all, you need to install typings for Google Analytics in your devDependencies

npm install --save-dev @types/google.analytics

Then add your tracking code in the base index.html, and remove the last line as shown bellow:

<body>
  <app-root>Loading...</app-root>
  <script>
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
 
    ga('create', 'UA-XXXXXX-ID', 'auto');  // <- add the UA-ID 
                                           // <- remove the last line 
  </script>
</body>

The next step consists to update your home component constructor for event tracking.

constructor(public router: Router) {
    this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        ga('set', 'page', event.urlAfterRedirects);
        ga('send', 'pageview');
      }
    });
  }

If you want to track some specific event, you can also create a service and inject it into any component that you want to implement event tracking.

// ./src/app/services/google-analytics-events-service.ts

import {Injectable} from "@angular/core";
 
@Injectable()
export class GoogleAnalyticsEventsService {
 
  public emitEvent(eventCategory: string,
                   eventAction: string,
                   eventLabel: string = null,
                   eventValue: number = null) {
    ga('send', 'event', { eventCategory, eventLabel, eventAction, eventValue });
  }
}

So if you want track a click on your home component for example, all you need to do is to inject the GoogleAnalyticsEventsService and call the emitEvent() method.

The updated home component source code:

constructor(public router: Router, public googleAnalyticsEventsService: GoogleAnalyticsEventsService) {
    this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        ga('set', 'page', event.urlAfterRedirects);
        ga('send', 'pageview');
      }
    });
  }
  submitEvent() { // event fired from home.component.html element (button, link, ... )
    this.googleAnalyticsEventsService.emitEvent("testCategory", "testAction", "testLabel", 10);
  }

Solution 2 - Angular

I'm surprised nobody here mentioned Google's Tag Manager yet (which is the version of the script that the Google Analytics console outputs for me in the last few years, whenever I add a new identity).

Here's a solution that I came up with today, which is a variation of the solutions already mentioned in the other answers, adapter to Google's Tag Manager script. I think it would be useful for people who migrated from ga() to gtag() (a migration that is recommended as far as I know).

analytics.service.ts

declare var gtag: Function;

@Injectable({
  providedIn: 'root'
})
export class AnalyticsService {

  constructor(private router: Router) {

  }

  public event(eventName: string, params: {}) {
    gtag('event', eventName, params);
  }

  public init() {
    this.listenForRouteChanges();

    try {

      const script1 = document.createElement('script');
      script1.async = true;
      script1.src = 'https://www.googletagmanager.com/gtag/js?id=' + environment.googleAnalyticsKey;
      document.head.appendChild(script1);

      const script2 = document.createElement('script');
      script2.innerHTML = `
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', '` + environment.googleAnalyticsKey + `', {'send_page_view': false});
      `;
      document.head.appendChild(script2);
    } catch (ex) {
      console.error('Error appending google analytics');
      console.error(ex);
    }
  }

  private listenForRouteChanges() {
    this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        gtag('config', environment.googleAnalyticsKey, {
          'page_path': event.urlAfterRedirects,
        });
        console.log('Sending Google Analytics hit for route', event.urlAfterRedirects);
        console.log('Property ID', environment.googleAnalyticsKey);
      }
    });
  }
}

Prerequisites:

  • Declare the service in the imports[] section of your app.module.ts.

  • In your app.component.ts (or whichever higher level component holds the <router-outlet> tag in its template), inject the AnalyticsService and call this.analytics.init() as early as possible (e.g. ngOnInit)

  • In the environment.ts (in my case - environment.prod.ts), add the Analytics ID as googleAnalyticsKey: 'UA-XXXXXXX-XXXX'

Solution 3 - Angular

Load google analytics, using environment vars, the async way;

(Works on Angular 5)

(Using @Laiso answer)

google-analytics.service.ts
import {Injectable} from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';
declare var ga: Function;

@Injectable()
export class GoogleAnalyticsService {

  constructor(public router: Router) {
    this.router.events.subscribe(event => {
      try {
        if (typeof ga === 'function') {
          if (event instanceof NavigationEnd) {
            ga('set', 'page', event.urlAfterRedirects);
            ga('send', 'pageview');
            console.log('%%% Google Analytics page view event %%%');
          }
        }
      } catch (e) {
        console.log(e);
      }
    });

  }


  /**
   * Emit google analytics event
   * Fire event example:
   * this.emitEvent("testCategory", "testAction", "testLabel", 10);
   * @param {string} eventCategory
   * @param {string} eventAction
   * @param {string} eventLabel
   * @param {number} eventValue
   */
  public emitEvent(eventCategory: string,
   eventAction: string,
   eventLabel: string = null,
   eventValue: number = null) {
    if (typeof ga === 'function') {
      ga('send', 'event', {
        eventCategory: eventCategory,
        eventLabel: eventLabel,
        eventAction: eventAction,
        eventValue: eventValue
      });
    }
  }


}
Inside app.component or whatever component:
 // ... import stuff

 import { environment } from '../../../environments/environment';

 // ... declarations

 constructor(private googleAnalyticsService: GoogleAnalyticsService){
    this.appendGaTrackingCode();
 }

 private appendGaTrackingCode() {
    try {
      const script = document.createElement('script');
      script.innerHTML = `
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
        })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
       
        ga('create', '` + environment.googleAnalyticsKey + `', 'auto');
      `;
      document.head.appendChild(script);
    } catch (ex) {
     console.error('Error appending google analytics');
     console.error(ex);
    }
  }

// Somewhere else we can emit a new ga event
this.googleAnalyticsService.emitEvent("testCategory", "testAction", "testLabel", 10);

Solution 4 - Angular

GoogleAnalyticsService

You can create service that subscribes for router events and inject it in app.module.ts so you don't have to inject it in every component.

@Injectable()
export class GoogleAnalyticsService {

  constructor(router: Router) {
    if (!environment.production) return; // <-- If you want to enable GA only in production
    router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        ga('set', 'page', event.url);
        ga('send', 'pageview');
      }
    })
  }

Here is tutorial (my blog).

Solution 5 - Angular

To avoid any type checking if ga is defined globally at window level then you could simply do

 window["ga"]('send', {
	hitType: 'event',
    eventCategory: 'eventCategory',
	eventAction: 'eventAction'
	});

Hope it helps.

Solution 6 - Angular

Personally I've found it quite easy simply:

  • Adding the GA tracking code after my <app-root> in index.html (as shown above)
  • Including Angulartics for GA into my application (GA Example here)

In my app.component.ts I've added this:

import {Component, OnInit} from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';
import {Angulartics2GoogleAnalytics} from 'angulartics2/ga';
import {filter} from 'rxjs/operators';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
    constructor(private ga: Angulartics2GoogleAnalytics,
                 private router: Router) {
    }

    ngOnInit() {
        this.router.events
            .pipe(filter(event => event instanceof NavigationEnd))
            .subscribe((event: NavigationEnd) =>
                this.ga.pageTrack(event.urlAfterRedirects));
    }
}

It's not much different to the above, but makes it much easier for testing.

Solution 7 - Angular

I suggest embedding the Segment script into your index.html and extend analytics library onto the window object:

declare global {
  interface Window { analytics: any; }
}

Then add tracking calls onto the (click) event handler:

@Component({
  selector: 'app-signup-btn',
  template: `
    <button (click)="trackEvent()">
      Signup with Segment today!
    </button>
  `
})
export class SignupButtonComponent {
  trackEvent() {
    window.analytics.track('User Signup');
  }
}

I’m the maintainer of https://github.com/segmentio/analytics-angular. I recommend checking it out if you want to solve this problem by using one singular API to manage your customer data, and be able to integrate into any other analytics tool (we support over 250+ destinations) - without writing any additional code. 

Solution 8 - Angular

This solution seems to be the easiest:

After installing the types with:

npm install --save-dev @types/google.analytics

Update tsconfig.json to use them:

// tsconfig.json
{
  types: ["google.analytics"]
}

Solution 9 - Angular

You might be helped

app.component.ts

declare let gtag: Function;

this.router.events.subscribe(event => {
        
    if(event instanceof NavigationEnd) {
        gtag('config', '*****', {'page_path': event.urlAfterRedirects});
    }

});

Solution 10 - Angular

index.html file

<head>
.........

    <script async src="https://www.googletagmanager.com/gtag/js?id=Google-Tracking-ID"></script>
      <script>
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
    </script>

......
</head>

AppComponent

import { Component, OnInit } from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';
import {environment} from '../environments/environment';
// tslint:disable-next-line:ban-types
declare let gtag: Function;

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

  constructor(public router: Router) {
    this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        gtag('config', Google-Tracking-ID, {'page_path': event.urlAfterRedirects});
      }
    });
  }

}

Solution 11 - Angular

Do you have include the type in "types" compilerOptions of tsconfig.app.json?

I had the same problem and solved it including "google.analytics" (see, not "@ types/google.analytics") in tsconfig.app.json

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
QuestionLeonardo OliveiraView Question on Stackoverflow
Solution 1 - AngularLaisoView Answer on Stackoverflow
Solution 2 - AngularDzhuneytView Answer on Stackoverflow
Solution 3 - AngularArtipixelView Answer on Stackoverflow
Solution 4 - AngularFilip MolcikView Answer on Stackoverflow
Solution 5 - Angularjalak voraView Answer on Stackoverflow
Solution 6 - AngularJim DruryView Answer on Stackoverflow
Solution 7 - AngularWilliamView Answer on Stackoverflow
Solution 8 - AngularedrplsView Answer on Stackoverflow
Solution 9 - AngularSiddhartha MukherjeeView Answer on Stackoverflow
Solution 10 - AngularParth kharechaView Answer on Stackoverflow
Solution 11 - AngularRicardo Azzi SilvaView Answer on Stackoverflow