I’ve been playing around with Durandal lately, and was wondering how you get a Durandal SPA to work with Google Analytics. The problem is the visitor is staying on the same html page. Yet you want analytics for your different views (so everytime the hash in the browsers navigation bar changes).

It turns out this is fairly easy. I’m putting this here for my own and anyone else’s reference. It probably works with other SPA frameworks too (Angular for example).

There are a number of composition lifecycle functions that a Durandal viewmodel can implement, but instead of putting Google Analytics code in every viewmodel, it’s easier to just include it once. Durandal provides an event when navigation has completed. Hook into that event and you can add a Google Analytics entry each time your user navigates to a new view:

router.on('router:navigation:complete', function(instance, instruction) {
    ga('create', 'UA-XXXXXXXX-X', 'example.com');
    ga('send', 'pageview');
});

Put this in your main.js or shell.js file so it is hooked up early in your application’s lifetime. In my case, I added it in the ‘activate’ method of the shell.js (you can see it in my GitHub repository). Last but not least, don’t forget to include the usual Google Analytics code so the calls above actually work:

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

This is the newer code that works with ‘Universal Analytics’, but it works the same for the older code. You just have to split the code into the part that defines the function and the part that actually adds an entry on every navigation.

Update:
Thanks to a certain George, who commented on this post when it was still hosted on Blogspot/Blogger, you can make less calls to Google’s servers. Thanks to the Wayback Machine, I managed to retrieve his comment. Here it is in it’s entirety:

Thanks for the info Peter!

I think it can be simplified still. The script block goes in the landing page as you have it, but you don’t need to repeat the ga(‘create’, ‘xxx’) call on every navigation complete event. It should be sufficient to just call the ga(‘send’) from then on out, no?

In my case, I put this in my main.js:

router.on('router:navigation:complete', function (instance, instruction) {
    var viewName = instruction.fragment || 'landing';

    ga('send', 'pageview', {
        page: viewName,
        title: viewName
    });
});

It seems to be doing its job.

He then continues:

I mean call:

ga('create', 'UA-XXXXXXXX-X', 'example.com');

just once when the hosting page loads, after the google script snippet. That’s what I do.

George

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.