-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.xml
386 lines (373 loc) · 41.5 KB
/
index.xml
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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>Yeshas M P</title>
<link>https://yeshasmp.github.io/</link>
<description>Recent content on Yeshas M P</description>
<generator>Hugo -- gohugo.io</generator>
<language>en</language>
<lastBuildDate>Fri, 15 Apr 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://yeshasmp.github.io/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Demystifying Angular Services and Dependency Injection</title>
<link>https://yeshasmp.github.io/posts/angular-services/</link>
<pubDate>Fri, 15 Apr 2022 00:00:00 +0000</pubDate>
<guid>https://yeshasmp.github.io/posts/angular-services/</guid>
<description>Guide to Angular Services and Dependency Injection</description>
<content:encoded><![CDATA[<p>In any Angular application it is essential to keep component as lean as possible, it’s only concern should be on how to present the model data to view as described by <a href="https://angular.io/guide/architecture-services">Angular official docs</a>.</p>
<blockquote>
<p>“a component’s job is to enable the user experience and nothing more.”</p>
</blockquote>
<p>where other logics such as fetching data from API endpoint or handling client and server side errors should be taken care by services.</p>
<h2 id="angular-services">Angular Services</h2>
<p>Angular services are simple class which is used to perform specific functions. Angular Services offer several advantages -</p>
<ol>
<li>It’s easier to write logic once in service and share the service among the components instead of writing the same logic in every component.</li>
<li>It’s easier to test and debug.</li>
<li>It’s easier maintain and perform code updates when required.</li>
</ol>
<h3 id="angular-service-example">Angular Service Example</h3>
<p>We can generate Angular Service in Angular CLI using <code>ng g s AppService</code> where ‘g’ and ’s’ is shorthand form for ‘generate service’.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Injectable</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">'@angular/core'</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">@Injectable</span>({
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">providedIn</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'root'</span>
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AppService</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">constructor</span>() { }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">alertMsg</span>(<span style="color:#a6e22e">msg</span> : <span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">alert</span>(<span style="color:#a6e22e">msg</span>);
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Above service has method to launch alert popup with custom message. <code>AppComponent</code> can request <code>AppService</code> in it’s constructor and call the alertMsg method as shown below.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Component</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">'@angular/core'</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">AppService</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">'../app.service'</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">@Component</span>({
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">selector</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'app-root'</span>,
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">templateUrl</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'./app.component.html'</span>,
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">styleUrls</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">'./app.component.css'</span>]
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AppComponent</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">constructor</span>(<span style="color:#66d9ef">private</span> <span style="color:#a6e22e">appService</span>:<span style="color:#66d9ef">AppService</span>){
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">appService</span>.<span style="color:#a6e22e">alertMsg</span>(<span style="color:#e6db74">"App Component launched"</span>);
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>By default, Angular services are singleton. When services are registered either in provider array of root module or with providedIn value of ‘root’ or ‘any’ in the service itself there’s only single instance of service available throughout the application.</p>
<p>We understood how Angular service could be called in any component, but did you wonder how did <code>AppComponent</code> got an instance of <code>AppService</code>? For any class to execute another class method it needs to create an object of that class and call the method through it unless it’s static. But where did <code>AppComponent</code> create any instance of <code>AppService</code>?</p>
<p>Let’s move into next section to know how <code>AppComponent</code> got an instance of <code>AppService</code>.</p>
<h2 id="dependency-injection">Dependency Injection</h2>
<p>When <code>AppComponent</code> get’s loaded, Angular would create and provide an instance of <code>AppService</code> to the <code>AppComponent</code> giving access to <code>alertMsg</code> method. This process is known as <code>Dependency Injection</code>. As stated in <a href="https://en.wikipedia.org/wiki/Dependency_injection">Wikipedia</a></p>
<blockquote>
<p>“In software engineering, dependency injection is a technique in which an object receives other objects that it depends on, called dependencies.”</p>
</blockquote>
<p>In Angular terms it’s “Responsibility of Angular framework to create an instance of service and provide it to the requested component”. The requested component need not know how and where to create service instance, it can simply request in it’s constructor and Angular would provide it.</p>
<p>Services needs to register itself as dependency before any component can request it. There are 3 ways where service can register itself as dependency -</p>
<ol>
<li>Using providedIn property inside the <code>@Injectable</code> decorator of the service class itself. This is preferred way of registering a service as stated by Angular Docs since it tree shakable meaning Angular would include this service during build time if and only any component requests it. Otherwise this is excluded from the build which helps in improving the performance of our app.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">@Injectable</span>({
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">providedIn</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'root'</span>
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><ol start="2">
<li>By registering in provider array at Module level, Component level or Directive level. Service provided like below is not tree shakable and would be included in the build even if no component requests it.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#a6e22e">providers</span><span style="color:#f92672">:</span> [ <span style="color:#a6e22e">AppService</span> ]
</span></span></code></pre></div><ol start="3">
<li>By manually registering using <code>@Inject</code> inside constructor of consumer.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">constructor</span>(<span style="color:#66d9ef">@Inject</span>(<span style="color:#a6e22e">AppService</span>) <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">appService</span>)
</span></span></code></pre></div><p>A <code>Provider</code> is an object which holds list of all services registered in provider array. Angular creates provider and injector instance for root module and for each lazy loaded module. It also creates injector instance for all components and directives. Each <code>Injector</code> holds the provider list of all dependencies registered at respective component, directive or modules.</p>
<p>Note - Angular doesn’t create injector instance for <code>Eagerly Loaded modules</code> therefore, the services registered at those modules will be configured in <code>Root Module injector</code>.</p>
<p>Typical service when registered in provider array at module or component would look like below -</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#a6e22e">provders</span> <span style="color:#f92672">:</span> [<span style="color:#a6e22e">AppService</span>]
</span></span></code></pre></div><p>which is shorthand property when expanded would look like</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#a6e22e">providers</span> <span style="color:#f92672">:</span> [{ <span style="color:#a6e22e">provide</span> : <span style="color:#66d9ef">AppService</span> , <span style="color:#a6e22e">useClass</span> : <span style="color:#66d9ef">AppService</span> }]
</span></span></code></pre></div><p><code>provide</code> property holds the injection token while <code>provider</code> property holds the instruction on how to create the dependency. Injection token can either be a type, a string or an injection token itself. We can not only provide class as dependency but also provide direct value or value returned from function or function itself using <code>useValue</code>, <code>useFactory</code> and <code>useExisting</code> provider properties. Visit <a href="https://angular.io/guide/dependency-injection-providers">Angular Docs</a> to know more how you use other provider types.</p>
<p>Now let’s breakdown how Angular would resolve the dependency using provider and injector in below steps for better understanding -</p>
<p><img loading="lazy" src="/images/hierarchical-tree-flowchart.webp#center" alt="Hierarchical Injection" />
</p>
<ol>
<li>
<p>At runtime, Angular resolves dependency by following hierarchical injection tree. An injection tree is nothing but tree of injector instances.</p>
</li>
<li>
<p>By default, Angular creates <code>Module Injector tree</code> having one root module injector and seperate module injector for each lazy loaded module. At the top of root module injector sits Null and Platform module injectors. It also creates an <code>Element Injector tree</code> which holds injectors of all components and directives.</p>
</li>
<li>
<p>When <code>AppComponent</code> requests <code>AppService</code> Angular DI system at first will look at the provider array of <code>AppComponent</code> using the injection token given in the constructor.</p>
</li>
<li>
<p>If no provider is found in the <code>AppComponent</code> injector, then it traverse upto the parent components in search of matching provider using token until it reaches the root component injector in <code>Element Injector tree</code>.</p>
</li>
<li>
<p>If no providers are found in <code>Element Injector tree</code> then it searches in <code>Module Injector tree</code>. If the requested component is under lazy loaded module it searches in the provider of <code>Lazy Loaded Module injector</code> before proceeding to the <code>Root Module injector</code>.</p>
</li>
<li>
<p>When provider is found, it creates an instance of service and provide it to the requested component. If no provider is found in both <code>Element Injector</code> and <code>Module Injector</code> trees it reaches Null injector and throws <code>NullInjectorError</code> as shown below.</p>
</li>
</ol>
<p><img loading="lazy" src="/images/null-error.jpg#center" alt="Angular service null injector error" />
</p>
<p>We can control the dependency resolution using @Skip, @SkipSelf, @Optional and @Host resolution modifiers. We can avoid the above null injector error when dependency is tagged with <code>@Optional</code> modifier in the requested <code>AppComponent</code> constructor like below. Then Angular would return null instead of throwing error.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">constructor</span>(<span style="color:#66d9ef">@Optional</span> <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">appService</span> : <span style="color:#66d9ef">AppService</span>)
</span></span></code></pre></div><h2 id="are-angular-services-singleton">Are Angular Services singleton?</h2>
<p>Let’s consider below code scenario to understand Hierarchical injection of services and whether Angular services are singleton or not. Head to <a href="https://stackblitz.com/edit/angular-ivy-r6cden">Stackblitz</a> to experiment and play below code example.</p>
<p>We will create an <code>AppService</code> which generates random number when it’s instance is created and return that value through method. Initially we will register <code>AppService</code> only in root module using <code>providedIn</code> value as ‘root’ -</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Injectable</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">'@angular/core'</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">@Injectable</span>({
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">providedIn</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'root'</span>
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AppService</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">sharedValue</span> : <span style="color:#66d9ef">number</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">constructor</span>() {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">sharedValue</span> <span style="color:#f92672">=</span> Math.<span style="color:#a6e22e">floor</span>(Math.<span style="color:#a6e22e">random</span>() <span style="color:#f92672">*</span> <span style="color:#ae81ff">5</span>);
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">"AppService instance has been created!"</span>)
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">getSharedValue</span>(){
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">sharedValue</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Let’s create two more components - <code>AppComponent</code> and <code>HomeComponent</code> a child of <code>AppComponent</code> and request the <code>AppService</code> in both the component constrcutor.</p>
<p><code>AppComponent</code> -</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Component</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">'@angular/core'</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">AppService</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">'./app.service'</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">@Component</span>({
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">selector</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'app-root'</span>,
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">templateUrl</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'./app.component.html'</span>,
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">styleUrls</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">'./app.component.css'</span>]
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AppComponent</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">appServiceValue</span> : <span style="color:#66d9ef">any</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">constructor</span>(<span style="color:#66d9ef">private</span> <span style="color:#a6e22e">appService</span> : <span style="color:#66d9ef">AppService</span>) {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">appServiceValue</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">appService</span>.<span style="color:#a6e22e">getRandomNumber</span>();
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>HomeComponent</code> -</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Component</span>, <span style="color:#a6e22e">OnInit</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">'@angular/core'</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">AppService</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">'../app.service'</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">@Component</span>({
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">selector</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'app-home'</span>,
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">templateUrl</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'./home.component.html'</span>,
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">styleUrls</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">'./home.component.css'</span>],
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//providers: [AppService]
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>})
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">HomeComponent</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">appServiceValue</span> : <span style="color:#66d9ef">any</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">constructor</span>(<span style="color:#66d9ef">private</span> <span style="color:#a6e22e">appService</span> : <span style="color:#66d9ef">AppService</span>) {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">appServiceValue</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">appService</span>.<span style="color:#a6e22e">getRandomNumber</span>();
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>We will then display the random number in both app and home component by calling getRandomNumber method and passing the value to view. When we load the application we can see both components get the same random number as they both received same single instance of <code>AppService</code> from <code>Root Module injector</code>. This proves Angular services are singleton when they are registered only in <code>Root Module provider</code>.</p>
<p><img loading="lazy" src="/images/singleton.webp#center" alt="Angular singleton service" />
</p>
<p>Now let’s register <code>AppService</code> also in <code>HomeComponent</code> provider and run the app. App and Home component displays two different random numbers because they both received two different instances of <code>AppService</code>. Thus we can say Angular services are not singleton when they are provided at different levels.</p>
<p><img loading="lazy" src="/images/non-singleton.webp#center" alt="Angular non-singleton service" />
</p>
<p>But how did two instance of Angular services got created?</p>
<p><img loading="lazy" src="/images/hierarchical-injection-tree.webp#center" alt="Angular Hierarchical Injection" />
</p>
<ol>
<li>
<p>When <code>AppComponent</code> requested the <code>AppService</code>, Angular looked for it in <code>AppComponent</code> provider at first, when it couldn’t find it it went into <code>Module injector tree</code> and found the <code>AppService</code> in <code>Root Module provider</code> and returned it to <code>AppComponent</code>.</p>
</li>
<li>
<p>Next when <code>HomeComponent</code> requested <code>AppService</code> it found it in the <code>HomeComponent</code> provider itself and returned new <code>AppService</code> instance to <code>HomeComponent</code>.</p>
</li>
<li>
<p>Therefore we saw two instance of <code>AppService</code> being created and provided to respective components.</p>
</li>
</ol>
<p>Few points to remember before we conclude -</p>
<ol>
<li>
<p><code>Element Injector tree</code> always gets preference over <code>Module Injector tree</code> and it is not child of <code>Module Injector tree</code>.</p>
</li>
<li>
<p>Angular DI resolves dependencies using bottom to top approach, it starts the search for provider first from the requesting component and then traverse upto the parent components to the <code>Root Module provider</code>.</p>
</li>
<li>
<p>Service which are provided at <code>Root Module</code> or <code>Eagerly Loaded Module</code> are app scoped and accesible to all the components or directives. Services which are provided in <code>Lazy Loaded Module</code> are module scoped and available only to the components or directives under that module.</p>
</li>
<li>
<p><code>Proivder</code> holds the list of dependencies with it’s matching token, while <code>Injector</code> holds provider itself.</p>
</li>
<li>
<p>If two <code>Eagerly Loaded modules</code> providers has service for same injector token then the module which is imported at last in <code>Root Module</code> gets preference.</p>
</li>
</ol>
<p>Above code example have been shared over <a href="https://github.com/yeshasmp/AngularService">Github</a> and <a href="https://stackblitz.com/edit/angular-ivy-r6cden">Stackblitz</a>.</p>
<p>That’s it folks! I hope this article helped you understand better how Angular Dependency works and how Angular Services are singleton by nature.</p>
<p>Stay tuned for more such interesting articles!</p>
]]></content:encoded>
</item>
<item>
<title>Angular Bootstrapping : How does Angular work under the hood?</title>
<link>https://yeshasmp.github.io/posts/angular-bootstrapping/</link>
<pubDate>Sun, 16 Jan 2022 00:00:00 +0000</pubDate>
<guid>https://yeshasmp.github.io/posts/angular-bootstrapping/</guid>
<description>Guide to Angular Bootstrapping</description>
<content:encoded><![CDATA[<p>In this article we are going to explore together how Angular apps work behind the scenes. At first, we will understand various workspace and application configuration files and finally we will piece together all the files how they are involved in bootstraping.</p>
<h1 id="angular-project-structure">Angular Project Structure</h1>
<p>Before understanding how Angular works under the hood, it’s necessary to understand the workspace structure and application configuration involved in the process. Every Angular apps works in the context of workspace. By default <code>ng new app_name</code> command generates a skeleton application at root of workspace in below fashion -</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#a6e22e">workspace</span><span style="color:#f92672">/</span>(<span style="color:#a6e22e">folder</span> <span style="color:#a6e22e">name</span> <span style="color:#a6e22e">is</span> <span style="color:#a6e22e">app_name</span> <span style="color:#a6e22e">given</span> <span style="color:#a6e22e">at</span> <span style="color:#a6e22e">cmd</span>)
</span></span><span style="display:flex;"><span> ... (<span style="color:#a6e22e">workspace</span><span style="color:#f92672">-</span><span style="color:#a6e22e">wide</span> <span style="color:#a6e22e">config</span> <span style="color:#a6e22e">files</span>)
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">src</span><span style="color:#f92672">/</span> <span style="color:#f92672">--</span>(<span style="color:#a6e22e">source</span> <span style="color:#a6e22e">and</span> <span style="color:#a6e22e">support</span> <span style="color:#a6e22e">files</span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">application</span>)
</span></span></code></pre></div><p>The above structure is usually referred as ‘multi-repo’ development style where each application has it’s own workspace, ‘workspace’ folder consists of workspace specific configuration files where as ‘src’ folder consists of application specific files and folders.</p>
<h2 id="workspace-configuration-files">Workspace configuration files</h2>
<ol>
<li>angular.json - It specifies workspace-wide and project specific defaults for build and development. For instance, You can have ’n’ number of npm packages installed in your workspace, but you can specify which packages you want in your application in angular.json</li>
<li>package.json - It consists metadata of project and is used for managing the project’s dependencies, scripts, npm package versions and many more.</li>
<li>package-lock.json - It provides information of all npm packages installed in npm_modules folder such as package version, hash to verify the package integrity and list of its dependencies.</li>
<li>tsconfig.json - Since Angular apps are written using typescript, this file specifies root files and compiler options required to compile our application.</li>
</ol>
<h2 id="application-configuration-files">Application configuration files</h2>
<ol>
<li>index.html - This is our main HTML page which is rendered and is displayed to user in browser.</li>
<li>main.ts - This is responsible for compiling all components and template files with JIT(Just In Time) compiler. We can also use AOT(Ahead Of Time) compiler by adding –aot flag to <code>ng build</code> or <code>ng serve</code> CLI commands(Recommended for production environment).</li>
</ol>
<p>For more detailed explanation of all config files go through official <a href="https://angular.io/guide/file-structure">Angular Docs</a></p>
<h1 id="angular-bootstrapping">Angular Bootstrapping</h1>
<p>Some of you all might have wondered when I used the term ‘Bootstraping’ earlier. In Angular eco-system <code>Bootstraping</code> is technique of initializing root module and loading root component into index.html file. When we run <code>ng serve</code> or <code>ng build</code> multiple things happen in the background. Below steps provide a brief overview of bootstrapping process-</p>
<p><img loading="lazy" src="/images/angular-bootstrapping.webp#center" alt="Angular Bootstrapping" />
</p>
<ol>
<li>Compilation of application codes using <code>tsc</code> typescript compiler.</li>
<li>Bundling and minification of javascript files by Webpack.</li>
<li>Deployment and bootstrapping.</li>
<li>Run JIT compiler for all components, directives and pipes.</li>
<li>Render index.html in browser.</li>
</ol>
<p>The only difference between JIT and AOT compiler is that in AOT compiler, compilation takes place during build time rather than at runtime in browser. Let’s go through above steps in bit more detail -</p>
<ol>
<li>angular.json consist of multiple configuration properties related build and development. The one’s which are interesting to us are ‘builder’, ‘index’ and ‘main’ properties under ‘build’ as shown below.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#e6db74">"build"</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">"builder"</span><span style="color:#f92672">:</span> <span style="color:#e6db74">"@angular-devkit/build-angular:browser"</span>,
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">"options"</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span> .......
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">"index"</span><span style="color:#f92672">:</span> <span style="color:#e6db74">"src/index.html"</span>,
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">"main"</span><span style="color:#f92672">:</span> <span style="color:#e6db74">"src/main.ts"</span>,
</span></span><span style="display:flex;"><span> .......
</span></span><span style="display:flex;"><span> }
</span></span></code></pre></div><p>When we run <code>ng serve</code> or <code>ng build</code> Angular CLI looks at angular.json for these properties. ‘builder’ property is saying to Angular CLI where it can find Architect builders to build Angular apps for browser environment. where as our ‘index’ and ‘main’ properties specifies index.html file to be rendered in browser and main.ts which handles bootstrapping process. Then Angular CLI calls <code>tsc</code> typescript compiler to transpile all the typescript codes into javascript codes using configuration provided in <code>tsconfig.json</code>.</p>
<ol start="2">
<li>If we look at index.html file of newly generated Angular app, it only has <code><app-root></code> element inside <code><body></code> tag. <code><app-root></code> is root component template which is injected at runtime. Apart from these there is neither any javascript file or any reference to style links.</li>
</ol>
<pre tabindex="0"><code><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>SampleApp</title>
<base href="https://yeshasmp.github.io/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.a7a7ed6d783a0950.css" media="print" onload="this.media='all'">
<noscript>
<link rel="stylesheet" href="styles.a7a7ed6d783a0950.css">
</noscript>
</head>
<body>
<app-root></app-root>
<script src="runtime.0298365924d274ea.js" type="module"></script>
<script src="polyfills.daf14d28d6c3636f.js" type="module"></script>
<script src="main.94e04dd2c597738f.js" type="module"></script>
</body>
</html>
</code></pre><p>After transpiling all the typescript codes, Angular CLI then uses Webpack, a module bundler to minify and bundle all the files which are then added as reference into index.html file. Let’s look at each of these files :</p>
<ol>
<li>runtime.js - It contains utility codes used by Webpack to load other files.</li>
<li>vendor.js - It contains 3rd party library codes which are imported in our ngModules.</li>
<li>polyfill.js - It contains polyfills required to make our app compatible among different browsers.</li>
<li>main.js - It contains all the javascript codes written in our application.</li>
</ol>
<p>When we run <code>ng serve</code> application codes are built at local environment and served from memory, where as <code>ng build</code> outputs all the transpiled codes into dist folder from where it can be deployed to any hosting vendor and served from there.</p>
<ol start="3">
<li>At runtime in browser, main.js is responsible for bootstraping our Angular apps. It contains all our application codes and how to compile and run it. We earlier learnt Angular CLI looks at angular.json to find main.ts file. If we look at main.ts file it consist couple of import statements along with some lines of code.</li>
</ol>
<pre tabindex="0"><code>import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
</code></pre><p>Apart from import statements, we are having code to enable production mode if target environment is production but the line which is interesting to us is <code>platformBrowserDynamic().bootstrapModule(AppModule);</code>. Though it’s single line, lot of action takes place when executed by Angular CLI.</p>
<ol>
<li>At first, We are importing <code>platformBrowserDynamic</code> module from <code>@angular/platform-browser-dynamic</code> library.</li>
<li>Then we are calling <code>platformBrowserDynamic().bootstrapModule(AppModule)</code> by passing our root AppModule as parameter.</li>
<li>Internally <code>bootstrapModule</code> creates an instance of compiler <code>JIT compiler</code>, where it crawls through all components, directives and pipes declared in AppModule @NgModule decorator and other feature modules imported in AppModule.</li>
<li>Once it finds Component having ‘app-root’ as its selector, it will render the component template in index.html.</li>
</ol>
<p>But how does Angular know where to find template for <code><app-root></code> in index.html? Let’s find out in next section.</p>
<ol start="4">
<li>From the root AppModule we passed earlier, it refers @ngModule decorator in AppModule. @NgModule decorator contains metadata of all the components, directives and pipes under it along with other feature modules imported under AppModule. It also specifies services which are available at app-level under providers and bootstrap property specifying the root component as shown below.</li>
</ol>
<pre tabindex="0"><code>import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
</code></pre><ol start="5">
<li>
<p>Once it finds the root AppComponent, it then refers @Component decorator in AppComponent. @Component contains metadata of below properties :</p>
<ol>
<li>selector : It is used to identify all the components in Angular app.</li>
<li>templateUrl : It provides path to component HTML template file.</li>
<li>styleUrls : It provides path to component styles.</li>
</ol>
</li>
</ol>
<pre tabindex="0"><code>import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'SampleApp';
}
</code></pre><p>There it is! <code>app-root</code> specified in selector matches with <code>app-root</code> in index.html. It then finds HTML template along with styles and renders the same in index.html where <code>app-root</code> is present.</p>
<h1 id="conclusion">Conclusion</h1>
<p>Though above explanation is not complete, I hope it gave you brief idea on what’s happening under the hood when we run Angular app. Stay tuned for more such interesting articles!</p>
]]></content:encoded>
</item>
<item>
<title>About</title>
<link>https://yeshasmp.github.io/about/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://yeshasmp.github.io/about/</guid>
<description>About</description>
<content:encoded><![CDATA[<p>Welcome to my personal technology blog, <a href="https://yeshasmp.github.io">https://yeshasmp.github.io</a>! I’m Yeshas M P, an Application Developer at IBM India Pvt. Ltd. My primary focus is on Angular and various Microsoft technologies such as ASP.Net Core, SQL Server, and Azure Cloud. Through this blog, I share practical insights, tips, and tutorials on these technologies. Stay tuned for updates and dive into the world of software development with me!</p>
<hr>
<h1 id="disclaimer">Disclaimer</h1>
<hr>
<p>This is my personal blog. The opinions expressed here represent my own and not those of my current employer, previous employers and/or any other companies. For accuracy and official reference refer to respective official sources.</p>
]]></content:encoded>
</item>
</channel>
</rss>