React + Router + Google Tag Manager - google-analytics

I've been spending a bit of time developing an MVP at quickcypher.com. I wanted to start putting in some analytics, and it worked great for just tracking total visits, but things went south when I tried to track different URLs on my site that uses React Router.
My approach was this: Setup a GA tag that fires on some pages, using a trigger for a custom "pageview" event. When things did fire, I would set the field page to "/rap" for example. I was firing the event in the "componentDidMount" method of the top level component for each of my views. Using the debugger, I saw the event fire as expected, but for the life of me I can't get GA to acknowledge the event. GA works as expected when I simplify the tag to fire on "all pages", so I'm assuming it has something to do with React.
Has anyone successfully implemented this or run into similar problems? Is my approach all wrong? Hoping for some guidance...cheers!

A bit late to the party here, but react router should need no special code to integrate with GTM. Just drop the GTM script on your page (immediately after the opening <body> tag as recommended) and let your app run as normal.
In GTM create a custom trigger for history change.
You can fire it on all history changes.
Or only on some of them. Only on your production hostname, for example.
Then add a tag for your google analytics (or whatever) and configure it to fire on your history change event by clicking "More" under "Fire On" and selecting the trigger created above.
It's also important to change the Advanced Settings of our tag to fire once per event instead of once per page. Without this, the tag will only fire on the initial page load.

This could be due to misconfiguration of your google analytics account, but assuming that you can fire the initial pageview event back to GA, here is a recipe that taps into react-router's willTransitionTo hook. It also uses react-google-analytics. First npm install react-google-analytics.
Then configure your app like so:
var React = require('react');
var Router = require('react-router');
var Route = Router.Route;
var DefaultRoute = Router.DefaultRoute;
var RouteHandler = Router.RouteHandler;
var ga = require('react-google-analytics');
var GAInitiailizer = ga.Initializer;
// some components mapped to routes
var Home = require('./Home');
var Cypher = require('./Cypher');
var App = React.createClass({
mixins: [Router.State],
statics: {
willTransitionTo: function(transition, params, query, props) {
// log the route transition to google analytics
ga('send', 'pageview', {'page': transition.path});
}
},
componentDidMount: function() {
var GA_TRACKING_CODE = 'UA-xxxxx';
ga('create', GA_TRACKING_CODE);
ga('send', 'pageview');
},
render: function() {
return (
<div>
<RouteHandler />
<GAInitiailizer />
</div>
);
}
});
var routes = (
<Route path="/" handler={App} >
<DefaultRoute handler={Home} />
<Route name="cypher" path="/cypher" handler={Cypher} />
</Route>
);
Router.run(routes, function (Handler) {
React.render(<Handler />, document.body);
});
module.exports = App;

Related

wpcf7 and GTM event listener issue

I have a Tag set up in GTM, custom html like this;
<script>
document.addEventListener( 'wpcf7submit', function( event ) {
dataLayer.push({
'event' : 'wpcf7successfulsubmit',
'CF7formID' : event.detail.contactFormId
});
}, false );
</script>
Doesn't work. Not at all. So I put a script on the page.
var wpcf7Elm = document.querySelector( '.wpcf7' );
wpcf7Elm.addEventListener( 'wpcf7submit', function( event ) {
dataLayer.push({
'event' : 'wpcf7successfulsubmit',
'CF7formID' : event.detail.contactFormId
});
}, false );
from a basic example on contactform7.com. This, in GTM preview, triggers fine. The first time it triggers the tag once, the 2nd and subsequent times it triggers twice (implying that both my script and the GTM tag are firing). Guessing at a problem with the event bubbling up. I put the specific selector wpcf7Elm into the tag's custom html but this doesn't work - like the first example.
I have no problem with running from a script but the problem is firing the tag twice so that the analytics shows two events. I would like to use GTM but at the moment the only solution I can see is to go back to on page scripts.
Can anyone suggest what I might be doing wrong? Just to note that I have disabled all plugins and that I am using, on a different page, a wpcf7 event listener successfully (from a script on the page) to perform a presentation function.

Branch Deep Linking not working in Google Analytics hitCallback

I'm using both Google Analytics and branch.io in this website.
The website is designed for mobile.
The problem is that when clicking the banner with text "OPEN", the app cannot be opened.
Here is the code for the click:
$scope.openApp = () => {
let appOpened = false;
const open = () => {
if (!appOpened) {
appOpened = true;
branch.deepviewCta();
}
};
$timeout(open, 1000);
ga('send', 'event', 'homepage', 'download', {
hitCallback() {
open();
}
});
};
If I get rid of the GA code, it works fine:
$scope.openApp = () => {
let appOpened = false;
const open = () => {
if (!appOpened) {
appOpened = true;
branch.deepviewCta();
}
};
$timeout(open, 1000);
open();
};
The reason I put open() in hitCallback is to make sure GA sends out the hit because open() will redirect to another page.
Can you help me?
Alex from Branch.io here:
The Branch deepviewCta() function works on iOS 9+ by triggering an automatic redirect to a Universal Link URL (which opens the app) and then going to a fallback URL if that fails. But Apple is very specific about the situations in which a Universal Link is allowed to launch the app (including things like how long of a pause is allowed before redirection). Of course these restrictions are not public, so all we can do is guess. My suspicion is that putting the deepviewCta() function inside a GA callback is falling outside of Apple’s rules, so the app never opens and you are instead being sent to the fallback URL.
I can think of two options here:
You can build some way to trigger the GA and Branch functions separately so that they don’t conflict with Apple’s requirements.
We actually have a brand new, one-click integration with Google Analytics, which you can read about here and here. If you set that up, you’ll get all Branch-related events automatically instead of needing to manually collect link click data.
Hopefully that helps!

Social shares tracking in GA

This is pretty much covered topic for original FB/Twitter buttons. But what if I have my own "share on fb" button? Like this:
<div id="fb_share"><a target="_blank" href="http://www.facebook.com/share.php?u=blah-blah">Share on FB</a></div>
so I've come up with the folloing solution:
var FBbtn = document.getElementById("fb_share");
FBbtn.addEventListener('click', function() {
ga('send', 'social', {
'socialNetwork': 'facebook',
'socialAction': 'share',
'socialTarget': window.location
});
//console.log('tracked');
});
That is placed AFTER the Google Analytics code.
Despite the fact it wont catch FB callback - it is supposed to do the trick but for some reason I still cannot see any results in Analytics so the question is this: will the solution actually work? In fact it could be even like this I believe:
FB
Your 'share on Facebook' links causes the page to navigate (and not open a new window/tab). When this navigation happens, most mainstream browsers cancel all pending HTTP requests for the current page and then navigates to the new page (fb.com)
In this scenario, one of the pending HTTP requests will be the GA event tracking call which will therefore never complete and never be received by the GA servers.
What you need to use is the GA hit callback functionality, this essentially cancels the native navigation (to FB), sends the tracking call and waits enough time for it to complete and then does a JavaScript redirection to the next page.
You should read the google docs here
In your case your event tracking function should be similar to this:
var FBbtn = document.getElementById("fb_share");
FBbtn.addEventListener('click', function() {
ga('send', 'social', {
'socialNetwork': 'facebook',
'socialAction': 'share',
'socialTarget': window.location,
'hitCallback': function(){
window.location = this.href;
}
});
//console.log('tracked');
return false;
});
So I've made the following changes:
Added the hitCallback property to the event tracking call. this is an anonymous function that is called once the GA servers have sent their response to the event tracking.
added a 'return false' statement which cancels the native functionality and then relies on the hitCallback function to do the navigating.

How can I tell if my Google content experiment is running?

I've created a google content experiment without redirects using the docs.
The basic implementation involves a javascript snippet that uses the following code to choose the version of the experiment:
<!-- Load the Content Experiment JavaScript API client for the experiment -->
<script src="//www.google-analytics.com/cx/api.js?experiment=YOUR_EXPERIMENT_ID"></script>
<script>
// Ask Google Analytics which variation to show the user.
var chosenVariation = cxApi.chooseVariation();
</script>
<!-- Load the JQuery library -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script>
// Define JavaScript for each page variation of this experiment.
var pageVariations = [
function() {}, // Original: Do nothing. This will render the default HTML.
function() { // Variation 1: Banner Image
document.getElementById('banner').src = 'bay-bridge.jpg';
},
function() { // Variation 2: Sub-heading Text
document.getElementById('heading').innerHTML = 'Look, a Bridge!';
},
function() { // Variation 3: Button Text
document.getElementById('button').innerHTML = 'Learn more';
},
function() { // Variation 4: Button Color
document.getElementById('button').className = 'button button-blue';
}
];
// Wait for the DOM to load, then execute the view for the chosen variation.
$(document).ready(
// Execute the chosen view
pageVariations[chosenVariation]
);
</script>
However, when I visit the page using an incognito window, I only see the first variation of the experiment. When I check chosenVariation in the console, it's always 0. In fact, when I call cxApi.chooseVariation(); in the console, it always returns 0.
Is this because google recognizes my incognito browser windows, or is something broken with cxApi.chooseVariation(); or in my implementation?
I had the same problem, 100% of the sessions were given the original (0) variation. In order to fix the problem, I added the javascript code provided by the experiment. Go to your experiment (edit), click Setting up your experiment code, manually insert the code, copy the code in there.
Now since you (and I) don't want to have a redirect, remove this part at the end of the code <script>utmx('url','A/B');</script>. If your page is templated, you can use a variable and insert your experiment key (not experiment id) where you see var k='########-#'
Now either very few people use the experiments in a client-only fashion or we're totally stupid because it would seem to me that the guide is wrong and there's absolutely no documentation that shows a working client-only setup.

Tracking Google Analytics Page Views with AngularJS

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.
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() });
});
}
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()});
app.run(function ($rootScope, $location) {
$rootScope.$on('$routeChangeSuccess', function(){
ga('send', 'pageview', $location.path());
});
});
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'});
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.
https://github.com/isamuelson/angularjs-googleanalytics
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
}
]);
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:

Resources