For a while, in my professional work, I have been developing software applications using Angular. In a few cases I have faced questions about how one or another thing could be achieved and I have not found a quick and unambiguous answer for myself. In this post I will discuss two interesting topics of this nature:

  1. How dynamically, from Angular, to “download” in the app third-party JavaScript libraries
  2. How to “hook” to the initialization process of an Angular application and run our own script

For illustration purposes I have written a sample application that I will quote in several places in the post.

The code for the example application is available at: https://github.com/agyonov/NgInitGA

The app itself is live on: https://agyonov.github.io/NgInitGA

Dynamic download of a third-party JavaScript library

For most libraries that you would like to use, someone has already created, developed and maintains a NPM package with the relevant Angular modules and you could (and should) use them. But sometimes there is no ready NPM package or you may not like the one that is available. In this case you can directly reference the requested JavaScript library from your Angular application and use it.

There are two easy approaches that are described in the Angular documentation and, for example, in Google Analytics. And which don’t always do the job for one reason or another.

  1. The first, entirely Angular, solution is to download the corresponding .js file from the Internet. Add it to your project and place a reference to the .js library in the scripts array, in the angular.json config file. E.g:

…
"scripts": [
              "src/scripts/analytics.js"
            ]
…

This is described in detail in the documentation of Angular.

It’s an OK approach and will work.

A consideration against its possible use is that under this approach is that the .js script will be download from your hosting server and not from the original server (in this case https://www.google-analytics.com/analytics.js). And the question is not so much in increased traffic to your site and performance issues, but in that the author of the library (Google) could change things in it, have new versions, etc., and you will not have this new version until you re-build and re-publish your app.

2 . The second approach is to apply the instruction on the Google Analytics page:


<script>
window.ga=window.ga || function(){(ga.q=ga.q||[]).push(arguments)};
ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src='https://www.google-analytics.com/analytics.js'>
</script>

And the above code to be pasted directly into the index.html file initializing your Angular application. In particular, the last line of the above sample code.

It’s OK as an approach and will also work in its part to download the .js library. In addition, the objections which were valid for the first decision are irrelevant.

A consideration against its possible use is stylistic – it is not a “clean” Angular solution.

The rest of the code snip from Google Analytics, shown above, does not work for us at all! Because with Angular we have a Single Page Application (SPA) where, unlike traditional web applications, in reality only the first page is loaded from the Web server and subsequently every transition from page to page, occurs only inside the SPA application in the browser. Also, if we have any special events (e.g. button press) they as well occur inside the Angular app and will not affect the data in Google Analytics.

These issues are discussed in detail in the Google Analytics documentation itself under the Single Page Application Tracking topic.

3. Let’s get back to the issue with the dynamic download of the JavaScript library. There is a third method that I will look at. Because the example is using Google Analytics, I’ll expand it to be used to track users’ actions in the Angular app.

Note: As for tracking of users in Angular application, it has great, ready-made libraries, such as angulartics2, which you can use. Here the question is not to create another such library, but to illustrate with an example a possible solution to the two questions set at the beginning of this post!

So, the dynamic download. Let us see a sample of the following Angular service:


…
// Typescript extention of Window interface
declare global {
  interface Window { ga: any; }
}

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

  // The source for the load
  private googleAnalyticsScript= {
    loaded: false,
    url: 'https://www.google-analytics.com/analytics.js'
  };

  constructor(@Inject(PLATFORM_ID) private platformId: Object,
    @Inject(DOCUMENT) private dom: Document) {
  }
…
  // Init the GA infrastructure
  public loadScript(): void {
    // Check already loaded
    if (!this.googleAnalyticsScript.loaded) {
      // Check if we are at browser
      if (isPlatformBrowser(this.platformId)) {
        // Create new scipt element
        const scriptElm: HTMLScriptElement = this.dom.createElement('script');
        scriptElm.src = this.googleAnalyticsScript.url;
        scriptElm.type = 'text/javascript';
        scriptElm.async = true;
        scriptElm.onload = () => {
          // Prevent from load second time
          this.googleAnalyticsScript.loaded = true;
        };

        // Define GA object
        window.ga = window.ga || function () { (window.ga.q = window.ga.q || []).push(arguments); };
        window.ga.l = +new Date;

        // Set some Google Analytics stuff
        window.ga('create', environment.propertyID, 'auto');

        // Add to document
        this.dom.body.appendChild(scriptElm);
      }
    }
  }
}

Basically, it’s a transcription in TypeScript, of the instructions from the Google Analytics page, which we have above. The important points are:

  • Only in the beginning we expand the TypeScript Window interface with a new field-‘ga’
  • In the ‘loadScript’ method, the actual work is done:
    • A scriptElm script element is created and is dynamically added to the DOM of the Web page. It’s the same as putting:
<script async src='https://www.google-analytics.com/analytics.js'></script>

in index.html.

  • Definition of the ‘ga’ field with which the Window interface is expanded, as specified by Google Analytics. It’s the same as putting:
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};
ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
</script>

in index.html.

And voila!

😊 well not quite… Still, the ‘loadScript‘ method should be invoked, somewhere from the Angular application and cause the browser to start the download of the JavaScript library. And here we come to the second question:

How to “Hook” to the initializing of an Angular application

But this post is already a bit long. I guess it’s time to finish this first part. For “hooking” to the initialization of the Angular application, check  part two.

Author: Aleksandar Gyonov

Stay tuned for Part 2!