angular

Add google analytics to your Angular SPA app in a few simple steps

Reading Time: 5 minutes

Do you have a SPA (Single Page Application) and you want to add google analytics but can’t figure out how to make it work?  Well then you’ve come to the right place.

UPDATED:

The code for this article has been update.  After you read this you may want to check out the updated version here

For a little backdrop, you might wonder why you’d want to add analytics to your SPA app.  It’s not just about seo traffic but what’s happening on your App.  You may have some custom code tracking when user are logged in or out, but you might not know which component is most often used, or how many active users you have right now.  Sure, you could add all that tracking in yourself but that’s a lot of extra grunt work and quite often that leads to – ‘I’ll do it later’.  How would you like to add google analytics to your SPA app in less than 1 hour?  Sounds good, so let’s begin.

The first thing you need to know is that analytics was originally designed with page loads in mind.  Every time you clicked / loaded a new page, the ga (google analytic) scripts would load and log the information.  Angular SPA apps don’t follow this same paradigm.  With a SPA app you have the main page (the single page) loaded once, then your components are dynamically swapped in and out.  This all happens without a full page load.  If you added ga to your main page it would only log once, when the main page loads or if you manually refreshed it.

What we need to do, is load the ga scripts into the main page as you would normally do.  This makes the scripts accessible to all your components.  The next part is where we differ from the ‘standard’ logging.  Now we need to track the action when each route is loaded, which will trigger the page view action of logging to google analytics.  Ideally we don’t want to replicate this code into each component, so it would be nice the main app.component would take care of this for us.

Quick Guide: Get the source code

All the code here can be found on my github repo at: https://github.com/eric-wilson/angular-google-analytics-starter

Simply pull the repo down and cherry pick the necessary code.  The readme.md explains the basic structure or you can follow the steps below.  I’d suggest at least reading the steps below since they have more detail than the readme.md

Once you clone the repo do the following:

  1. npm install
  2. npm start
  3. Once the browser loads, open the browsers the debugger tools and view the network or the console
  4. Click on some of the menu options, you’ll see the scripts being logged to google analytics on the network and some basic messaging in the console.

Step 1. – Create an analytics account if needed

If you don’t already have a google analytics account you will need to create one.  Then set up the site you will track.  This will provide you with you UA-id (your tracking id).

 

Step 2. – Get the latest ga scripts

Get the required scripts from google analytics developer guides

https://developers.google.com/analytics/devguides/collection/analyticsjs/

I prefer to use the async / non-blocking script which is towards the middle or end of the page.  Copy and paste that into index.html file.  You can also add it to a .js file with a little modifications (see the github repo with an example).

If using a script file, just be sure that you’ve added to webpack, glup or whatever blunder you use.  For this sample and the one on github I added it to the angular-cli.json

 "scripts": [ "assets/js/google-analytics.js"

 

Step 3. Create your angular service

Create an angular service.  You can do this by hand or use the angular cli command: ng g s /pathToYourService for example mine was:

ng g s services/goolge/analytics/google-analytics

The command above is a simplified version of: ng generate service /path

The service is really basic.  Inject the router via dependency injection in the constructor, so we can use it in our subscribe() & unsubscribe() method.  Add a class level variable of type Subscription and make the service injectable.  If your editor is nice enough it will automatically add the imports, but if not you will need the following:

import { Injectable } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { Router, NavigationEnd } from '@angular/router';

In the subscribe method will be calling the ga (google analytic function) that’s not directly listed in this code.  The compiler may give you an error and for clarity it’s best if you declare the variable so that typescript knows to ignore it here and that it will be defined else where.

// --- use a declare to allow the compiler find the ga function
declare let ga: Function;

Now for the cool part.  When a calling component uses our class it can call the subscribe method to “subscribe” or wire up the event structure.  This will basically do the following

  1. It will look to see if you’ve already subscribed to it, there’s no need to add extra events.  We don’t want them doubling or tripling up, etc
  2. If no current subscription then will use the router (that we injected) and subscribe to it’s events.
  3. We’ll use an if statement to make sure we only care about events that are an instance of NavigationEnd.  This event type fires a notice when the navigation has completed.
  4. If all those conditions are met then we send off the normal ga(‘set’, ‘page’ e.urlAfterRedirects); and the ga(‘send’, ‘pageview’);
  5. This routine is registered with the router events and will fire off the event every time the navigation changes.
  6. The only way it will stop is if we unsubscribe, which we’ll also do

Note:

In #4 above, I mentioned the more ga(‘set’, ‘page’ …) routine as “normal” but you probably won’t recognize the e.urlAfterRedirects.  That’s because it’s part of the ‘e’ parameter .subscribe( e => … ).  Here we’re using the event object and acquiring the value in the urlAfterRedirects which should be the final url which is our components route.  Ta Da!  We just snagged the event and we’re sending a page event notice to google analytics.  But we’re not quite done yet – don’t skip Step 4.

For clarity the full code block for the service can be see below, which can also be found at https://github.com/eric-wilson/…/analytics/google-analytics.service.ts

import { Injectable } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { Router, NavigationEnd } from '@angular/router';

// --- use a declare to allow the compiler find the ga function
declare let ga: Function;

// notes you may need to do this as well: npm install --save-dev @types/google.analytics
@Injectable()


export class GoogleAnalyticsService {
  private subscription: Subscription;

  constructor(private router: Router) {  }


  public subscribe() {
    if (!this.subscription) {
      // subscribe to any router navigation and once it ends, write out the google script notices
      this.subscription = this.router.events.subscribe( e => {
        if (e instanceof NavigationEnd) {
          // this will find & use the ga function pulled via the google scripts
          try {
            ga('set', 'page', e.urlAfterRedirects);
            ga('send', 'pageview');
            console.log(`logging: ${e.urlAfterRedirects} to google analytics`);
          } catch {
             console.log('tracking not found - make sure you installed the scripts');
          }
        }
      });
    }
  }

  public unsubscribe() {
    if (this.subscription) {
      // --- clear our observable subscription
      this.subscription.unsubscribe();
    }
  }

}

 

Step 4.  Add it to your main app.component.

This is assuming that your <router-outlet> tags are located on this component.  If not add it to the root component where your routing exists.

export class AppComponent implements OnInit, OnDestroy {
  title = 'Google GA with an Angular SPA App';
  // Creates an instance of AppComponent and inject a router service we'll use in the component.
  constructor(private googleAnalyticsService: GoogleAnalyticsService) {  }

  ngOnInit() {
    // subscribe to the ga posts
    this.googleAnalyticsService.subscribe();
  }
  ngOnDestroy() {
    // unsubscribe to the post
    this.googleAnalyticsService.unsubscribe();
  }
}

 

That’s it, you now have ‘pageview’ or in this case component routing views being logged to google analytics.

Bonus: Custom Events

You can also track custom events.  See the same github project for logging custom events.  The custom event method is in the GoogleAnalyticsService in the github project.  It was exclude here to keep things as simple as possible.

Updated Content & Code

I recently updated the code and content to use the environment variables for customization.  You can see the updated content in the Updated: Angular Spa App with Google Analytics

 

Happy Tracking!