Skip to content
This repository was archived by the owner on Sep 9, 2024. It is now read-only.

Commit b4522cc

Browse files
committed
feat(docs-infra): set up Google Analytics 4 along with keeping legacy Universal Analytics
We currently use Universal Analytics. This is deprecated in favor of Google Analytics 4 and UA will stop processing hits in October 2023. This change intends to prepare us for this migration, and to already pre-populate our GA4 property (there is no way to migrate existing data /properties into a GA4 property -- a new one needs to be created). This will help us minimize the data gap so that we can: * Continue to look at the UA property with the full time span until October 2023 * Can start using the GA4 property long-term in the future, starting with data even before Universal Analytics stops processing new data. We need to keep the existing `analytics.js` setup. Initially we have considered using `gtag.js` for both the UA and GA4 properties, as it supports that, but that doesn't work with our strict trusted types enforcement because it results in multiple `gtag.js` scripts (specific versions for UA or GA4) that recreate the same trusted type policies. This causes runtime errors and breaks the setup. Instead, with continued use of `analytics.js` we have the benefit of a good separation of trusted types + events and configuration. There is some problematic with translation of Universal Analytics Events to GA4, or the other way around (even though we don't use custom events currently) We also do not need to send page views for our GA4 property because GA4 with gtag supports this automatically (respecting the history state -- using the `Enhanced measurement events` setting in the UI). For our UA legacy instance we continue to dispatch events manually. This logic can be removed in the future. More details can be found here: https://docs.google.com/document/d/1aK8u4ZlXbqQ2wMqmgSX7Ces8iLgamC13oCoG6VeBruA/edit?usp=sharing&resourcekey=0-EVe-Rhnme3bj_pkz2RcOmw.
1 parent e9d6277 commit b4522cc

File tree

5 files changed

+73
-21
lines changed

5 files changed

+73
-21
lines changed

angular.json

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"index": "src/index.html",
1515
"main": "src/main.ts",
1616
"tsConfig": "src/tsconfig.app.json",
17+
"optimization": false,
1718
"polyfills": "src/polyfills.ts",
1819
"assets": [
1920
{
@@ -27,9 +28,7 @@
2728
"output": "/"
2829
}
2930
],
30-
"styles": [
31-
"src/styles.css"
32-
],
31+
"styles": ["src/styles.css"],
3332
"scripts": []
3433
},
3534
"configurations": {
@@ -82,9 +81,7 @@
8281
"polyfills": "src/polyfills.ts",
8382
"tsConfig": "src/tsconfig.spec.json",
8483
"scripts": [],
85-
"styles": [
86-
"src/styles.css"
87-
],
84+
"styles": ["src/styles.css"],
8885
"assets": [
8986
{
9087
"glob": "**/*",
@@ -102,10 +99,7 @@
10299
"lint": {
103100
"builder": "@angular-devkit/build-angular:tslint",
104101
"options": {
105-
"tsConfig": [
106-
"src/tsconfig.app.json",
107-
"src/tsconfig.spec.json"
108-
],
102+
"tsConfig": ["src/tsconfig.app.json", "src/tsconfig.spec.json"],
109103
"exclude": []
110104
}
111105
},
@@ -129,4 +123,4 @@
129123
"prefix": "app"
130124
}
131125
}
132-
}
126+
}

src/app/analytics.service.ts

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,70 @@
1-
import { Injectable } from '@angular/core';
1+
import { Inject, Injectable } from '@angular/core';
22

3-
declare var ga: any;
3+
import { environment } from '../environments/environment';
44

5-
@Injectable({
6-
providedIn: 'root'
7-
})
5+
/** Extension of `Window` with potential Google Analytics fields. */
6+
declare global {
7+
interface Window {
8+
dataLayer?: any[];
9+
gtag?(...args: any[]): void;
10+
/** Legacy Universal Analytics `analytics.js` field. */
11+
ga?(...args: any[]): void;
12+
}
13+
}
14+
15+
@Injectable({ providedIn: 'root' })
16+
/**
17+
* Google Analytics Service - captures app behaviors and sends them to Google Analytics.
18+
*
19+
* Note: Presupposes that the legacy `analytics.js` script has been loaded on the
20+
* host web page.
21+
*
22+
* Associates data with properties determined from the environment configurations:
23+
* - Data is uploaded to a legacy Universal Analytics property
24+
* - Data is uploaded to our main Google Analytics 4+ property.
25+
*/
826
export class AnalyticsService {
27+
constructor() {
28+
this._installGlobalSiteTag();
29+
}
30+
31+
send(name: string, value: string | boolean | number) {
32+
this._legacyGa('send', 'event', name, value);
33+
this._gtag('event', name, { value });
34+
}
935

10-
constructor() { }
11-
send(category: string, action: string) {
12-
ga('send', 'event', category, action);
36+
private _gtag(...args: any[]) {
37+
if (window.gtag) {
38+
window.gtag(...args);
39+
}
1340
}
1441

42+
private _legacyGa(...args: any[]) {
43+
if (window.ga) {
44+
window.ga(...args);
45+
}
46+
}
47+
48+
private _installGlobalSiteTag() {
49+
const url = `https://www.googletagmanager.com/gtag/js?id=${environment.googleAnalyticsId}`;
50+
51+
// Note: This cannot be an arrow function as `gtag.js` expects an actual `Arguments`
52+
// instance with e.g. `callee` to be set. Do not attempt to change this and keep this
53+
// as much as possible in sync with the tracking code snippet suggested by the Google
54+
// Analytics 4 web UI under `Data Streams`.
55+
window.dataLayer = window.dataLayer || [];
56+
window.gtag = function () {
57+
window.dataLayer?.push(arguments);
58+
};
59+
window.gtag('js', new Date());
60+
61+
// Configure properties before loading the script. This is necessary to avoid
62+
// loading multiple instances of the gtag JS scripts.
63+
window.gtag('config', environment.googleAnalyticsId, { debug_mode: true });
64+
65+
const el = window.document.createElement('script');
66+
el.async = true;
67+
el.src = url;
68+
window.document.head.appendChild(el);
69+
}
1570
}

src/environments/environment.prod.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export const environment = {
2-
production: true
2+
googleAnalyticsId: 'G-BVV0RDSG7F',
3+
production: true,
34
};

src/environments/environment.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
// The list of which env maps to which file can be found in `.angular-cli.json`.
55

66
export const environment = {
7-
production: false
7+
googleAnalyticsId: 'G-Q8PB6PJ5CC',
8+
production: false,
89
};

src/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
2222

2323
ga('create', 'UA-8594346-15', 'auto');
24+
ga('set', 'anonymizeIp', true);
2425
ga('send', 'pageview');
2526

2627
</script>

0 commit comments

Comments
 (0)