July 14, 2016
Angular 2: Set up Google Analytics and Google Tag Manager
There are many modules to include Google Analytics into your Angular 2 project. But actually this overhead is not really required since a plain implementation is only a few lines of code. So you can spare extra modules.
I highly recommend you to use Google Tag Manager in order to place the tracking information on your site since it allows you to have a professional workflow. Beyond that you can enjoy the advantages of having a versioned history of your changes. Additional many things are way more easier with Google Tag Manager, for example to implement a simple cookie opt-out that affects all implemented tracking codes. If you don’t want to use Google Tag Manger see the code at the end of this article.
? Tutorial for Google Analytics with AngularJS 1
Recently we had to implement Analytics on our new site for our German TypeScript User Group ( https://www.typescriptusers.de ). The first step is to create Account and a Container on Google Tag Manager ( tagmanager.google.com ).
On confirm you will receive the tracking snippet that you should place on in your index.html. Since we rely on this code in execution time it is required that you place the code directly under the the opening <body> tag of your HTML File. It is recommended to place your custom javascript files after the implementation of GTM or use defer attribute on the script tags.
Once you have added the code to your site it is time for the first publish of your empty container. Until you publish your Tag Manager snippet will only cause an loading error. Use the publish Button on the upper right corner of GTM.
Alright. Let’s set up Google Analytics. The first thing to do is to create a property in GA and copy the UA-ID.
In GTM Tracking codes are implemented as Tags. So when you want to add Facebook’s Tracking Pixel you’d simply create a new tag for it and bind it to a Trigger (pageview, click, form submission,..) to fire on. Lets create our first tag for Analytics, fill the form as seen in the screenshot below. But keep in mind to use your own UA-ID. Leave the “Fire On” empty for now. We need to create a trigger first.
Alright our tracking snippet is now included but will never be fired since there is no trigger. On Angular 2 Sites we have no real pageview (hard reload) but only state changes that do not trigger the browser reload. This means we have to implement the trigger on our own. This is not Google Tag Manager specific but applies for all fully JavaScript driven pages! Go to GTM and create a new trigger of type History Changes. Call it “Route Change”.
GTM will now observe the HTML 5 History API. Any route change will call the trigger. Now it is time to connect the trigger to our Google Analytics Tag. Go to Tags and select the GA tag. Change the “Fire On” -> “More” -> “Route Change”. Then save the tag and publish your changes by using the publish button in the upper red corner.
This tutorial applies not only to Google Tag Manager and Google Analytics but to all tracking tools that can be connected to GTM. So you can add Piwik, Facebook, AdWords Tracking, Crazyegg, and many more without even touching your application’s source code once.
Analytics in Angular 2 (without Google Tag Manager):
It is indeed not necessary to use Tag Manager. Rather you can implement plain Google Analytics into your single page application. The initial setup may seam easier but you have to repeat this implementation for every new tracking code. Copy the full tracking code of your Analytics property and put it in your index.html above your own JavaScript ressources, then remove the last line which is responsible for the page view:
1 2 3 4 5 6 7 8 9 10 11 |
<body> <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'); ga('send', 'pageview'); <!-- <- REMOVE THIS LINE --> </script> |
MAKE SURE TO INSERT YOUR OWN UA-ID!
Now go to your app.ts and subscribe to router changes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import {Component} from '@angular/core'; import {Router, NavigationEnd} from '@angular/router'; declare let ga: Function; @Component({ selector: 'my-app', templateUrl: './app.html', providers: [], }) export class AppComponent { constructor(public router: Router) { this.router.events.subscribe(event => { if (event instanceof NavigationEnd) { ga('set', 'page', event.urlAfterRedirects); ga('send', 'pageview'); } }); } } |
Thanks very much for this! Exactly what I needed. I did have to move the function declaration to before the @Component.
Thank you for the feedback.
And you are right! I’ve updated the code snippet, thanks!
window.dataLayer not defined error is coming. do we need to any import
Hello Sujatha,
if the dataLayer is not defined the Google Tag Manager Snippet is not yet included. Add the container snippt to your index.html. You can find it at tagmanager.google.com/?hl=en then select a container and finally click on the displayed container ID in the top navbar.
This will trigger a popup with your embed code, place it in your index.html immediately after the opening
tag.Good Luck!
This code does not work in AngularJS 2 RC5. You get error TS2304 “Cannot find the name dataLayer”. Even when you add the google tagmanager code in the body, the compiler still does not have the dataLayer variable defined. I have not found the solution yet, but I do know that if you compile your typescript first, then add the code, it works.
Hello jason,
this is not a RC 5 problem but a missing TypeScript declaration and correct behavior of the compiler. TS doesn’t know weather window.dataLayer will exist or not. To tell TS to not check for the existence we use this line of code:
(<any>window).dataLayer.push({
event: ‘pageView’,
action: event.urlAfterRedirects,
});
If updated the code snippet.
A cleaner method would be to create a new interface that extends the Window element:
interface MyWindow extends Window {
dataLayer:any[];
}
declare let window:MyWindow;
window.dataLayer.push({});
Thanks for the article!
I don’t known whether Google Tag Manager was updated after you wrote it or something, but triggers now include a “History Change” trigger (see: https://support.google.com/tagmanager/answer/6106961?hl=en). According to the docs, it: “Triggers based on the History Change event will fire a tag when the URL fragment (hash) changes or when a site is using the HTML5 pushstate APIs. This trigger is useful to fire tags tracking virtual pageview in an Ajax application, for instance.”
This should do all required triggering without the need of a custom event, e.g. withough any Angular2 Code change, shouldn’t it?
Hi netmikey,
thanks for the hint, I have updated the information above! Tutorial now refers to History API . This is simpler since no code adjustment is required but this approach wont work for AngularJS 1.
Hi! This tutorial was very useful. One thing: I’m not sure if I did something wrong or it’s not meant to be logged, but is there any way to log the path that the user is visiting? All my logged visits are to /, it ignores everything after the #!
Thanks again!
Hello Vincent,
are you using Angular 1 or 2?
If you are using Angular 2 i highly recommend to remove the hashbang! It is obsolete and you should use HTML 5 history API with URLs like this ‘http://wwww.domain.com/route/deeproute’.
Then GTM will be enabled to catch the URL changes. Plus you are later enabled to redirect the URLs on the serverside. (this can be really important for SEO!!, HASHBANG = no 301 redirect).
Hi Can!
Thanks for the advice. I followed what you told me and it’s working perfectly now. I wasn’t aware there was a way to get rid of the hashbang. I had looooots of problems when refreshing the page and getting a 404, I’m sure you know what the problem is, but managed to find the right config combination for both my app and the server.
Thanks a lot! 😀
when i use this code in my angular 2 app it shows no exported member ‘ ROUTER_DIRECTIVES’ !
I have the same issue
Hello Francesco & mitesh,
you can skip the ROUTER_DIRECTIVES call, it has been deprecated. I’ll update this blog post soon.
https://angular.io/docs/ts/latest/tutorial/toh-pt5.html
any ideas on how to add logged in user to the tag manager tracking ?? or do we have to go back to the non tag manager method to get user id in the data ??
Yes. But be careful with data privacy. You need to submit the user ID via DataLayer. Do this in your App e.g. Component with:
(window).dataLayer.push({
userId: user.id
});
Then you have access in GTM via data layer variables.
See this tutorial for full instructions:
https://www.optimizesmart.com/cross-device-tracking-with-user-id-in-google-tag-manager/
I dont have index.html file in my Angular2 application. Where can I put my tracking code? Do we have tracking code for typeScript?
The tracking code you insert to your HTML File is mainly for loading Google Analytics // Tag Manager. If you have no global entrypoint in your application you can load the file for example with your build tool. Downside: you don’t receive updates that are made to the file from Google.
Why don’t you have an index.html? Do you have an AppComponent that is loaded global? You can load the file there and execute the required JavaScript.
e.g. for Google Analytics : -> ga(‘create’, ‘UA-XXXXXX-ID’, ‘auto’);
Hi! We implemented this way for our websites but we have a problem that meaning is caused by Angular2. Google Analytics does not identify the source traffic, mainly Facebook ads(using UTM) and Google Search. Has anyone had a similar problem?
This is because the parameters that Facebook attaches to the URL are not transferred to Google Analytics. To do this, you would have to send the parameters to Google Analytics manually.
We have migrated our website to Angular2. Initially we struggled with Sessions tracking which got solved by moving “ga(‘send’, ‘pageview’)” to app.ts.
Now the issue is we are not able to track Ecommerce on GA i.e., transactions, revenue ,etc.
Kindly help to let us know what needs to be done for this.
To Send eCommerce data in GA, you can use below code. Hope this will help you. Its working fine for me.
(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-xxxxxxx-x’, ‘auto’);
ga(‘send’, ‘pageview’);
ga(‘require’, ‘ecommerce’);
ga(‘ecommerce:addTransaction’, {
‘id’: ‘1234’, // Transaction ID. Required.
‘affiliation’: ‘Acme Clothing’, // Affiliation or store name.
‘revenue’: ‘27.50’, // Grand Total.
‘shipping’: ‘5’, // Shipping.
‘tax’: ‘1.29’ // Tax.
});
ga(‘ecommerce:addItem’, {
‘id’: ‘1234’, // Transaction ID. Required.
‘name’: ‘Garg Rommy’, // Product name. Required.
‘sku’: ‘DD23444’, // SKU/code.
‘category’: ‘Party Toys’, // Category or variation.
‘price’: ‘12.30’, // Unit price.
‘quantity’: ‘1’ // Quantity.
});
ga(‘ecommerce:addItem’, {
‘id’: ‘1234’, // Transaction ID. Required.
‘name’: ‘Rommy Garg’, // Product name. Required.
‘sku’: ‘DD23423’, // SKU/code.
‘category’: ‘Party Toys’, // Category or variation.
‘price’: ‘15.20’, // Unit price.
‘quantity’: ‘1’ // Quantity.
});
ga(‘ecommerce:send’);
I implemented GA with Google Tag Manager per this blog post and so far so good. I can see pageviews in real-time. I guess the next step though is, how can I add event-based tracking (e.g. clicking a link) if implementing GA using Tag Manager?
I tried enabling such triggers directly on Tag Manager and it did not work.
Do you need to go into every Angular component and manually add JavaScript on top of this?
Tks very much!
It’s work! =)
Dear Can!
Thanks for the article.
Works perfectly except since first pageview has no “History Change” then trigger will never be fired for the first pageview.
Is it possible to find any solution to this?
Thanks.
Google Tag Manager is not needed in angular 2 + versions . Right?
Not sure where the issue is. We are using the GoogleTagManager provider. I see the events show up in the window.dataLayer object.
However, all of the event are using ‘interaction’ for the event name.. Is that correct? ( see below)
{event: “interaction”, target: “Link”, action: undefined, label: “Back to questions”, value: undefined, …}
Looking at the gtm.ts code. In the eventTrack method it defaults to ‘iteraction’ if properties.event is not defined. The ‘event’ property is never passed in the properties object, however eventType is. And eventType matches what I defined in Angulartics2On tag.
So is this a bug?
Hi,
What is the version of angular-gtag compatible with Angular 4?