Angulartics custom plugin which uses an Angular service

Recently I started to use in my Angular projects Angulartics to deal with web analytics. It is a clean solution that could be directly used with multiple providers like Google Analytics or Piwik, see the list of plugins available.

If you want to use a different analytics or usage​ tracking service, you can create your own vendor plugin. The project documentation explains how to do it and claims that

It’s very easy to write your own plugin

And indeed it’s easy, the only thing to do is write your code inside these two functions:

angular.module('angulartics.myplugin', ['angulartics'])
  .config(['$analyticsProvider', function ($analyticsProvider) {

  $analyticsProvider.registerPageTrack(function (path) {
    // your implementation here
  }

  $analyticsProvider.registerEventTrack(function (action, properties) {
    // your implementation here
  }

}]);

If you take a look to the existing plugins implementation all of them access to an external api defined in the global scope, for example the piwik plugin uses window._paq.

But what I want to do is call from these track methods an angular service that allows me to post usage resources to a REST API. In “the angular way”​ without using any global scope function.

The problem here is that we should write our plugin code is inside an angular config block. And from a config block angular doesn’t allow to instantiate services neither inject the instance $injector.

I solved that issue using this approach:

var $injector = angular.element(document.documentElement).injector();

it allows us to get an instance injector and with it instantiate any server that we need.

As an example this is the code for an angulartics custom plugin that uses $log and MyUsageTrackingService services:

(function (angular) {
    'use strict';

    /**
     * @name angulartics.customplugin
     * Custom Angulartics plugin that uses an Angular Service for the Usage Tracking.
     *
     * Implementation docs:
     * https://github.com/luisfarzati/angulartics/blob/master/README.md
     * http://stackoverflow.com/questions/26890042/how-to-inject-location-into-a-click-event-handler-defined-in-a-config-function
     */
    angular.module('angulartics.customplugin', ['angulartics'])

        .config(['$analyticsProvider', function ($analyticsProvider) {

            /**
             * Track Page
             */
            $analyticsProvider.registerPageTrack(function (path) {

                /* Instantiate $log service */
                var $log = instanceInjector().get('$log');

                /* Log Page */
                $log.debug('Page tracking: ', path);

            });

            /**
             * Track Event
             */
            $analyticsProvider.registerEventTrack(function (action, properties) {

                /* Instantiate services */
                var $log = instanceInjector().get('$log');
                var myUsageTrackingService = instanceInjector().get('MyUsageTrackingService');

                /* Track */
                $log.debug("Event tracking: ", action, properties);

                var event = {
                    "applicationCode": "MYAPP",
                    "eventType": action,
                    "message": properties.message,
                    "appData": properties.appData
                };

                myUsageTrackingService.trackEvent(event);
            });

            /**
             * Returns an instance injector to allow the injection of services inside config blocks.
             */
            function instanceInjector() {
                return angular.element(document.documentElement).injector();
            }

        }]);

})(angular);
Advertisements