diff --git a/generators/app/templates/sources/main/_main.constants.ts b/generators/app/templates/sources/main/_main.constants.ts index fce3809..cf51494 100644 --- a/generators/app/templates/sources/main/_main.constants.ts +++ b/generators/app/templates/sources/main/_main.constants.ts @@ -9,6 +9,7 @@ export interface IApplicationConfig { export interface IApplicationEnvironment { debug: boolean; + googleAnayticsId: string; server: IServerConfig; } @@ -19,6 +20,10 @@ let environment = { local: { debug: true, + // Google Analytics account. Leave null to not have any analytics active. + // Typical values are of the form 'UA-########-1', where each # is a digit. + googleAnayticsId: null, + // REST backend configuration, used for all web services using restService server: { url: '', @@ -27,6 +32,7 @@ let environment = { }, production: { debug: false, + googleAnayticsId: null, server: { <% if (props.target === 'web') { -%> url: '', diff --git a/generators/app/templates/sources/main/_main.run.ts b/generators/app/templates/sources/main/_main.run.ts index e07eeca..9f5a3dc 100644 --- a/generators/app/templates/sources/main/_main.run.ts +++ b/generators/app/templates/sources/main/_main.run.ts @@ -1,6 +1,7 @@ import app from 'main.module'; import {IApplicationConfig} from 'main.constants'; import {RestService} from 'helpers/rest/rest.service'; +import {AnalyticsService} from 'helpers/analytics/analytics.service'; <% if (props.target !== 'web') { -%> import {ILogger, LoggerService} from 'helpers/logger/logger'; <% } -%> @@ -11,6 +12,7 @@ import {ILogger, LoggerService} from 'helpers/logger/logger'; */ function main($window: ng.IWindowService, $locale: ng.ILocaleService, + $location: ng.ILocationService, $rootScope: any, $state: angular.ui.IStateService, <% if (props.target !== 'web') { -%> @@ -26,7 +28,8 @@ function main($window: ng.IWindowService, <% if (props.target !== 'web') { -%> logger: LoggerService, <% } -%> - restService: RestService) { + restService: RestService, + analyticsService: AnalyticsService) { /* * Root view model @@ -84,6 +87,21 @@ function main($window: ng.IWindowService, updateTitle($state.current.data ? $state.current.data.title : null); }); + /** + * Enables tracking by analytics service. + */ + // HACK : ignore the first $viewContentLoaded event because it's actually fired once when uiView is instantiated, + // and then it's fired a second time after is has been linked. This is "by design" :-/ + // (http://stackoverflow.com/questions/31000417/angular-js-viewcontentloaded-loading-twice-on-initial-homepage-load) + let loadedOnce = false; + vm.$on('$viewContentLoaded', function () { + if (!loadedOnce) { + loadedOnce = true; + } else if (analyticsService) { + analyticsService.trackPage($location.url()); + } + }); + init(); /* diff --git a/generators/app/templates/sources/main/helpers/analytics/analytics.service.ts b/generators/app/templates/sources/main/helpers/analytics/analytics.service.ts new file mode 100644 index 0000000..a23f33e --- /dev/null +++ b/generators/app/templates/sources/main/helpers/analytics/analytics.service.ts @@ -0,0 +1,82 @@ +import app from 'main.module'; +import {ILogger, LoggerService} from 'helpers/logger/logger'; +import {IApplicationEnvironment} from 'main.constants'; + +const analyticsScriptUrl = '//www.google-analytics.com/analytics.js'; + +interface IWindowWithAnalytics extends ng.IWindowService { + ga: any; +} + +/** + * Analytics service: insert Google Analytics library in the page. + */ +export class AnalyticsService { + + private logger: ILogger; + private analyticsAreActive = false; + + constructor(private $window: IWindowWithAnalytics, + private config: IApplicationEnvironment, + logger: LoggerService) { + + this.logger = logger.getLogger('analyticsService'); + + this.init(); + } + + /** + * Tracks a page change in google analytics. + * @param {String} url The url of the new page. + */ + trackPage (url: string) { + if (this.analyticsAreActive) { + let urlWithoutParams = url; + let split = url.split('?'); + if (split.length > 1) { + urlWithoutParams = split[0]; + } + this.$window.ga('send', 'pageview', urlWithoutParams); + } + } + + /** + * Sends a track event to google analytics. + * @param {String} category The category to be sent. + * @param {String} action The action to be sent. + * @param {String=} label The label to be sent. + */ + trackEvent (category: string, action: string, label?: string) { + if (this.analyticsAreActive) { + this.$window.ga('send', 'event', category, action, label); + let logMessage = 'Event tracked: ' + category + ' | ' + action; + if (label) { + logMessage += ' | ' + label; + } + this.logger.log(logMessage); + } + } + + private init(): void { + if (this.config.googleAnayticsId !== null) { + this.createGoogleAnalyticsObject(this.$window, document, 'script', analyticsScriptUrl, 'ga'); + this.$window.ga('create', this.config.googleAnayticsId, 'auto'); + this.analyticsAreActive = true; + } + } + + private createGoogleAnalyticsObject(i: any, s: any, o: any, g: any, r: any, a?: any, m?: any) { + i.GoogleAnalyticsObject = r; + i[r] = i[r] || function () { + (i[r].q = i[r].q || []).push(arguments); + }; + i[r].l = new Date(); + a = s.createElement(o); + m = s.getElementsByTagName(o)[0]; + a.async = 1; + a.src = g; + m.parentNode.insertBefore(a, m); + } +} + +app.service('analyticsService', AnalyticsService);