maxandriani / ngx-google-analytics

An easy way to use and configure Google Analytics on Angular 6+ applications
MIT License
120 stars 49 forks source link

If I use the above method, I am getting the below error on page load - #47

Open aman-koco opened 3 years ago

aman-koco commented 3 years ago

If I use the above method, I am getting the below error on page load - Empty tracking code for Google Analytics. Make sure to provide one when initializing NgxGoogleAnalyticsModule.

In the environment files for which I do not want to have GA, I have entered as blank i.e GA: '' Any idea how to resolve the error?

Originally posted by @aman-koco in https://github.com/maxandriani/ngx-google-analytics/issues/37#issuecomment-775147121

KeithGillette commented 3 years ago

I solved the conditional initialization issue by creating an AnalyticsService to manage ngx-google-analytics configuration after application bootstrap, per the recommendation on #37 and the coding hints on #40, combined with some searching on how to get a ComponentRef to the bootstrap component:

analytics.service.ts

import { ComponentRef, Inject, Injectable } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { GoogleAnalyticsInitializer, GoogleAnalyticsRouterInitializer, GoogleAnalyticsService, GtagFn, NGX_GOOGLE_ANALYTICS_SETTINGS_TOKEN, NGX_GTAG_FN } from 'ngx-google-analytics';
import { IGoogleAnalyticsSettings } from 'ngx-google-analytics/lib/interfaces/i-google-analytics-settings';

import { environment } from '../environments/environment';

@Injectable({providedIn: 'root'})
export class AnalyticsService {
  private isInitialized: boolean;

  public constructor(
    @Inject(NGX_GOOGLE_ANALYTICS_SETTINGS_TOKEN) private googleAnalyticsSettingsToken: IGoogleAnalyticsSettings,
    @Inject(NGX_GTAG_FN) private googleTagFunction: GtagFn,
    @Inject(DOCUMENT) private document: Document,
    public googleAnalyticsService: GoogleAnalyticsService,
  ) {
  }

  public initializeGoogleAnalytics(bootstrapComponent: ComponentRef<unknown>) {
    if (environment.production && !this.isInitialized) {
      Promise.all([
        GoogleAnalyticsInitializer(this.googleAnalyticsSettingsToken, this.googleTagFunction, document)(),
        GoogleAnalyticsRouterInitializer({}, this.googleAnalyticsService)(bootstrapComponent),
      ]).then(() => {
        this.isInitialized = true;
      }).catch((error: any) => {
        this.isInitialized = false;
      });
    }
  }
}

app.module.ts:

import { APP_BOOTSTRAP_LISTENER, ComponentRef, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NGX_GOOGLE_ANALYTICS_SETTINGS_TOKEN } from 'ngx-google-analytics';

import { environment } from './core/environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AnalyticsService } from './core/services/analytics.service';

@NgModule({
  // Providers: Services and Values available to the Angular dependency injector
  providers: [
    {
      provide: NGX_GOOGLE_ANALYTICS_SETTINGS_TOKEN,
      useValue: {
        trackingCode: environment.GoogleAnalyticsCode,
      },
    },
    {
      provide: APP_BOOTSTRAP_LISTENER,
      multi: true,
      useFactory: (analyticsService: AnalyticsService) => {
        return (component: ComponentRef<unknown>) => {
          analyticsService.initializeGoogleAnalytics(component);
        };
      },
      deps: [AnalyticsService],
    },
  ],
  // Declarations: Components, Pipes, Directives belonging to this module & thereby available to others in this module
  declarations: [
    AppComponent,
  ],
  // Imports: Other Modules whose Declarations should be available to components in this module
  imports: [
    // …
    BrowserModule,
    AppRoutingModule,
  ],
  // Bootstrap: initial component to load when initializing application
  bootstrap: [AppComponent],
})
/** Angular root module */
export class AppModule {
  public constructor(
  ) {
  }
}
maxandriani commented 3 years ago

@KeithGillette The only down side is you can not make any call to GoogleAnalyticsService before call the initialize functions :/ Currently I have no time to make new features on this lib because I've applied to a Certification Program and I also have a full-time job.

I think your guys could implement a hidden property on ConfigurationToken w/ a Subject<'initialized'|'consent'|'initialize'>(); And then refactory GoogleAnalyticsService and initialization functions to listen this observable.

The GoogleAnalyticsService should also stack the commands if ga is not initialized and than call all stacked commands.

KeithGillette commented 3 years ago

Yes, @maxandriani, I realized that limitation, so in my application, I added methods to the AnalyticsService from my previous reply that proxy the GoogleAnalytics.event and GoogleAnalytics.exception methods that I want to use, but guard against calls if (!this.isInitialized).

The ngx-google-analytics library refactoring you describe makes sense and would be a great enhancement to easily enable delayed initialization for the multiple use cases of disabling outside of production, getting user consent, and perhaps also including an app-generated userId for authenticated sessions, which is something I'm trying to figure out how to do now …

alexsanderluisdev commented 3 years ago

I'm trying this code but it doesn't detect the change of routes. The Analytics dashboard only shows the first page and no longer updates. Can you help me?

andriipaziuk commented 2 years ago

@KeithGillette did you figure out how to set userId?

KeithGillette commented 2 years ago

@andriipaziuk — Not in any automated way. I am only logging sign_up & login GA actions via the proxied event method (described in my previous reply), which is called in the response handlers for the methods that invoke our application's UserRegister and UserSignIn mutations, both of which return a user object containing an id, so those calls pass in User as category and the user id as label.

xCryzed commented 2 years ago

@KeithGillette how do you call the initializeGoogleAnalytics method of AnalyticsService then?

KeithGillette commented 2 years ago

@xCryzed — The code sample in my original reply to this thread shows how initializeGoogleAnalytics is called in the AppModule provider creation.

xCryzed commented 2 years ago

@KeithGillette yes but what if I want to initialize it only when the user consent to the cookies?

KeithGillette commented 2 years ago

@xCryzed — Does issue #40 referenced in my original reply address your question?