Skip to content

Conversation

@bursauxa
Copy link

@bursauxa bursauxa commented Jan 4, 2017

Added analytics service in /helpers. This services supports basic page tracking (automatically triggered on view change, so the function never has to be called manually) and event tracking by calling the provided function.

Copy link
Member

@sinedied sinedied left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of this PR and its simplicity, thanks for the proposal 👍

Even though more complete libraries like angulartics, having the possibility to quickly add GA page/event tracking through a simple single file service.

After the PR is fixed, I'll merge it to separate feature branch before going to master so it can be enabled or not via a Yeoman prompt, and directly enter the GA account ID from there.

// 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 () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use $stateChangeSuccess like the hook to update title on route changes?
This should fix the need for the "hack" :)

Copy link
Author

@bursauxa bursauxa Jan 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per ui-router documentation (link) :

State change events are deprecated, DISABLED and replaced by Transition Hooks as of version 1.0

Note that the view load events are not deprecated. So maybe the real solution is to look into transition hooks ? But that's a different beast altogether.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOL, missed that since we are still on the 0.3.x version and the 1.x version has not been finalized yet 😅

I have created a separate issue for the migration to using $transitions hooks (see #38 ), for the meanwhile it's still a valid change (until 1.0.0 version is out) that will get rid of the ugly hack, and it will simplify the migration task, as it will be needed anyway as we use it elsewhere.
Thanks for pointing that!


private init(): void {
if (this.config.analyticsAccount !== null) {
let analyticsScriptUrl = '//www.google-analytics.com/analytics.js';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be better to define it as a const on the beginning of the file

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

private init(): void {
if (this.config.analyticsAccount !== null) {
let analyticsScriptUrl = '//www.google-analytics.com/analytics.js';
this.createGoogleAnalyticsObject(window, document, 'script', analyticsScriptUrl, 'googleAnalytics');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use this.$window instead of window

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last parameter should be 'ga' instead of 'googleAnalytics' so other libraries depending on google analytics can also use it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both done (although replacing an explicit field name with an obfuscated one does not sit right with me).

private config: IApplicationConfig,
logger: LoggerService) {

this.logger = logger.getLogger('analyticsService');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger not needed if it's not used, or maybe it could be used to log tracked events?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely, logging tracked events was the point. I must have lost it somewhere along the road ; will fix it now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done earlier (with previous changes).


this.logger = logger.getLogger('analyticsService');

this.init();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer a public init() method not called in constructor but in app init (in main.run.ts) explicitely, so we can also pass custom GA configuration as an optional argument for the create call.

This is also mandatory for mobile/Cordova compatibility, as specific configuration is needed for GA to work with file:// protocol used in Cordova.

This would also allow us to disable analytics in debug environment, as it is most of the time only wanted on production and it can mess with unit tests by generating extra HTTP calls.

See this example except from a mobile of mine, to make it work with Cordova (located in init() method of main.run.ts, it uses amgulartics but the idea is the same:

      // Setup analytics
      if (!config.environment.debug && $window['ga']) {
        // Warning, breaks unit tests if included in debug!
        $window['ga']('create', config.googleAnalyticsId, {
          'storage': 'none',
          'clientId': $cordovaDevice.getUUID()
        });
        // Allow file:// protocol for cordova
        $window['ga']('set', 'checkProtocolTask', (data: any) => {
          data.set('location', 'https://htf2016.app/');
        });
        $window['ga']('set', 'appVersion', config.version);
        $analytics.eventTrack('App started', { value: vm.festival.version });
      }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the responsibility of customizing the creation of the GA object lies within the analytics service. All the Cordova specific code can be added in analytics-service instead of main-run.

Regarding disabling of analytics in debug, there are some cases where having them on is useful (typically when one wants to test out a newly-added event tracker). Of course you would want a different analytics account for debugging and for actual production, but that is just a matter of setting up the gulp-replace task around IApplicationConfig.googleAnalyticsId. If you want to disable them entirely in debug mode, it can also be done the same way, by setting the value to null in debug mode.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the responsibility of customizing the creation of the GA object lies within the analytics service. All the Cordova specific code can be added in analytics-service instead of main-run.

I completely agree with that, I just posted it to show a mobile setup ;)

Regarding disabling of analytics in debug, there are some cases where having them on is useful (typically when one wants to test out a newly-added event tracker). Of course you would want a different analytics account for debugging and for actual production, but that is just a matter of setting up the gulp-replace task around IApplicationConfig.googleAnalyticsId. If you want to disable them entirely in debug mode, it can also be done the same way, by setting the value to null in debug mode.

In my idea disabling analytics in debug would be done in main.run.ts by calling or not the analyticsService.init(), so if needed the behavior can be changed. But it's a good idea to just check if googleAnalyticsId is defined to activate or not GA in the init() method, so the you just need to move the googleAnalyticsId in the environment object so there can be an value for each environment.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: googleAnalyticsId moved from Config to Environment.

}
}

app.service('analyticsService', AnalyticsService);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it is a service, unit tests are mandatory (though they will be quite simple as GA will be mocked) and should be added :)

version: string;
environment: IApplicationEnvironment;
supportedLanguages: Array<string>;
analyticsAccount: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be more explicit to rename it googleAnayticsId

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@sinedied
Copy link
Member

sinedied commented Jan 6, 2017

Do not forget the unit tests, so I can merge it ;)

@bursauxa
Copy link
Author

bursauxa commented Jan 6, 2017

I did not forget them, I just don't see how to proceed, considering than I can not actually mock GA (which is loaded from the Google URL by a private function). I will come back to this issue when I have more time on my hands.

@sinedied sinedied changed the base branch from master to feature/analytics January 13, 2017 18:02
@sinedied sinedied merged commit 4de8401 into angular-starter-kit:feature/analytics Jan 13, 2017
@sinedied
Copy link
Member

This PR has been merged on feature/analytics so I can add the generation prompt to opt in/out the service.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants