今回はGoogleAnalyticsのコードをAngularのプロジェクトに埋め込む話
SPAなので単に計測コードを貼り付けただけでは計測できないのでよしなにする必要がある
ライブラリも探せばあったがあまり頻繁に更新されてなかったりとちょっと不安要素があったので今回は自前で入れてみる
ただ単に入れましたーみたいな記事は探せば結構あったので今回はstrict: true
でも導入できるようにする
始めに
GoogleAnalyticsの管理画面から取得できる計測コードは下記(2019年10月現在)
<!-- Global site tag (gtag.js) - Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-xxxxxxxx-x"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-xxxxxxxx-x'); </script>
単純に計測コードをindex.html
に貼るだけだととりあえず初回のロード時だけは計測できる、が…初回ロードだけ計測できても…って感じではありますね
今回は下記2点を実現したいので工夫する必要がある
- 開発時は何も設定しない+本番のときのみtrackingのIDを入れ込みたい
- ページ遷移ごとに計測をしたい
SPAに関しての公式のページを見ると
gtag.js を使用した単一ページ アプリケーションの測定 | ウェブ向けアナリティクス(gtag.js)
コンテンツが動的に読み込まれてアドレスバーの URL が更新された時点で、gtag.js で保存されているページ URL も更新する必要があります。
とあります
ということでページが変わったらその都度config
を指定する必要があるようです
環境によってIDを読み込ませる
まずは、本番と開発でIDを切り替える(開発では入れない)ようにする
environment.ts
にgaCode
という名前でIDを記述する
- src/environments/environments.ts
export const environment = { production: false, gaCode: '' };
- src/environments/environment.prod.ts
export const environment = { production: true, gaCode: 'UA-xxxxxxxx-x' };
Angular側で本番のときはprodに記述した内容を適用してくれるので今回はこんな感じでOK
scriptタグの動的差し込み
素直にapp.component.html
に計測タグを貼り付けてしまうとAngularがコンパイル時にscript
タグを除いてしまうのでコンポーネント読み込み時に動的に差し込む必要がある
- app.component.ts(一部抜粋)
export class AppComponent implements OnInit { constructor( private renderer: Renderer2, @Inject(DOCUMENT) private document: Document ){ } ngOnInit(){ const s1 = this.renderer.createElement('script'); s1.type = 'text/javascript'; s1.src = `https://www.googletagmanager.com/gtag/js?id=${environment.gaCode}`; this.renderer.appendChild(this.document.head, s1); const s2 = this.renderer.createElement('script'); s2.type = 'text/javascript'; s2.text = `window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments)};gtag('js', new Date());gtag('config', '${environment.gaCode}');`; this.renderer.appendChild(this.document.head, s2); } }
AngularのRenderer2経由でscriptタグを差し込む
型定義を読み込む
1から型の定義をしていくのはつらいのですでに型定義されているものがないか探してみたところ下記がそれっぽい感じ
DefinitelyTyped/index.d.ts at master · DefinitelyTyped/DefinitelyTyped
- install
npm install --save-dev @types/gtag.js
中身を見るとgtag
がexport
されていないのでよしなに型情報を引っ張りだしてくる必要はあるがなんとかできそう
ここを読む感じだとtypes
に入れるとデフォルトでincludeしてくれるよう
gtag.jsの中身で var gtag
を定義しているので自動で読み込んだらどこでもgtag
を参照できるようになる
- tsconfig.app.json
{ "compilerOptions": { "types": ["gtag.js"] } }
これでgtag
が呼び出せる様になったので
type Gtag = typeof gtag;
とすればGtag
の型が取り出せる
あとはよしなにページが切り替わったタイミングでconfigのパスを書き換える
- app.component.ts
import { Component, Inject, OnInit, Renderer2 } from '@angular/core'; import { environment } from '../environments/environment'; import { DOCUMENT } from '@angular/common'; import { NavigationEnd, Router } from '@angular/router'; type Gtag = typeof gtag; type WindowWithGtag = Window & { gtag: Gtag }; ..... ..... ..... export class AppComponent implements OnInit { private windowWithGtag!: WindowWithGtag; constructor( private renderer: Renderer2, private router: Router, @Inject(DOCUMENT) private document: Document ) { this.windowWithGtag = window as WindowWithGtag; } ngOnInit(){ ..... ..... ..... this.router.events.subscribe(event => { if (event instanceof NavigationEnd) { this.windowWithGtag.gtag('config', environment.gaCode, { page_path: event.urlAfterRedirects }); } }); const s1 = this.renderer.createElement('script'); s1.type = 'text/javascript'; s1.src = `https://www.googletagmanager.com/gtag/js?id=${environment.gaCode}`; this.renderer.appendChild(this.document.head, s1); const s2 = this.renderer.createElement('script'); s2.type = 'text/javascript'; s2.text = `window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments)};gtag('js', new Date());gtag('config', '${this.gaCode}');`; this.renderer.appendChild(this.document.head, s2); }
page_path
に渡している部分urlAfterRedirects
はAngular側でリダイレクトした場合のリダイレクト後のパス
無事ページごとのアクセスを計測することが可能になりました
まとめ
今回はGoogleAnalyticsを題材として下記を行った
- 型定義情報を引っ張りだす(exportされてないものでも取得できる)
- windowオブジェクトに特定のプロパティを追加してTypeScript側から呼び出せるようにする
計測系のコードはwindowオブジェクトに対して色々追加してよしなにやるっていうパターンが多いので、この方法であれば他の計測コードとかも使えるかと思います