In the first part of this post, the discussion was focused mainly around the different methods of dynamic download of a third-party JavaScript library in an Angular application. Here in this second post, we will be discussing the ways in which we can “hook” to the initialization of the Angular application and fulfill our programming logic. For illustration I will continue to use the example application from the first part, which I will quote again in several places in the current 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
For those who have not read the first part or have forgotten it, the example is using Google Analytics and to make the example more meaningful it is extended to allow for full tracking of users’ actions in the Angular application.
NB: 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 with an example to show a possible solution to the two questions set at the beginning of this post!
“Hook” to initialization of an Angular application
I have found several methods to run your initialization program logic (your script) when launching the Angular application in the browser. I will list two of them in short, and the third one I will look at a little more closely.
1. Use the “forRoot” static method:
If you have a service in a separate Angular “feature” module, then perhaps the best and most established way is to use a static “forRoot” method. The method is called when you import your “feature” module into the main module of the Angular application.
You can read in more detail here and here.
2. Calling your script in the ngOnInit method of the main AppComponent:
The most direct and not complicated method is to run the script in the ngOnInit method of the main AppComponent in your Angular application. If it works, apply it. If we use the example from the first part of the publication, we have completed one method-“Loadscript”. This method should be called from your initialization code to start the download of third-party .js Google Analytics Library. In the app.component.ts code, this would appear as follows:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
title = 'NgInit';
constructor(private gas: GoogleAnalyticsService) {
}
ngOnInit(): void {
// Start down load of JS Library
this.gas.loadScript();
}
}
Particularity, in the sample application and the use of Google analytics for illustration, this technique will work without any problems.
A recital against its possible use in other cases is that there would be situations where it would not have done the job ☹. These are the cases where all the initializing logic must have completed its execution before the start of the ngOnInit method execution or any other code from your application.
An example, for such a situation, is to say when you have a mandatory user authentication and it must be completed, successful or not, before any other code from your Angular application is executed.
The user authentication scenario, in an Angular application, is an interesting topic for which I can write a separate article in the future.
3. Using pre-defined in Angular tokens (the most interesting)
There is one way to “hook” to the Angular application initialization process, about which, I believe, the information from the Angular team is shared in an overly “modest” manner. These are the so-called predefined tokens and “Factory providers“. Information about them can be found here and here.
In the example application, this approach is implemented into the app.module.ts file, the main module of the Angular application.
At the beginning we define “factory” function GoogleAnalyticsServiceFactory and export it! The latter is important from the perspective of TypeScript and AOT compiling the Angular application.
The function itself must return another, internal function. That’s why it’s called factory 😊.
The internal function that is returned by GoogleAnalyticsServiceFactory is your code that will be executed when initializing the Angular application. The inner function can return in result void or Promise.
Note: If it returns Promise, the Angular app will be blocked until Promise is resolved!
The internal function gets as input parameter (via dependency-injection) service GoogleAnalyticsService and executes the “loadScript” method. That’s what we were aiming for!
// Factory provider for Angular. Provides function to be executed on Angular application
//startup
export const GoogleAnalyticsServiceFactory = (gas: GoogleAnalyticsService) => {
return () => {
// download
gas.loadScript();
};
};
@NgModule({
declarations: [
AppComponent,
SecondPageComponent,
HomeComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [
// Provides function to be executed on Angular application startup
{
provide: APP_INITIALIZER,
useFactory: GoogleAnalyticsServiceFactory,
deps: [GoogleAnalyticsService],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
In the providers array, when defining the AppModule main module of the Angular application, we figuratively “hook” the function defined above to the Angular startup process.
This is done by using the pre-defined token APP_INITIALIZER. It identifies all provider marked with it to run before the Angular application is initialized!
Note that as dependency (the array deps: [GoogleAnalyticsService]) are listed the parameters that Angular bootstrap process must instantiates and submit as parameters to the function that will run when the application starts!
Аnd voila! This time for real 😊.
Example application
For assurance that the above code is working, you can look at the live sample application. Try it!
The following code samples have also been added to the GoogleAnalyticsService service for accessing Google Analytics:
// Send page view event to Google Analytics
public send(pageUrl: string): void {
// Send
window.ga('set', 'page', pageUrl);
window.ga('send', 'pageview');
}
// Send event tracking event to Google Analytics
public event(eventCategory: string, eventAction: string): void {
// Send
window.ga('send', 'event', eventCategory, eventAction);
}
These methods follow the instructions of Google Analytics to work with SPA applications.
The methods are used by the Home, Second Page pages and the button Just an Event to send information to Google Analytics about the user’s actions.
It is visible from the above screen that:
- The script analytics.js is downloaded from the browser
- When transitioning from page to page and pressing the button, a post of data to the collect address of Google Analytics, occurs.
Conclusion
In these two posts, I have tried to examine and describe in words and examples three different approaches to achieving the objectives of both topics:
- Download the third-party JavaScript library in Angular application
- “Hook” to the initializing of an Angular application
I hope you have not been bored and that it will be helpful for you if you need to program things like above mentioned, to choose an approach that best serves you.
Author: Aleksandar Gyonov