Injecting the Service into Our Pages
With the service initialized, we can now capture events on our pages.
As stated in the previous post, Google Analytics was designed to provide insights on user experience based on user interactions. It is not so much an Application Performance Monitor (APM) like Application Insights or New Relic (while it can provide some extremely basic metrics), but rather to assist internal teams with increasing conversion rates and improving customer acquisition (and retention). To this end, we can track those interactions and log them in Google Analytics. In this post, we’re not going to cover goal setting, funnels, metrics, etc. We’re simply going to track a few events. However, the following principles can be used to accomplish the same purpose.
Add Event Logging Method
For our pages to log an event to Google Analytics, we’ll need to expose the functionality in our GoogleAnalyticsService
. Open your src/app/google-analytics.service.ts
file and add the following function:
logEvent(event, category, label, value) { gtag('event', event, { event_category: category, event_label: label, value: value }); console.log('gtag event captured...'); }
There’s really nothing special about this method. It simply takes a generic event, category, label, and value and then registers that information with Google Analytics.
You can see the full, completed source of google-analytics.service.ts
here.
Quick and Dirty
If you remember from the “Setup” section from page 1, I created a few pages. On both First Page and Second Page I created a simple button, and I bound it to a click
event.
<button (click)="click()">Click Me!</button>
View the source (for First Page) here.
In my page’s TypeScript file, I’ve injected my GoogleAnalyticsService
and, using the bound click
event, I call the service’s logEvent
method that we added in the previous section.
The following is the initial source for my first-page.component.ts
:
import { Component, OnInit } from '@angular/core'; import { GoogleAnalyticsService } from '../google-analytics.service'; @Component({ selector: 'app-first-page', templateUrl: './first-page.component.html', styleUrls: ['./first-page.component.scss'] }) export class FirstPageComponent implements OnInit { private _clickCnt = 0; constructor(protected $gaService: GoogleAnalyticsService) { } ngOnInit() { } click() { this.$gaService.logEvent('click', 'Button', 'First Page', ++this._clickCnt); console.log(`Button clicked ${this._clickCnt} times.`); console.log('Click for "First Page" sent!'); } }
The service is injected into my component (line 12) and it’s logEvent
method is called when the user clicks the button (line 18).
Now, this approach works. BUT, I would have to inject the service into every component page. A better approach would be to simply inject the GoogleAnalyticsService
once, which is what the next section demonstrates.
A Better Approach
Instead of injecting the service on every page, it is sometimes much simpler to inject the service into a base page/component and then inherit from that component.
Let’s create our base component (e.g. ng g c base-component
). This will create the necessary component file and its unit tests specification file. In the component file, we’ll pretty much scrub all the UI references and add our Google Analytics service:
import { Component, OnInit } from '@angular/core'; import { GoogleAnalyticsService } from '../google-analytics.service'; @Component({ template: '', }) export class BasePageComponent implements OnInit { constructor(protected $gaService: GoogleAnalyticsService) { } ngOnInit() { } }
Notice, we’ve removed the templateUrl
and stylesUrls
properties from the @Component
attribute, and we’ve replaced them with an empty template
property (which is required). (You can also delete the base-page.component.html
and base-page.component.scss
files if you’d like as they are no longer needed.) A copy of the base-page.component.ts
file can be found in the repository.
Now, instead of injecting the service into each component, we’ll simple modify our page components to inherit from our base component. The following is First Page‘s final source code.
import { Component, OnInit } from '@angular/core'; import { BasePageComponent } from '../base-page/base-page.component'; @Component({ selector: 'app-first-page', templateUrl: './first-page.component.html', styleUrls: ['./first-page.component.scss'] }) export class FirstPageComponent extends BasePageComponent implements OnInit { private _clickCnt = 0; ngOnInit() { } click() { this.$gaService.logEvent('click', 'Button', 'First Page', ++this._clickCnt); console.log(`Button clicked ${this._clickCnt} times.`); console.log('Click for "First Page" sent!'); } }
Notice, the changes:
- Instead of importing the
GoogleAnalyticsService
directly, we’re importing theBasePageComponent
- We’ve removed our component’s constructor (it’s no longer needed in this example)
- We’ve updated our
FirstPageComponent
class to extendBasePageComponent
The final version of the First Page‘s source can be found here.
(NOTE: As mentioned in the previous post, one downside of using Google Analytics is that the values for a specific event are summed in the daily reports. So, if the button on the page is clicked 3 times, the final event report will show 6 (1 for the first click + 2 for the second + 3 for the third) for the value. If you want to track values for individual users, sessions, clicks, etc., then you’ll need to configure metrics in Google Analytics.)
With your click event wired up, go ahead and run your application again (remember, with the --prod
flag). You’ll then begin to see events being logged in realtime on your Google Analytics dashboard.
Conclusion
Like the previous post, we’ve added Google Analytics to our Angular application. However, in this post, we’ve accomplished that by creating and injecting a service. This is considered the better, “more Angular” way to perform this.
Again, for the full source code to this project, check out my GitHub blog demos repository.
In your service change line 15 to read:
filter((e): e is NavigationEnd => e instanceof NavigationEnd)
see: https://stackoverflow.com/questions/49722369/angular-router-events-navigationend-how-to-filter-only-the-last-event
You could, but how I have the method currently written, it makes little difference as I’m not reusing
e
within the pipe chain.