Angular event tracking
With the rise in popularity for single page apps, we’ve been getting a lot of questions regarding what is the proper way to implement Segment into an Angular app. While we do not have an official library, many people have used Angularitics successfully to track various data from their Angular app.
UPDATE: We recently came across a new Segment-Angular module that is very well documented, tested, and maintained!
However, if you don’t want to rely on a third party plugin but would rather use Segment’s Analytics.js library with maximum control, it might be a good idea to learn how to implement it properly into your single page app. This article will cover the main events you can track through Segment: page, identify, and track.
It’s going to look something like this:
Important: Before we paste this into the head tag of your website, we first need to extract the analytics.page() at the bottom of the script. We will cover why in the next section.
Once you have removed the initial page call and pasted the snippet at the bottom of the head tag (after angular scripts), we’re ready to start tracking and sending data through Segment!
Why do we need to remove the initial page call inside the snippet? This is because Angular only loads the actual HTML page once. Even though you may be navigating to different “routes” or “states”, it doesn’t reload the actual HTML page and thus will not reload the scripts inside the head tag.
This means that each time you switch states, we need to manually call page inside your Angular modules to properly track real page views. So let’s get to it!
1. Bind the page call on your router’s broadcast
If we want to track a page view every time we switch to another route or a state, we can bind the page call to either $stateChangeSuccess() or $routeChangeSuccess depending on the router you use (ui-router vs. ngRoute). These are broadcasted each time there is a successful state or route change. The example below is assuming you are using ui-router rather than ngRoute for your routing:
This will track a page event each time you change states so you don’t have to add the `.page()` call to all your controllers manually.
2. Manually pass in the `path` , `referrer`, and the `url`
Since Angular has its own concept of states separate of the browser’s document, we want to make sure we supply the page call with context that only Angular knows. This is why you want to correctly parse the current state’s path, referrer, search, and url and pass them in manually. We will have to need to use $location service in this implementation:
You can see between line 8–10 we are checking to see if the current path has query parameters and setting that to the querystring variable. Line 13-15 is checking if there was a previous state and setting that as the referrer variable. The fromState variable has no functions to get the full absolute url so we are using a combination of $location.protocol(), $location.host() and fromState.url to create the full URL.
This implementation will properly send the actual context of an Angular state each time your users navigate through your app. A sample request you might see in your debugger would be:
Tracking User Identity
The next important thing is to identify your users. Our general recommendation is to fire the identify call after a user registers, logs in or visits any page that a logged in user can see. You want to identify on every state that a user can see because you never know where a user might start their session within your app and it’s important to identify them as soon as possible during their session.
Let’s go ahead and see how we might identify in each of those situations.
1. Identifying users after registration or login
Assuming that you are handling the registration/login flow within your controller, you will want to identify the user in the success callback of the ajax request that is registering or logging in the user. Let’s assume we are using a custom authFactory that returns an $http promise object:
Let’s walk through what’s going on here. Line 6, we are attaching an empty JSON object to the $scope so that you can use ng-model on the form inputs of your signup/login page. In line 9 we’re defining the function that will send the ajax request to register the user. Assuming that you extracted the ajax request inside your own custom factory that uses $http, we can identify the user in its success callback.
Assuming that your server returns the user data in the success response object, let’s set that to user on line 13. Now from line 16-20, we’ll call identify with the user.id and throw in some user traits. Once the user has been identified, we can supply a callback function inside the identify call and redirect to another state.
Redirecting in the callback of the identify call can prevent issues where some data might be missing due to the network request not having enough time to fire the event before the redirect occurs. The above implementation will ensure that the identify call was successfully made prior to changing the state.
This response handling can be applied for your login flow as well.
2. Identifying users on states that are only accessible by logged in users
Given that you allow users to stay logged in over multiple sessions, it’s important to identify as soon as a user visits any of the secured states/pages within your app. Since we don’t want to manually add an identify call to every controller that is in charge of these secured states, we can use the similar implementation for tracking page events with the $stateChangeSuccess, but this time we will use $stateChangeStart.
Note: you can still use $stateChangeSuccess — this is just preference of when exactly you want to identify your user, before they access the state or after.
We’re going to be using ngStorage to access the localStorage.
Let’s take a deeper look at this piece by piece. First thing we need to do is create a function that returns a boolean value of whether the state you are going to is a secured page that only a logged in user can see. This is what we are doing in line 2–5. Any state names you add to the whiteList will omit the identify call since you probably don’t have user information in those states.
Second thing we need is some implementation that will give us a boolean value of whether or not a user is currently logged in. I’ll leave that up to you since there are many ways to do that.
Lastly, on line 18, we want to add an if block that only executes if the state you are going to is secured and the user is authenticated. Assuming that you are saving your user data on the client such as localStorage, we’re going to retrieve that data on line 20 and pass that in our identify call on lines 22-26.
That’s it! Now you are identifying your users in your Angular app! Woo!
Tracking Custom User Actions
Now that the basics are covered, we want to make sure we are properly tracking custom actions by your users. Now since there are an infinite number of track events you can collect, I’m going to cover some common ones such as:
- Button Clicks / Event listeners
- Form Submitted
Let’s do it!
Button Clicks / Event listeners
The best way to send data based on some eventListener is to create a function that sends the event to Segment and bind it to the controller’s $scope and trigger with ng-click in your view:
Pretty basic stuff here. We’ve attached a function to $scope that takes an event name and sends a track event to Segment with that name. You can opt to add whatever custom properties you might want to send as well on line 8.
Lastly, we simply listen for a click event on the button we want to track and invoke the function by passing in a custom track even name.
The key things you want to do here is to use two-way data binding for the form inputs and make sure that the track call is fired when a user successfully submit the form (not just when someone clicks the submit button in case the form failed validation):
Just like how we handled the identify call during the signup/login flow, you want to make sure you’re tracking the form submission in the success callback of whatever function is actually making the ajax request to your servers.
On line 6 we define a function that can take the name of the form. Once we return from the servers and the form was successfully processed, we’re going to make the track event name a bit more verbose so that on line 12 we will be tracking the event SignUp Form Submitted and pass in the user inputs as the track properties.
Simple as that!
Few Last Words
The most important thing to remember when using Segment in a single page app such as Angular is that you want to make sure that no matter what event you track, you are executing the tracking code when that event actually occurred in your app.
For example, if you only cared about tracking how many times a user attempted to submit a form, it might suffice to fire the track on ng-click . However, if you want to track that a form was successfully submitted, you want to put the tracker code after the form was processed in a callback function.