Dodawanie Google Analytics do strony z Angularem 2+ [aktualizacja 12.05.2021]

Google Analytics Logo

Zadanie niby proste ale nie do końca. Zacznijmy od początku, dodawanie skryptu Google Analytics do statycznej stony nie należy do najtrudniejszych, polega ono na skopiowaniu kodu ze strony Google Analytics i wklejeniu go na samym początku znacznika <head>. Jednak jeśli chcemy to zrobić w aplikacja z Angularem, robi się ciut trudniej.

Kod dostępny jest na naszej stronie Google Analytics po wejściu w zakładkę „Administracja -> Informacja o śledzeniu -> Kod śledzenia”

Przykład:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=twoj-kod-Google-Analytics"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'twój-kod-Google-Analytics');
</script>

Stety niestetety w naszym przypadku sprawa trochę się komplikuję ponieważ strony w Angularze przełączane za pomocą serwisu Angular Router działają jak Single Page Application tzn. „podstawa” czyli między innymi znacznik <head> ładowany jest tylko raz przy pierwszym załadowaniu strony. Wtedy też za pomocą gtag('config', 'twój-kod-Google-Analytics'); wysyłamy informacje do Google Analytics. Podczas routingu zmienia się treść na stronie, „podstawa” nie przeładowuje się co skutkuje brakiem informacji o ruchach użytkownika w naszej aplikacji.

Jak to naprawiać? Musimy zaimplementować odpowiedni mechanizm który będzie wysyłał do Google Analytics informację o poczynaniach użytkownika. Wykorzystamy do tego metodę navigationEnd z serwisu Angular Router oraz jej propertkę urlAfterRedirects. Dzięki temu uzyskamy każdą zmianę adresu url. Aby wychwycić wszystkie ruchy użytkownika proponuję zaimplementować metodę w app.component.ts:

declare let gtag: Function;

export class AppComponent {
  constructor(public router: Router){   
      this.router.events.subscribe(event => {
         if(event instanceof NavigationEnd){
             gtag('config', 'twój-kod-Google-Analytics', 
                   {'page_path': event.urlAfterRedirects}
                  );
          }
       }
    )}
} 

[Aktualizacja 12.05.2021] Powyższy kod może powodować błąd „gtag is not defined”. Występuje on ponieważ dynamicznie generowany skrypt (opisany poniżej) może się załadować po komponencie AppComponent. Jak to naprawiać? Najlepiej przenieść naszą metodę wysyłającą do nowego serwisu:

import {Injectable} from '@angular/core';

declare let gtag: Function;

@Injectable({
  providedIn: 'root'
})
export class GoogleAnalyticsService {

  pageChange(googleAnalyticsCode: string, pagePath: string): void {
    gtag('config', googleAnalyticsCode, {'page_path': pagePath});
  }
}

Dzięki temu zyskamy na przejrzystości kodu i wyeliminujemy wspomniany błąd. Przykład wywołania:

this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd && this.googleAnalyticsCode) {
        this.googleAnalyticsService.pageChange(this.googleAnalyticsCode, event.urlAfterRedirects);
      }
    });

Dodatkowo do strony index.html należy dodać wcześniej już wspomniany skrypt ze stronny Google Analytics:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=twoj-kod-Google-Analytics"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
</script>

Jednak już bez gtag('config', 'twój-kod-Google-Analytics') ponieważ tą funkcję wysyłania mamy zaimplementowaną w app.components.ts. Nie zapomnijmy o dopisaniu w src pierwszego znacznik script naszego kodu Google Analytics.

I gotowe. Poprawność konfiguracji możemy łatwo zweryfikować wchodząc na nasze konto na stronie Google Analytics lub sprawdzić czy requesty są wysyłane prawidłowo np. w inspektorze Mozilla Firefox w zakładce „Sieć”:

Implementacja z dynamicznym kodem Google Analytics

Scenariusz przedstawiony powyżej zakłada, że kod śledzenia Google Analytics jest zahardcodowany, a co w przypadku jeśli chcielibyśmy, żeby był on przypisywany ze zmiennej?

W tym celu przygotujemy specjalną metodą, która wygeneruje skrypt taki sam jak dołączony w znaczniku head:

loadGoogleAnalytics(trackingID: string): void {

    const gtagScript = document.createElement('script');
    gtagScript.setAttribute('async', 'true');
    gtagScript.setAttribute('src', `https://www.googletagmanager.com/gtag/js?id=${ trackingID }`);

    const gtagScript2 = document.createElement('script');
    gtagScript2.innerText = `window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag(\'js\', new Date());`;

    document.documentElement.firstChild.appendChild(gtagScript);
    document.documentElement.firstChild.appendChild(gtagScript2);
  }

Przyjrzyjmy się dokładniej fragmentowi document.documentElement – ta propertka daje nam dostęp do elementu nadrzędnego co przekłada się także na dostęp do elementu <head>

<html lang="pl">
  <head>...</head>
  <body>...</body>
</html>

Dzięki propertce firstChild dostajemy się do elementu <head> a następnie poprzez metodę appendChild() dodajemy nasze wcześniej przygotowany skrypty. Voilà!

Nie zapomnijmy jeszcze na zakończenie wywołać metody loadGoogleAnalytics() w odpowiednim momencie, np. zaraz na początku konstruktora app.components.ts. Całość prezentuje się następująco:

See the Pen Angular Google Analytics by MichalZmu (@michalzmu) on CodePen.

To by było na tyle, jeśli mój mały poradnik wydał się Tobie interesujący lub masz inny pomysł jak rozwiązać opisany problem, daj znać w komentarzach!


Sprawdź inne moje wpisy: