CodeBrief

Ember.js Analytics Integration

Update: As with many things Ember, this post has been outdated. Using the new router landing in 1.0, one way to do this is as follows:

App.ApplicationController = Ember.Controller.extend

  routeChanged: ( ->
    return unless window._gaq
    Em.run.next ->
      _gaq.push(['_trackPageview'])
      mixpanel.track_pageview()
  ).observes('currentPath')

Inevitably, if you are building any web application, you are going to need some form of analytics. Single page javascript applications are no exception. Unlike normal applications, however, integrating Google Analytics and Mixpanel is not as trivial as including some javascript in the page's header and forgetting about it.

In this case, page transitions are frequently handled by url hash changes or HTML5's pushstate API. When using these technologies, application developer's must explicitly track page views.

When using Ember.js, the place to do this is inside the routing system. One such option is to reopen the Route class:

Ember.Route.reopen({
  enter: function(router) {
    this._super(router);
    if(this.get('isLeafRoute')) {
      var path = this.absoluteRoute(router);
      mixpanel.track('page viewed', {'page name' : document.title, 'url' : path});
      _gaq.push(['_trackPageview', path]);
    }
  }
});

This should function correctly when using url hashes as well as the history implementation. Since we took advantage of Ember's open classes, this is truly a drop-in fix and does not require any change to how you would go about defining routes:

App.Router = Ember.Router.extend({
  ...
  root: Ember.Route.extend({
    foo: Ember.Route.extend({
      route: 'foo',
      ...
    })
  })
  ...
});

Since we are using Mixpanel and Google Analytics here, we must also circumvent the initial page view that is normally tracked by including their default javascript. For Google Analytics, this means removing the _gaq.push(['_trackPageview']); that is included in the default snippet of code. For Mixpanel, the call to init must be changed to include a special option:

mixpanel.init("#{MIXPANEL_TOKEN}", {track_pageview: false});

That's it. By incorporating the above technique, single page Ember.js applications should now be reporting page views as one would expect for a normal web application!

Note the condition if(this.get('isLeafRoute')). In Ember, only leaf nodes are routable, and therefore it only makes sense to track pageviews at that level. When dealing with Mixpanel, however, the line between events and page views is often very thin. In some cases it makes sense to trigger a particular mixpanel event when an entire tree of routes is reached in the router. One convenient solution to this is to define a mixpanelEvent property on certain routes and handle them similarly to page views:

Ember.Route.reopen({
  enter: function(router) {
    this._super(router);
    ...
    var mixpanelEvent = this.get('mixpanelEvent');
    if(mixpanelEvent) {
      mixpanel.track(mixpanelEvent);
    }
  }
});

To use this, simply set the mixpanelEvent property in the route:

App.Router = Ember.Router.extend({
  ...
  root: Ember.Route.extend({
    foo: Ember.Route.extend({
      route: 'foo',
      mixpanelEvent: 'Foo Entered'
      ...
    })
  })
  ...
});

Unlike page views, these events will be tracked whenever the route is earch or whenever any child route is reached. Fortunately, due to the underlying state manager based implementation of the router, these events will not be tracked multiple times on transitions between child routes.

comments powered by Disqus