Angularytics (Google Analytics for AngularJS directive) only in production environment

2.5k views Asked by At

I´m using Angularytics in my AngularJS web app. It´s working fine, however, I have three environments (development, test and production) and it´s gathering statistics from both of them. I would like to show analytics only for production enviroment.

I have analyzed many options but all of them have some problems. Now, I would like to edit the angularytics.js script to add a condition, so, the code it will only do something in case $rootScope.ENVIRONMENT constant is Production.

Something like:

(function () {
  angular.module('angularytics', []).provider('Angularytics', function () {
    if($rootScope.ENVIRONMENT == 'Production') {
    var eventHandlersNames = ['Google'];
    this.setEventHandlers = function (handlers) {
      if (angular.isString(handlers)) {
        handlers = [handlers];
      }
      eventHandlersNames = [];
      angular.forEach(handlers, function (handler) {
        eventHandlersNames.push(capitalizeHandler(handler));
      });
    };
    var capitalizeHandler = function (handler) {
      return handler.charAt(0).toUpperCase() + handler.substring(1);
    };
    var pageChangeEvent = '$locationChangeSuccess';
    this.setPageChangeEvent = function (newPageChangeEvent) {
      pageChangeEvent = newPageChangeEvent;
    };
    this.$get = [
      '$injector',
      '$rootScope',
      '$location',
      function ($injector, $rootScope, $location) {
        var eventHandlers = [];
        angular.forEach(eventHandlersNames, function (handler) {
          eventHandlers.push($injector.get('Angularytics' + handler + 'Handler'));
        });
        var forEachHandlerDo = function (action) {
          angular.forEach(eventHandlers, function (handler) {
            action(handler);
          });
        };
        var service = {};
        service.init = function () {
        };
        service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
          forEachHandlerDo(function (handler) {
            if (category && action) {
              handler.trackEvent(category, action, opt_label, opt_value, opt_noninteraction);
            }
          });
        };
        service.trackPageView = function (url) {
          forEachHandlerDo(function (handler) {
            if (url) {
              handler.trackPageView(url);
            }
          });
        };
        service.trackTiming = function (category, variable, value, opt_label) {
          forEachHandlerDo(function (handler) {
            if (category && variable && value) {
              handler.trackTiming(category, variable, value, opt_label);
            }
          });
        };
        $rootScope.$on(pageChangeEvent, function () {
          service.trackPageView($location.url());
        });
        return service;
      }
    ];
  }});
}());
(function () {
  angular.module('angularytics').factory('AngularyticsConsoleHandler', [
    '$log',
    function ($log) {
      var service = {};
      service.trackPageView = function (url) {
        $log.log('URL visited', url);
      };
      service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
        $log.log('Event tracked', category, action, opt_label, opt_value, opt_noninteraction);
      };
      service.trackTiming = function (category, variable, value, opt_label) {
        $log.log('Timing tracked', category, variable, value, opt_label);
      };
      return service;
    }
  ]);
}());
(function () {
  angular.module('angularytics').factory('AngularyticsGoogleHandler', function () {
    var service = {};
    service.trackPageView = function (url) {
      _gaq.push([
        '_set',
        'page',
        url
      ]);
      _gaq.push([
        '_trackPageview',
        url
      ]);
    };
    service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
      _gaq.push([
        '_trackEvent',
        category,
        action,
        opt_label,
        opt_value,
        opt_noninteraction
      ]);
    };
    service.trackTiming = function (category, variable, value, opt_label) {
      _gaq.push([
        '_trackTiming',
        category,
        variable,
        value,
        opt_label
      ]);
    };
    return service;
  }).factory('AngularyticsGoogleUniversalHandler', function () {
    var service = {};
    service.trackPageView = function (url) {
      ga('set', 'page', url);
      ga('send', 'pageview', url);
    };
    service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
      ga('send', 'event', category, action, opt_label, opt_value, { 'nonInteraction': opt_noninteraction });
    };
    service.trackTiming = function (category, variable, value, opt_label) {
      ga('send', 'timing', category, variable, value, opt_label);
    };
    return service;
  });
}());
(function () {
  angular.module('angularytics').filter('trackEvent', [
    'Angularytics',
    function (Angularytics) {
      return function (entry, category, action, opt_label, opt_value, opt_noninteraction) {
        Angularytics.trackEvent(category, action, opt_label, opt_value, opt_noninteraction);
        return entry;
      };
    }
  ]);
}());

I know a little bit of Angular, however, I think I´m not skilled enough to inject the $rootScope in this script. I always get $rootScope is not defined.

UPDATE Regarding the comments below, I update the post: To add the script conditionally was my first approach. This can be done in a controller like:

if ($rootScope.ENVIRONMENT == 'Production') {    
            var head = document.getElementsByTagName('head')[0];
            var js = document.createElement("script");    
            js.type = "text/javascript";
            js.src =   "lib/plugins/angularytics-yanpy.js";
            head.appendChild(js);           
        }

This is angularytics-yanpy:

(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-59702007-1', 'auto');

In the other hand, you have to add the angularytics script to home page:

I think this will work in production environment. However, now in development a got a javascript error. Because angularytics.js needs the ga object created in the angularytics-yanpy.js. As this script is not created because is in development environment a get the javascript error.

I also added the <script src="lib/plugins/angularytics.js"></script> dinamically. But I got a new error, because this script defines a angularytics provider that is defined in the app.js file.

So, the errors are chained and thats why I tried to update the angularytics.js script in order to keep the provider but in case of environment different that production it should do nothing.

Probably I´m not so clear, but it´s not easy to explain. Just let me know if you need some more clarifications.

4

There are 4 answers

3
Ed Knowles On BEST ANSWER

You shouldn't be setting the environment in the $rootScope, it would be best to set it as a constant using grunt-ng-constant (see tutorial).

To answer your question. You can inject rootScope into the provider by using:

.provider('Angularytics', function ($rootScope) {});

However this isn't a good solution to your overall problem.

A good method would be to detect the hostname and set separate Google Analytic Properties for:

  • development
  • staging
  • production

In your html google analytics script add this switch statement.

var gaProp;

switch (window.location.hostname) {
    case 'www.domain.com':
        gaProp = 'UA-123456'; // production
        break;
    case 'staging.domain.com':
        gaProp = 'UA-654321'; // staging
        break;
    default:
        gaProp = 'UA-000000'; // development
}

// Google Analtics
(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', gaProp, 'auto');

Note I am setting gaProp in the ga event (last line).

This way you can setup different goals and filters for each property and all your test data doesn't interfere with each other.

2
Billy On

You probably want to set up a filter in the Google Analytics data views.

  1. Click on Add Filter.
  2. Give your filter a name.
  3. Set the filter Type to Custom.
  4. Select the include button Set the filter field to Hostname.
  5. Then set the pattern like so: www\.yourdomain\.com$ (Of course if you don't use '.com' you use the TLD you actually use).
  6. Select the profiles for which you would like to enable this filter.

This is probably the easiest way of doing what you want. You filter out any data that is not from the domain.

0
Aurelio On

As you are using Grunt to build your app here's a method I've successfully implemented in one of my apps at work.

I have generated 2 tracking codes in Google Analytics, one for the testing environment, one for production. I see that you're not planning to track you environment, and that was my initial plan too. However it's good to test that everything is working regarding Google Analytics too, and also it will be very useful in case you'll start tracking custom events (like how many users click on a certain button and so on). Luckily tracking codes are free...

I have used Grunt preprocess to conditionally write the appropriate code.

In my index.html I have placed my GA code at the bottom like so:

  <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');

  <!-- @if DEV -->
  ga('create', 'UA-554030000-1', 'auto');
  <!-- @endif -->
  <!-- @if PRODUCTION -->
  ga('create', 'UA-554030000-2', 'auto');
  <!-- @endif -->
  </script>  

Then simply configure your task:

preprocess : {
  html: 'build/index.html',
  options: {
    inline : true
  }
  production : {
  options: {
   context : {
    PRODUCTION: true
   }
  }
 },
 dev : {
  options: {
    context : {
     DEV: true
   }
 }

Then running grunt preprocess:production or grunt preprocess:dev will strip the unused code and build the appropriate file. This task can then be included in your build process (I use a flag like grunt build --dev to direct the script to the right path).

4
Joshua Kelly On

It should be as easy as turning development mode on to stop sending tracking information. Straight from the docs, it says to do the following in your app config.

For the two environments you don't want tracking sent, set to true and when pushing to production just set it back to false using a build script.

$analyticsProvider.developerMode(true);