angulargoogle-analyticsgoogle-tag-managergoogle-analytics-4

How to add variable google analytics ids to index.html


I'm trying to add a Google Analytics config value to index.html. This value will differ from staging and production, so it should be configurable, preferrably via an env file. I've seen some answers here in SO about this, but they're all very old (4+ years), so there may be a different way in modern Angular.

Basically, I need to make the following code accept variables:

<script>
    window.dataLayer = window.dataLayer || [];
    function gtag() {
      dataLayer.push(arguments);
    }
    gtag('js', new Date());

    gtag('config', <google analytics id>);
  </script>
  <!-- Google Tag Manager -->
  <script>
    (function (w, d, s, l, i) {
      w[l] = w[l] || [];
      w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
      var f = d.getElementsByTagName(s)[0],
        j = d.createElement(s),
        dl = l != 'dataLayer' ? '&l=' + l : '';
      j.async = true;
      j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
      f.parentNode.insertBefore(j, f);
    })(window, document, 'script', 'dataLayer', <google tag manager id>);
  </script>

Solution

  • I solved this issue by injecting the scripts from a service. Here's the full code:

    declare let gtag: any; // put this before the @Injectable decorator
    ...
    // Below are functions of the analytics service
    useGA(): boolean {
      return Boolean(
        environment.googleAnalyticsId &&
          environment.googleAnalyticsId.trim() !== '' &&
          environment.googleTagManagerId &&
          environment.googleTagManagerId.trim() !== '' &&
          typeof gtag !== 'undefined',
      );
    }
    
    loadBasicScripts() {
        if (!this.useGA()) return;
        const head = document.getElementsByTagName('head')[0];
        if (head !== null && head !== undefined) {
          const googleAnalyticsSrc = document.createElement('script');
          googleAnalyticsSrc.async = true;
          googleAnalyticsSrc.src = `https://www.googletagmanager.com/gtag/js?id=${environment.googleAnalyticsId}`;
    
          const googleAnalyticsScript = document.createElement('script');
          googleAnalyticsScript.type = 'text/javascript';
          googleAnalyticsScript.innerHTML = `
          window.dataLayer = window.dataLayer || [];
          function gtag() {
            dataLayer.push(arguments);
          }
          gtag('js', new Date());
    
          gtag('config', '${environment.googleAnalyticsId}');
        `;
    
          const googleTagManagerScript = document.createElement('script');
          googleTagManagerScript.type = 'text/javascript';
    
          googleTagManagerScript.innerHTML = ` (function (w, d, s, l, i) {
          w[l] = w[l] || [];
          w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
          var f = d.getElementsByTagName(s)[0],
            j = d.createElement(s),
            dl = l != 'dataLayer' ? '&l=' + l : '';
          j.async = true;
          j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
          f.parentNode.insertBefore(j, f);
        })(window, document, 'script', 'dataLayer', '${environment.googleTagManagerId}');
        `;
    
          document.head.appendChild(googleAnalyticsSrc);
          document.head.appendChild(googleAnalyticsScript);
          document.head.appendChild(googleTagManagerScript);
        }
        const body = document.getElementsByTagName('body')[0];
        if (body !== null && body !== undefined) {
          const googleAnalyticsTagNoScript = document.createElement('noscript');
          googleAnalyticsTagNoScript.innerHTML = `
          <iframe
            src="https://www.googletagmanager.com/ns.html?id=${environment.googleTagManagerId}"
            height="0"
            width="0"
            style="display: none; visibility: hidden"
          ></iframe>
        `;
    
          document.body.appendChild(googleAnalyticsTagNoScript);
        }
      }
    
    // use the script loader in the app.component.ts:
    ngOnInit(): void {
      this.analyticsService.loadBasicScripts();
    }