I'm setting up a new app using AngularJS as the frontend. Everything on the client side is done with HTML5 pushstate and I'd like to be able to track my page views in Google Analytics.
-
Try angulartics luisfarzati.github.io/angulartics– DavidCommented Jul 8, 2015 at 14:29
-
2angulartics is deprecated. Use angular-google-analytics– webdev5Commented Jun 7, 2016 at 15:55
-
6@webdev5 A new version of angulartics is out. Try it out at: angulartics.github.io– DevnerCommented Jun 24, 2016 at 4:17
21 Answers
If you're using ng-view
in your Angular app you can listen for the $viewContentLoaded
event and push a tracking event to Google Analytics.
Assuming you've set up your tracking code in your main index.html file with a name of var _gaq
and MyCtrl is what you've defined in the ng-controller
directive.
function MyCtrl($scope, $location, $window) {
$scope.$on('$viewContentLoaded', function(event) {
$window._gaq.push(['_trackPageView', $location.url()]);
});
}
UPDATE: for new version of google-analytics use this one
function MyCtrl($scope, $location, $window) {
$scope.$on('$viewContentLoaded', function(event) {
$window.ga('send', 'pageview', { page: $location.url() });
});
}
-
12FWIW it should be
_trackPageview
and not_trackPageView
... spent 10 mins of head scratching :)– sajalCommented Aug 16, 2012 at 17:25 -
128
-
5@DavidRivers Hm, it's been a while, but just from glancing at this answer and my comment, it would seem that applying it to
$rootScope
would guarantee that it will not be removed by some other event or DOM change, and usingrouteChangeSuccess
will fire every time the location bar changes, instead of just ever time you change your view source template. Does that make sense? Commented Oct 17, 2013 at 17:00 -
5@dg988 during a $routeChangeSuccess event, you may NOT know upfront the page's title (which, for example, could be set inside a controller after it finished processing data from a RESTful service). This way alone with the URL of the page Google Analytics will track a title of the previous page. Commented Dec 3, 2013 at 9:01
-
43If you're using the GA new script, it's
$window.ga('send', 'pageview', { page: $location.path() });
instead of the$window._gaq...
part. Also, you may want to removega('send', 'pageview');
from your original GA code to prevent duplicates when you first land on a page.– CWSpearCommented Dec 13, 2013 at 5:25
When a new view is loaded in AngularJS
, Google Analytics does not count it as a new page load. Fortunately there is a way to manually tell GA to log a url as a new pageview.
_gaq.push(['_trackPageview', '<url>']);
would do the job, but how to bind that with AngularJS?
Here is a service which you could use:
(function(angular) {
angular.module('analytics', ['ng']).service('analytics', [
'$rootScope', '$window', '$location', function($rootScope, $window, $location) {
var track = function() {
$window._gaq.push(['_trackPageview', $location.path()]);
};
$rootScope.$on('$viewContentLoaded', track);
}
]);
}(window.angular));
When you define your angular module, include the analytics module like so:
angular.module('myappname', ['analytics']);
UPDATE:
You should use the new Universal Google Analytics tracking code with:
$window.ga('send', 'pageview', {page: $location.url()});
-
6The other thing to note here is you need to include the "analytics" service somewhere. I did mine in app.run.– NixCommented Sep 5, 2013 at 17:22
-
Would this need including in every module, or just the initial app? Commented Nov 6, 2014 at 11:08
-
2@Designermonkey It should work if you include just in your main app module. If you have a totally independent module though which does not require your main module, you may need to include that again. Cheers! Commented Nov 6, 2014 at 12:04
-
If you're using a service, you should define your functions with the "this" keyword: source. For example, within the service it would be
this.track = function($window.ga('send', 'pageview', {page: $location.url()});
This will allow you to usegoogleAnalytics.track();
within your app.run() function– zcoonCommented May 25, 2016 at 15:19
app.run(function ($rootScope, $location) {
$rootScope.$on('$routeChangeSuccess', function(){
ga('send', 'pageview', $location.path());
});
});
-
9Note: If you use minimzation/compilation you need to specify the parameter names:
app.run(["$rootScope", "$location", function(...
– dplassCommented Jul 1, 2014 at 15:15 -
This solution is excellent if you don't want to include GA code in every controller.– brzCommented Dec 14, 2014 at 11:56
-
1Not so great if you have async code that dynamically changes your title, else its great.– JohanCommented Sep 30, 2015 at 9:49
Just a quick addition. If you're using the new analytics.js, then:
var track = function() {
ga('send', 'pageview', {'page': $location.path()});
};
Additionally one tip is that google analytics will not fire on localhost. So if you are testing on localhost, use the following instead of the default create (full documentation)
ga('create', 'UA-XXXX-Y', {'cookieDomain': 'none'});
-
Oh man, thanks. I was getting weird errors, and noticed my snippet didn't even have _gaq in it, hehe. Silly Google: updating their code and all that! Haha.– CWSpearCommented Dec 13, 2013 at 5:19
-
3Make sure you use
$window
to access the window (ga
alone inside Angular is very likely to beundefined
). Also, you may want to removega('send', 'pageview');
from your original GA code to prevent duplicates when you first land on a page.– CWSpearCommented Dec 13, 2013 at 5:22 -
6Using ui-router you can send page views like this:
$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) { ga('send', 'pageview', { 'page': $location.path(), 'title': toState.name }); });
– pherrisCommented May 1, 2014 at 9:46 -
2To add: If you are using search parameters in your app you can track those using
$location.url()
– AdeCommented Jun 13, 2014 at 7:43 -
2I upvoted this answer since using .path() is better than .url() because you normally want to exclude query string params. See: Google Analytics Configuration Mistake #2: Query String Variables Commented Feb 6, 2015 at 15:36
I've created a service + filter that could help you guys with this, and maybe also with some other providers if you choose to add them in the future.
Check out https://github.com/mgonto/angularytics and let me know how this works out for you.
Merging the answers by wynnwu and dpineda was what worked for me.
angular.module('app', [])
.run(['$rootScope', '$location', '$window',
function($rootScope, $location, $window) {
$rootScope.$on('$routeChangeSuccess',
function(event) {
if (!$window.ga) {
return;
}
$window.ga('send', 'pageview', {
page: $location.path()
});
});
}
]);
Setting the third parameter as an object (instead of just $location.path()) and using $routeChangeSuccess instead of $stateChangeSuccess did the trick.
Hope this helps.
I've created a simple example on github using the above approach.
-
I added a fix to better report the path. so now if you have the route of
/account/:accountId
aka path ishttp://somesite.com/account/123
it now will report the path as/account?accountId=123
– SnIcKCommented Jan 4, 2013 at 20:21 -
@IBootstrap what is the benefit of putting the route params in query string? Commented Apr 23, 2013 at 9:57
-
@IBootstrap I have the same question, what is the benefit of putting the route params in query string ?– Tzu ngCommented Jun 6, 2013 at 7:14
-
1Not all routes are different pages and GA treat each route as different page. By converting the route to query string, you have the option to ignore parts of the query string.– SnIcKCommented Aug 21, 2013 at 5:26
The best way to do this is using Google Tag Manager to fire your Google Analytics tags based on history listeners. These are built in to the GTM interface and easily allow tracking on client side HTML5 interactions .
Enable the built in History variables and create a trigger to fire an event based on history changes.
In your index.html
, copy and paste the ga snippet but remove the line ga('send', 'pageview');
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID -->
<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','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXXXXX-X');
</script>
I like to give it it's own factory file my-google-analytics.js
with self injection:
angular.module('myApp')
.factory('myGoogleAnalytics', [
'$rootScope', '$window', '$location',
function ($rootScope, $window, $location) {
var myGoogleAnalytics = {};
/**
* Set the page to the current location path
* and then send a pageview to log path change.
*/
myGoogleAnalytics.sendPageview = function() {
if ($window.ga) {
$window.ga('set', 'page', $location.path());
$window.ga('send', 'pageview');
}
}
// subscribe to events
$rootScope.$on('$viewContentLoaded', myGoogleAnalytics.sendPageview);
return myGoogleAnalytics;
}
])
.run([
'myGoogleAnalytics',
function(myGoogleAnalytics) {
// inject self
}
]);
-
-
Just out of curiosity, why do you set the
page
with the current path, and then submit it as apageview
? What is the difference of doing it that way instead of just$window.ga('send', 'pageview', {page: $location.url()});
?– FizzixCommented Feb 29, 2016 at 6:42 -
1I can't remember TBH but by looking at it I would say it leaves flexibility for other actions or logging additional stuff elsewhere, and may be more accurate by explicitly telling ga of the current page, so ga can continue to functiona accurately, rather than JUST logging a pageview for some "arbitrary" page url.– ilovettCommented Feb 29, 2016 at 22:16
-
If you want realtime analytics to work in Google you should do "set". For why see documentation reference: developers.google.com/analytics/devguides/collection/… Commented Apr 1, 2016 at 14:54
-
'set' is recommended so that every single hits being sent on the same page (like GA Event to track user interactions) is sent with the same Page path value. Nothing to do with Realtime analytics. see developers.google.com/analytics/devguides/collection/…– Open SEOCommented Apr 18, 2020 at 19:32
I found the gtag()
function worked, instead of the ga()
function.
In the index.html file, within the <head>
section:
<script async src="https://www.googletagmanager.com/gtag/js?id=TrackingId"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'TrackingId');
</script>
In the AngularJS code:
app.run(function ($rootScope, $location) {
$rootScope.$on('$routeChangeSuccess', function() {
gtag('config', 'TrackingId', {'page_path': $location.path()});
});
});
Replace TrackingId
with your own Tracking Id.
If someone wants to implement using directives then, identify (or create) a div in the index.html (just under the body tag, or at same DOM level)
<div class="google-analytics"/>
and then add the following code in the directive
myApp.directive('googleAnalytics', function ( $location, $window ) {
return {
scope: true,
link: function (scope) {
scope.$on( '$routeChangeSuccess', function () {
$window._gaq.push(['_trackPageview', $location.path()]);
});
}
};
});
For those of you using AngularUI Router instead of ngRoute can use the following code to track page views.
app.run(function ($rootScope) {
$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
ga('set', 'page', toState.url);
ga('send', 'pageview');
});
});
If you're using ui-router you can subscribe to the $stateChangeSuccess event like this:
$rootScope.$on('$stateChangeSuccess', function (event) {
$window.ga('send', 'pageview', $location.path());
});
For a complete working example see this blog post
Use GA 'set' to ensure routes are picked up for Google realtime analytics. Otherwise subsequent calls to GA will not show in the realtime panel.
$scope.$on('$routeChangeSuccess', function() {
$window.ga('set', 'page', $location.url());
$window.ga('send', 'pageview');
});
Google strongly advises this approach generally instead of passing a 3rd param in 'send'. https://developers.google.com/analytics/devguides/collection/analyticsjs/single-page-applications
Developers creating Single Page Applications can use autotrack, which includes a urlChangeTracker plugin that handles all of the important considerations listed in this guide for you. See the autotrack documentation for usage and installation instructions.
I am using AngluarJS in html5 mode. I found following solution as most reliable:
Use angular-google-analytics library. Initialize it with something like:
//Do this in module that is always initialized on your webapp
angular.module('core').config(["AnalyticsProvider",
function (AnalyticsProvider) {
AnalyticsProvider.setAccount(YOUR_GOOGLE_ANALYTICS_TRACKING_CODE);
//Ignoring first page load because of HTML5 route mode to ensure that page view is called only when you explicitly call for pageview event
AnalyticsProvider.ignoreFirstPageLoad(true);
}
]);
After that, add listener on $stateChangeSuccess' and send trackPage event.
angular.module('core').run(['$rootScope', '$location', 'Analytics',
function($rootScope, $location, Analytics) {
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams, options) {
try {
Analytics.trackPage($location.url());
}
catch(err) {
//user browser is disabling tracking
}
});
}
]);
At any moment, when you have your user initalized you can inject Analytics there and make call:
Analytics.set('&uid', user.id);
I am using ui-router and my code looks like this:
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams){
/* Google analytics */
var path = toState.url;
for(var i in toParams){
path = path.replace(':' + i, toParams[i]);
}
/* global ga */
ga('send', 'pageview', path);
});
This way I can track different states. Maybe someone will find it usefull.
I personally like to set up my analytics with the template URL instead of the current path. This is mainly because my application has many custom paths such as message/:id
or profile/:id
. If I were to send these paths, I'd have so many pages being viewed within analytics, it would be too difficult to check which page users are visiting most.
$rootScope.$on('$viewContentLoaded', function(event) {
$window.ga('send', 'pageview', {
page: $route.current.templateUrl.replace("views", "")
});
});
I now get clean page views within my analytics such as user-profile.html
and message.html
instead of many pages being profile/1
, profile/2
and profile/3
. I can now process reports to see how many people are viewing user profiles.
If anyone has any objection to why this is bad practise within analytics, I would be more than happy to hear about it. Quite new to using Google Analytics, so not too sure if this is the best approach or not.
I suggest using the Segment analytics library and following our Angular quickstart guide. You’ll be able to track page visits and track user behavior actions with a single API. If you have an SPA, you can allow the RouterOutlet
component to handle when the page renders and use ngOnInit
to invoke page
calls. The example below shows one way you could do this:
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
ngOnInit() {
window.analytics.page('Home');
}
}
I’m the maintainer of https://github.com/segmentio/analytics-angular. With Segment, you’ll be able to switch different destinations on-and-off by the flip of a switch if you are interested in trying multiple analytics tools (we support over 250+ destinations) without having to write any additional code. 🙂
Merging even more with Pedro Lopez's answer,
I added this to my ngGoogleAnalytis module(which I reuse in many apps):
var base = $('base').attr('href').replace(/\/$/, "");
in this case, I have a tag in my index link:
<base href="/store/">
it's useful when using html5 mode on angular.js v1.3
(remove the replace() function call if your base tag doesn't finish with a slash /)
angular.module("ngGoogleAnalytics", []).run(['$rootScope', '$location', '$window',
function($rootScope, $location, $window) {
$rootScope.$on('$routeChangeSuccess',
function(event) {
if (!$window.ga) { return; }
var base = $('base').attr('href').replace(/\/$/, "");
$window.ga('send', 'pageview', {
page: base + $location.path()
});
}
);
}
]);
If you are looking for full control of Google Analytics's new tracking code, you could use my very own Angular-GA.
It makes ga
available through injection, so it's easy to test. It doesn't do any magic, apart from setting the path on every routeChange. You still have to send the pageview like here.
app.run(function ($rootScope, $location, ga) {
$rootScope.$on('$routeChangeSuccess', function(){
ga('send', 'pageview');
});
});
Additionaly there is a directive ga
which allows to bind multiple analytics functions to events, like this:
<a href="#" ga="[['set', 'metric1', 10], ['send', 'event', 'player', 'play', video.id]]"></a>