-
Notifications
You must be signed in to change notification settings - Fork 0
/
post_1.html
238 lines (231 loc) · 10.4 KB
/
post_1.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
<div>
</div>
<div style="text-align: justify;">
For a while, in my professional work, I have been developing software applications using <em>Angular</em>. In a few
cases I have faced questions about how one or other thing could have happened and I have not found a quick and
unambiguous answer for myself. In this post I will discuss two interesting things of this nature:
</div>
<ol>
<li>How dynamically, from <em>Angular</em>, to download in the app third-party JavaScript libraries</li>
<li>How to hook to the initialization process of an <em>Angular</em> application and run our own script</li>
</ol>
<div style="text-align: justify;">
For illustration I have written a sample application that I will quote in several places in the post.
</div>
<ul>
<li>
The code for the example application is available at: <a href="https://github.com/agyonov/NgInitGA">https://github.com/agyonov/NgInitGA</a>
</li>
<li>
The app itself is live on: <a href="https://agyonov.github.io/NgInitGA">https://agyonov.github.io/NgInitGA</a>
</li>
</ul>
<h2>
Dynamic download of a third-party JavaScript library</h2>
<div style="text-align: justify;">
For most libraries that you would like to use, someone has already created, developed and maintains an NPM package
with the relevant <em>Angular</em> modules and you could (and should) use them. But sometimes there is no ready NPM
package or you do not like one that is available. Then you can directly reference the requested JavaScript library
from your <em>Angular</em> application and use it.
</div>
<br />
<div style="text-align: justify;">
There are two easy approaches that are described in the <em>Angular</em> documentation and, for example, in <em>Google
Analytics</em>. And which don't always do the work for one reason or another.
</div>
<br />
<div style="text-align: justify;">
<strong>The first</strong>, entirely <em>Angular</em>, 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 <em>angular.json</em>
config file. E.g:
</div>
<pre class="prettyprint"><code class="language-ts">
"scripts": [
"src/scripts/analytics.js"
]
</code></pre>
<div style="text-align: justify;">
As described in detail in the documentation of <em>Angular</em>, at <a href="https://angular.io/guide/workspace-config#additional-build-and-test-option">https://angular.io/guide/workspace-config#additional-build-and-test-options</a>.
</div>
<br />
<div style="text-align: justify;">
It's OK as an approach and will work.
</div>
<br />
<div style="text-align: justify;">
<strong>A consideration</strong> against its possible use is that under this approach the .js script will be
downloaded from your hosting server and not from the original server (in this case <a href="https://www.google-analytics.com/analytics.js">https://www.google-analytics.com/analytics.js</a>).
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 do not re-publish your app.
</div>
<br />
<div style="text-align: justify;">
<strong>The second</strong> approach is to apply the instruction on the <em>Google Analytics</em> page <a href="https://developers.google.com/analytics/devguides/collection/analyticsjs/#alternative_async_tracking_snippe">https://developers.google.com/analytics/devguides/collection/analyticsjs/#alternative_async_tracking_snippet</a>:
</div>
<pre class="prettyprint"><code class="language-html">
<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>
</code></pre>
<br />
<div style="text-align: justify;">
And the above code to be pasted directly into the <em>index.html</em> file initializing your <em>Angular</em>
application. In particular,
<strong>the last line</strong> of the above sample code.
</div>
<br />
<div style="text-align: justify;">
It's OK as an approach and will also work in its <strong>part to download the .js library</strong>. In addition, here
the objections which were valid for the first decision are irrelevant.
</div>
<br />
<div style="text-align: justify;">
<strong>A consideration</strong> against its possible use is stylistic – not a "clean" <em>Angular</em> solution.
</div>
<br />
<div style="text-align: justify;">
The rest of the code snip from <em>Google Analytics</em>, shown above, <strong>does not work</strong> for us at all!
Because with <em>Angular</em> 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 <em>Angular</em> app and will not affect the data in <em>Google Analytics</em>.
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-XNiBfJq8ui8/XFFrB107SxI/AAAAAAAABw0/kfensK1yaIkmAl963qfnUS1jS9Dfd239gCLcBGAs/s1600/0.png"
imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="243"
data-original-width="519" src="https://3.bp.blogspot.com/-XNiBfJq8ui8/XFFrB107SxI/AAAAAAAABw0/kfensK1yaIkmAl963qfnUS1jS9Dfd239gCLcBGAs/s1600/0.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div style="text-align: justify;">
These issues are discussed in detail in the <em>Google Analytics</em> documentation itself under the <a href="https://developers.google.com/analytics/devguides/collection/analyticsjs/single-page-applications">Single
Page Application Tracking</a> theme.
</div>
<br />
<div style="text-align: justify;">
Let's get back to the issue with the dynamic download of the JavaScript library. <strong>There is a third way</strong>
that I will look at. Because the example is using <em>Google Analytics</em>, I'll expand it to be used to track
users’ actions in the <em>Angular</em> app.
</div>
<br />
<div style="text-align: justify;">
<strong>NB:</strong> As for tracking of users in <em>Angular</em> application, it has great, ready-made libraries,
such as <a href="https://github.com/angulartics/angulartics2">angulartics2</a>, 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!
</div>
<br />
<div style="text-align: justify;">
So, the dynamic download. Let us see a sample of the following <em>Angular</em> service:
</div>
<br />
<pre class="prettyprint"><code class="language-ts">
// Typescript extention of Window interface
declare global {
interface Window { ga: any; }
}
@Injectable({
providedIn: 'root'
})
export class GoogleAnaliticsService {
// The source for the load
private googleAnaliticsScript = {
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.googleAnaliticsScript.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.googleAnaliticsScript.url;
scriptElm.type = 'text/javascript';
scriptElm.async = true;
scriptElm.onload = () => {
// Prevent from load second time
this.googleAnaliticsScript.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);
}
}
}
}
</code></pre>
<div style="text-align: justify;">
Basically, it's a transcription in <em>TypeScript</em>, of the instructions from the <em>Google Analytics</em> page,
which we have above. The important points are:
</div>
<ul>
<li>
Just in the beginning we expand the <em>TypeScript</em> <strong>Window</strong> interface with a new field-'ga'
</li>
<li>
In the <em>'loadScript'</em> method, the actual work is done:
<ul>
<li>
A <em>scriptElm</em> script element is created and is added to the DOM of the Web page for download. It's the
same as putting:
<pre class="prettyprint"><code class="language-html">
<script async
src='https://www.google-analytics.com/analytics.js'>
</script>
</code></pre>
in <em>index.html</em>.
</li>
<li>
Defines the 'ga' field with which the <strong>Window</strong> interface is expanded, as specified by <em>Google
Analytics</em>. It's the same as putting:
<pre class="prettyprint"><code class="language-html">
<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>
</code></pre>
in <em>index.html</em>.
</li>
</ul>
</li>
</ul>
<div style="text-align: justify;">
And voila!
</div>
<br />
<div style="text-align: justify;">
😊 well not quite... Still, the <em>'loadScript'</em> method should be invoked, somewhere from the <em>Angular</em>
application and cause the browser to start the download of the JavaScript library. And here we come to the second
question:
</div>
<h2>
Hook to the initialization of an Angular application</h2>
<div style="text-align: justify;">
But this post became a bit long. I guess it is time to finish this first part. For "hooking" to the initialization of
the Angular application, I will write in the second part.
</div>
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?skin=desert"></script>