You are on page 1of 115

The Angular

Tutorial
Learn Front-End Development and
Automated Testing with Angular

by Adam Morgan
2
The Angular Tutorial
Learn Front-End Development and Automated Testing with
Angular

Adam Morgan
ii
Contents

Preface v

1 Book Methodology 1
1.1 The approach . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Who is this for? . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.1 The wannabe front-end developer with a grasp on the
basics . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.2 The Angular developer who wants to learn automated
testing . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.3 The front-end developer who wants to learn Angular . 3
1.3 What this book is vs. what this book isn’t . . . . . . . . . . . 3
1.4 Why am I writing this book? . . . . . . . . . . . . . . . . . . 4
1.5 What are we building? . . . . . . . . . . . . . . . . . . . . . 4
1.6 Conventions used in this book . . . . . . . . . . . . . . . . . 9

2 How Web Applications Work 11


2.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2 MongoDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

iii
iv CONTENTS

2.3 Node.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4 Angular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.5 The Client-Server model . . . . . . . . . . . . . . . . . . . . 14

3 Getting Started and Installation 17


3.1 Tool requirements . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2 Technical requirements . . . . . . . . . . . . . . . . . . . . . 17
3.3 Text editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.4 Terminal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.5 Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.5.1 Windows users . . . . . . . . . . . . . . . . . . . . . 19
3.6 Postman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.7 Robo 3T . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.8 Node.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.9 nvm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.9.1 Mac . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.9.2 Windows . . . . . . . . . . . . . . . . . . . . . . . . 23

4 API Setup and Installation 25


4.1 Installing MongoDB . . . . . . . . . . . . . . . . . . . . . . 25
4.1.1 Mac installation . . . . . . . . . . . . . . . . . . . . . 25
4.1.2 Windows installation . . . . . . . . . . . . . . . . . . 26
4.1.3 Creating the MongoDB data directory . . . . . . . . . 26
4.2 Create GitHub account . . . . . . . . . . . . . . . . . . . . . 28
4.3 Creating a Zomato account . . . . . . . . . . . . . . . . . . . 29
CONTENTS v

4.4 Cloning the API repository . . . . . . . . . . . . . . . . . . . 32


4.5 Creating config files . . . . . . . . . . . . . . . . . . . . . . . 33
4.6 Test config vs. dev config . . . . . . . . . . . . . . . . . . . . 35
4.7 Running the API . . . . . . . . . . . . . . . . . . . . . . . . 36
4.8 Verifying the database . . . . . . . . . . . . . . . . . . . . . . 41
4.9 Add to GitHub . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.10 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

5 Introduction to Angular 49
5.1 The approach . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5.2 Install the CLI . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5.3 Exploring the code . . . . . . . . . . . . . . . . . . . . . . . 54
5.4 Root folder . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.5 Src folder . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.6 AppComponent . . . . . . . . . . . . . . . . . . . . . . . . . 56
5.7 AppModule . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.8 NgModule . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
5.9 Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
5.10 Directives . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
5.11 Pipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.12 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

6 How Angular Works 67


6.1 Refresher . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
6.2 Feature modules . . . . . . . . . . . . . . . . . . . . . . . . . 68
vi CONTENTS

6.3 Routing and modules . . . . . . . . . . . . . . . . . . . . . . 69


6.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

7 Home Page and an Introduction to Routing 71


7.1 Create the app . . . . . . . . . . . . . . . . . . . . . . . . . . 71
7.2 HomeComponent . . . . . . . . . . . . . . . . . . . . . . . . 72
7.3 Adding Bootstrap . . . . . . . . . . . . . . . . . . . . . . . . 76
7.4 Updating the AppComponent test . . . . . . . . . . . . . . . 78
7.5 Add to GitHub . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

8 Introduction to Testing 83
8.1 Karma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
8.2 Jasmine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
8.3 Unit testing . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
8.4 Integration testing . . . . . . . . . . . . . . . . . . . . . . . . 85
8.5 End-to-end (E2E) testing . . . . . . . . . . . . . . . . . . . . 86
8.6 The testing pyramid . . . . . . . . . . . . . . . . . . . . . . . 87
8.7 Why do we test? . . . . . . . . . . . . . . . . . . . . . . . . . 88
8.8 How to approach testing . . . . . . . . . . . . . . . . . . . . 89
8.9 Testing Adder . . . . . . . . . . . . . . . . . . . . . . . . . . 89
8.10 Test-driven development . . . . . . . . . . . . . . . . . . . . 92

9 User Signup 95
9.1 Auth service . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
CONTENTS vii

9.2 API documentation . . . . . . . . . . . . . . . . . . . . . . . 97


9.3 Auth service - signup . . . . . . . . . . . . . . . . . . . . . . 101
9.3.1 Update test setup . . . . . . . . . . . . . . . . . . . . 102
9.3.2 Importing HTTP testing modules . . . . . . . . . . . 103
9.3.3 Signup test (success) . . . . . . . . . . . . . . . . . . 105
9.3.4 Why mock? . . . . . . . . . . . . . . . . . . . . . . . 108
9.3.5 Implement signup . . . . . . . . . . . . . . . . . . . 110
9.4 Signup feature . . . . . . . . . . . . . . . . . . . . . . . . . . 111
9.4.1 Routing . . . . . . . . . . . . . . . . . . . . . . . . . 111
9.4.2 Signup form . . . . . . . . . . . . . . . . . . . . . . 115
9.4.3 Signup form functionality . . . . . . . . . . . . . . . 117
9.4.4 Dietary preferences (form) . . . . . . . . . . . . . . . 119
9.4.5 Dietary preferences (code) . . . . . . . . . . . . . . . 120
9.4.6 Signup test (error) . . . . . . . . . . . . . . . . . . . 122
9.5 Display an error message . . . . . . . . . . . . . . . . . . . . 123
9.6 Auth service - login . . . . . . . . . . . . . . . . . . . . . . . 125
9.6.1 Login test (success) . . . . . . . . . . . . . . . . . . . 127
9.6.2 Add login service method . . . . . . . . . . . . . . . 128
9.7 Signup and login . . . . . . . . . . . . . . . . . . . . . . . . 130
9.7.1 Signup and login (test) . . . . . . . . . . . . . . . . . 130
9.7.2 Signup and login (code) . . . . . . . . . . . . . . . . 134
9.8 Signup component test . . . . . . . . . . . . . . . . . . . . . 136
9.8.1 Fix failing test . . . . . . . . . . . . . . . . . . . . . 139
9.8.2 Signup page setup . . . . . . . . . . . . . . . . . . . 141
viii CONTENTS

9.8.3 Signup page - success . . . . . . . . . . . . . . . . . 145


9.8.4 Signup page - error . . . . . . . . . . . . . . . . . . . 147
9.9 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

10 User Dashboard 151


10.1 Dashboard feature . . . . . . . . . . . . . . . . . . . . . . . . 151
10.1.1 Routing . . . . . . . . . . . . . . . . . . . . . . . . . 151
10.2 Redirect users . . . . . . . . . . . . . . . . . . . . . . . . . . 153
10.2.1 Signup component . . . . . . . . . . . . . . . . . . . 153
10.2.2 Add Router mock to signup test . . . . . . . . . . . . 154
10.2.3 Update signup success . . . . . . . . . . . . . . . . . 156
10.3 Installing Cypress (E2E testing) . . . . . . . . . . . . . . . . 157
10.4 Signup E2E test . . . . . . . . . . . . . . . . . . . . . . . . . 160
10.4.1 First Cypress test . . . . . . . . . . . . . . . . . . . . 161
10.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 166

11 Route guard (AuthGuard) 167


11.1 AuthGuard setup . . . . . . . . . . . . . . . . . . . . . . . . 167
11.2 Auth service - logged in status . . . . . . . . . . . . . . . . . 168
11.2.1 angular-jwt . . . . . . . . . . . . . . . . . . . . . . . 168
11.2.2 Add test for isLoggedIn . . . . . . . . . . . . . . . . 169
11.2.3 Implement isLoggedIn . . . . . . . . . . . . . . . . . 171
11.2.4 Add test for isLoggedIn . . . . . . . . . . . . . . . . 172
11.3 AuthGuard implementation . . . . . . . . . . . . . . . . . . . 173
11.4 AuthGuard test . . . . . . . . . . . . . . . . . . . . . . . . . 175
CONTENTS ix

11.5 Add guard to dashboard . . . . . . . . . . . . . . . . . . . . . 178


11.6 E2E test for dashboard . . . . . . . . . . . . . . . . . . . . . 180
11.7 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 180

12 Navbar 183
12.1 Component setup . . . . . . . . . . . . . . . . . . . . . . . . 183
12.2 Add isLoggedIn to navbar . . . . . . . . . . . . . . . . . . . 187
12.3 Event Emitter . . . . . . . . . . . . . . . . . . . . . . . . . . 188
12.3.1 Add Event Emitter to login . . . . . . . . . . . . . . . 189
12.3.2 Add Event Emitter to logout . . . . . . . . . . . . . . 191
12.4 Subscribe to event in NavbarComponent . . . . . . . . . . . . 192
12.5 Add logout to NavbarComponent . . . . . . . . . . . . . . . . 192
12.6 Navbar test . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
12.6.1 Logged in user . . . . . . . . . . . . . . . . . . . . . 197
12.6.2 Logged out user . . . . . . . . . . . . . . . . . . . . . 200
12.7 Navbar E2E test . . . . . . . . . . . . . . . . . . . . . . . . . 201
12.7.1 Add first test suite . . . . . . . . . . . . . . . . . . . 201
12.7.2 Cypress command . . . . . . . . . . . . . . . . . . . 203
12.7.3 Add second test suite . . . . . . . . . . . . . . . . . . 204
12.8 Update signup E2E test . . . . . . . . . . . . . . . . . . . . . 205
12.9 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 206

13 User Login 209


13.1 Module setup . . . . . . . . . . . . . . . . . . . . . . . . . . 209
13.2 Add login form . . . . . . . . . . . . . . . . . . . . . . . . . 210
x CONTENTS

13.3 Implement login . . . . . . . . . . . . . . . . . . . . . . . . . 212


13.4 Login test . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
13.5 Update navbar . . . . . . . . . . . . . . . . . . . . . . . . . . 221
13.6 Login E2E test . . . . . . . . . . . . . . . . . . . . . . . . . 222
13.7 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 227

14 Creating Events 229


14.1 Setup and routing . . . . . . . . . . . . . . . . . . . . . . . . 229
14.2 Auth interceptor . . . . . . . . . . . . . . . . . . . . . . . . . 232
14.2.1 Implement interceptor . . . . . . . . . . . . . . . . . 232
14.3 Add interceptor to app providers . . . . . . . . . . . . . . . . 235
14.3.1 Test interceptor . . . . . . . . . . . . . . . . . . . . . 236
14.4 Events service . . . . . . . . . . . . . . . . . . . . . . . . . . 240
14.4.1 Event create test . . . . . . . . . . . . . . . . . . . . 241
14.4.2 Implement event create . . . . . . . . . . . . . . . . . 244
14.4.3 Event error test . . . . . . . . . . . . . . . . . . . . . 245
14.5 Auth service - current user . . . . . . . . . . . . . . . . . . . 246
14.6 Packages and modules . . . . . . . . . . . . . . . . . . . . . 248
14.6.1 Reactive forms . . . . . . . . . . . . . . . . . . . . . 249
14.6.2 Date/time picker . . . . . . . . . . . . . . . . . . . . 250
14.6.3 Google Maps API key . . . . . . . . . . . . . . . . . 253
14.7 Form setup . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
14.7.1 Add Google Places autocomplete . . . . . . . . . . . 271
14.7.2 Add onSubmit method . . . . . . . . . . . . . . . . . 275
CONTENTS xi

14.8 Cypress tests . . . . . . . . . . . . . . . . . . . . . . . . . . 278


14.8.1 Event success . . . . . . . . . . . . . . . . . . . . . . 279
14.8.2 Event error . . . . . . . . . . . . . . . . . . . . . . . 281
14.9 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 282

15 Dashboard Calendar 285


15.1 Get user events . . . . . . . . . . . . . . . . . . . . . . . . . 285
15.2 Add “getUserEvents” to DashboardComponent . . . . . . . . 288
15.3 Update dashboard test . . . . . . . . . . . . . . . . . . . . . . 289
15.4 Angular Calendar . . . . . . . . . . . . . . . . . . . . . . . . 293
15.4.1 Installation and setup . . . . . . . . . . . . . . . . . . 293
15.4.2 Calendar template . . . . . . . . . . . . . . . . . . . 294
15.4.3 Add calendar event properties . . . . . . . . . . . . . 299
15.5 Update test . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
15.6 Update dashboard E2E test . . . . . . . . . . . . . . . . . . . 315
15.7 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 318

16 Event View 321


16.1 Setup and routing . . . . . . . . . . . . . . . . . . . . . . . . 321
16.2 Get event . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
16.3 Add call to event view . . . . . . . . . . . . . . . . . . . . . 328
16.4 Event view test . . . . . . . . . . . . . . . . . . . . . . . . . 334
16.5 E2E tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
16.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
xii CONTENTS

17 Event Comments 339


17.1 Comments service . . . . . . . . . . . . . . . . . . . . . . . . 339
17.1.1 Test setup . . . . . . . . . . . . . . . . . . . . . . . . 340
17.1.2 Comment create . . . . . . . . . . . . . . . . . . . . 341
17.1.3 Get event comments . . . . . . . . . . . . . . . . . . 344
17.2 Comment create module . . . . . . . . . . . . . . . . . . . . 346
17.2.1 Add comment create to event module . . . . . . . . . 347
17.2.2 Comment create @Input . . . . . . . . . . . . . . . . 351
17.2.3 Get comments for event . . . . . . . . . . . . . . . . 352
17.2.4 Fix tests . . . . . . . . . . . . . . . . . . . . . . . . . 355
17.2.5 Add comment create . . . . . . . . . . . . . . . . . . 358
17.2.6 Comment create test . . . . . . . . . . . . . . . . . . 362
17.3 E2E tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
17.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 369

18 Events List 371


18.1 Get all events . . . . . . . . . . . . . . . . . . . . . . . . . . 371
18.2 Events list module . . . . . . . . . . . . . . . . . . . . . . . . 373
18.2.1 Configure routing . . . . . . . . . . . . . . . . . . . . 374
18.2.2 Get events . . . . . . . . . . . . . . . . . . . . . . . . 375
18.3 Events list test . . . . . . . . . . . . . . . . . . . . . . . . . . 378
18.3.1 Test - no existing events . . . . . . . . . . . . . . . . 380
18.3.2 Test - existing events . . . . . . . . . . . . . . . . . . 382
18.4 Update navbar . . . . . . . . . . . . . . . . . . . . . . . . . . 384
CONTENTS xiii

18.5 E2E tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385


18.5.1 Navbar . . . . . . . . . . . . . . . . . . . . . . . . . 385
18.5.2 Events list . . . . . . . . . . . . . . . . . . . . . . . . 386
18.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 389

19 Event Subscribe/Unsubscribe 391


19.1 Events service . . . . . . . . . . . . . . . . . . . . . . . . . . 391
19.1.1 IsEventCreator . . . . . . . . . . . . . . . . . . . . . 391
19.1.2 Subscribe . . . . . . . . . . . . . . . . . . . . . . . . 394
19.2 Member list module . . . . . . . . . . . . . . . . . . . . . . . 396
19.2.1 Update event module . . . . . . . . . . . . . . . . . . 397
19.2.2 Add input properties . . . . . . . . . . . . . . . . . . 400
19.2.3 Display member list . . . . . . . . . . . . . . . . . . 401
19.3 Subscribe/Unsubscribe . . . . . . . . . . . . . . . . . . . . . 402
19.3.1 isCreator . . . . . . . . . . . . . . . . . . . . . . . . 402
19.3.2 isMember . . . . . . . . . . . . . . . . . . . . . . . . 404
19.3.3 Update view . . . . . . . . . . . . . . . . . . . . . . 404
19.4 Subscribe . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
19.5 Member list test . . . . . . . . . . . . . . . . . . . . . . . . . 407
19.5.1 Event creator . . . . . . . . . . . . . . . . . . . . . . 411
19.5.2 Non-event creator . . . . . . . . . . . . . . . . . . . . 412
19.6 E2E tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
19.6.1 Login command . . . . . . . . . . . . . . . . . . . . 415
19.6.2 Event subscribe test . . . . . . . . . . . . . . . . . . . 416
xiv CONTENTS

19.7 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 419

20 Event Recommendations 421


20.1 Recommendations service . . . . . . . . . . . . . . . . . . . 421
20.2 Recommendations list . . . . . . . . . . . . . . . . . . . . . . 426
20.2.1 Update EventModule . . . . . . . . . . . . . . . . . . 427
20.2.2 Update EventViewComponent . . . . . . . . . . . . . 427
20.3 RecommendationsListComponent . . . . . . . . . . . . . . . 430
20.4 RecommendationsListComponent test . . . . . . . . . . . . . 432
20.4.1 Recommendations . . . . . . . . . . . . . . . . . . . 435
20.4.2 No recommendations . . . . . . . . . . . . . . . . . . 436
20.4.3 Suggest locations set to false . . . . . . . . . . . . . . 437
20.5 E2E tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
20.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 441

21 Event Update 443


21.1 Update event . . . . . . . . . . . . . . . . . . . . . . . . . . 443
21.2 Event update . . . . . . . . . . . . . . . . . . . . . . . . . . 446
21.2.1 Setup and routing . . . . . . . . . . . . . . . . . . . . 446
21.2.2 Update EventViewComponent . . . . . . . . . . . . . 447
21.2.3 Update EventViewComponent test . . . . . . . . . . . 448
21.2.4 EventUpdateComponent . . . . . . . . . . . . . . . . 451
21.3 E2E tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
21.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
CONTENTS xv

22 Route Guard (EventGuard) 467


22.1 EventGuard implementation . . . . . . . . . . . . . . . . . . 467
22.2 EventGuard test . . . . . . . . . . . . . . . . . . . . . . . . . 470
22.3 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 472

23 Deploy to Heroku 475


23.1 Update environment variables . . . . . . . . . . . . . . . . . 475
23.2 Update services . . . . . . . . . . . . . . . . . . . . . . . . . 476
23.2.1 AuthService . . . . . . . . . . . . . . . . . . . . . . . 476
23.2.2 CommentsService . . . . . . . . . . . . . . . . . . . 477
23.2.3 EventsService . . . . . . . . . . . . . . . . . . . . . . 477
23.2.4 RecommendationsService . . . . . . . . . . . . . . . 478
23.3 MongoDB Atlas setup . . . . . . . . . . . . . . . . . . . . . 478
23.4 Heroku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
23.4.1 CLI . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
23.4.2 Create an account . . . . . . . . . . . . . . . . . . . . 488
23.4.3 API Heroku app . . . . . . . . . . . . . . . . . . . . 488
23.4.4 Angular app . . . . . . . . . . . . . . . . . . . . . . . 491
23.5 Restrict Google Maps API Key . . . . . . . . . . . . . . . . . 497
23.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 500

24 Appendix: Going Forward 503


24.1 A few things to consider . . . . . . . . . . . . . . . . . . . . 504
24.1.1 Deploy more often to catch issues . . . . . . . . . . . 504
24.1.2 Cypress - bypassing the UI . . . . . . . . . . . . . . . 505
xvi CONTENTS

24.1.3 Testing errors . . . . . . . . . . . . . . . . . . . . . . 505


24.1.4 Testing, and coding, is an art . . . . . . . . . . . . . . 506
24.1.5 Testing is second to code IRL . . . . . . . . . . . . . 506
24.2 Soapbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
24.2.1 Algorithms in interviews . . . . . . . . . . . . . . . . 507
24.2.2 The Bay Area . . . . . . . . . . . . . . . . . . . . . . 508
24.2.3 The industry is desperate for talent . . . . . . . . . . . 509
24.2.4 Interviewing is a bitch . . . . . . . . . . . . . . . . . 509
24.2.5 Race and gender . . . . . . . . . . . . . . . . . . . . 510
24.2.6 Impostor syndrome . . . . . . . . . . . . . . . . . . . 510
24.2.7 Get on LinkedIn . . . . . . . . . . . . . . . . . . . . 511

25 Appendix: Practice 513


25.1 Public APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
25.1.1 Pokéapi . . . . . . . . . . . . . . . . . . . . . . . . . 513
25.1.2 CoinMarketCap API . . . . . . . . . . . . . . . . . . 514
25.1.3 Other public APIs . . . . . . . . . . . . . . . . . . . 514
25.2 Mocking an API using observables . . . . . . . . . . . . . . . 514
Preface
Just under five years ago I was working in Cincinnati as a data analyst for a
marketing consulting firm when I decided to quit my job, move to Atlanta,
and begin my career as a software developer. I had taken a few programming
courses in college and thought I knew enough to hit the ground running on day
one. I was wrong.
I started my career working on a Java stack using a front-end framework 99%
of software developers I talk to have never heard of, JavaScriptMVC1 . It didn’t
take me long to realize how little I knew about web development.
Not long after I quit that job and quickly realized how unemployable I was. I
didn’t have much experience on my resume and I knew next to nothing about
web development. I spent the following months learning enough web develop-
ment to become employable and have been consistently employed as a software
developer since then. I’ve worked at a variety of companies including Fortune
500 companies, startups, creative agencies, and an angel investment firm. I’ve
worked with multiple, front-end frameworks building traditional web applica-
tions, mobile apps, TV apps, and real-time video conferencing software.
During my journey to becoming a software developer there were very few com-
prehensive resources for teaching web development in a way that I felt prepared
me for real, on-the-job work. So many tutorials seemed to cut corners teaching
enough for me to be familiar with the high level concepts of a framework but
still feeling like I was unable to build an application by myself, from start to
finish.
1
http://www.javascriptmvc.com/

xvii
xviii PREFACE

The most common shortcoming when teaching front-end frameworks is the


tutorial that teaches the framework but mocks the entire API (a significant part
of front-end development) as hard-coded data. This approach is fine for more
experienced developers who are already familiar with another framework. But
for beginners like myself it felt like a gigantic gap that was missing and very
needed.
This book is the book I wish I had when I was just starting out. My goal for
this book is to simulate what it would be like to work as a front-end developer
to give you what you need to start your own career.
A database and API won’t be mocked and hard-coded. Instead, you’ll set it up
yourself just as I’ve done numerous times at real companies. You’ll learn how
to interact with this API using API documentation that’s provided to you. You
won’t build everything yourself from scratch. Instead, you’ll learn to leverage
existing libraries such as angular-calendar2 to do some of the heavy lift-
ing for you. Automated testing, a topic that is often entirely unaddressed in
tutorials (but they do stress the importance of it!) will be thoroughly covered.
Software development is daunting. Many people, often before even trying,
assume they aren’t intelligent enough to learn. When I was in college, I thought
the same thing. I dropped out of CS and only after a year out of college as a
data analyst did I decide to pursue it again.
You hear similar things in art. People say they aren’t creative enough to learn
how to draw. They don’t have the right gene for that. They’re wrong3 . Learning
how to draw, like learning how to code, is just a difficult thing to do. And it’s
difficult because it’s so transparently obvious when you suck at it. But with
some patience and persistence, you can learn.
Some of the material in this book is difficult. That’s normal. You’re going
to run into what feels like a brick wall. This happens to everyone. Don’t be
too hard on yourself. Some concepts won’t make sense right away. Move
on, if necessary, and come back to it later. A later section or chapter may

2
https://mattlewis92.github.io/angular-calendar/#/kitchen-sink
3
https://www.youtube.com/watch?v=aVrerOv73o8
xix

clarify things for you. Take a walk to clear your mind and things may click
when you’re away from your computer when you least expect it to. I hit brick
walls myself while writing this book. How in the hell do I explain this? Just
like coding, I’d have breakthroughs when I wasn’t at my computer writing the
book. I’d be taking a shower and a new approach to explaining a concept would
just come to me. Be patient, but persistent.
Our education system is outdated and alternatives are desparately needed. If
you know how to code, employers don’t care if you don’t have a degree. This
book is my contribution to give people an alternative to the current system.
Writing this book is one of the most difficult things I’ve ever done. I’ve done
my best to provide you the material you need to learn Angular and hopefully
get you started on a path to a career as a software developer. I hope you find it
valuable. Good luck.

If you run into trouble at any point in this book, I’d suggest comparing your
code to the reference code4 for this book that contains the finished code for
each chapter in this book. You can also post questions on Stack Overflow5
using the tag “theangulartutorial”6 .
You’re also welcome to join a Discord community I’ve created just for this
book. Click here7 to join this group.
(If you’re reading a print copy of this book and you’d like a digital copy in the
form of a PDF, email me a copy of your receipt to atom.morgan@gmail.com)

4
https://github.com/theangulartutorial/lets-get-lunch-code-ng8
5
https://stackoverflow.com/
6
https://stackoverflow.com/questions/tagged/theangulartutorial
7
https://discord.gg/xMc7n3Z
xx PREFACE
Chapter 1

Book Methodology

1.1 The approach

In this book, my goal is to provide you the experience of working as a real


front-end developer as you learn Angular.
I remember when I was first learning a front-end framework and feeling as if
so many tutorials were leaving out big parts of actual coding. An API would be
substituted with hard-coded data. A functional application would be built with
all of the code in a single file with a disclaimer near the end stating, “This isn’t
how you’d do it in real life.”
Since I found those practices to be extremely frustrating, I want to avoid all of
that.
In this book you will be provided with a code repository that will serve as
the API that your Angular application will interact with. You will be walked
through installation, tooling setup, database setup, etc., until you have a local,
working API.
At that point, well walk through the API’s documentation and the design specs
for the application to be built in Angular. From there, you will build an Angular
application from scratch with a heavy emphasis on automated testing.

1
2 CHAPTER 1. BOOK METHODOLOGY

This process isn’t at all unlike one you may expect to see at your first job as a
front-end developer where a separate team would be responsible for developing
the API. You’re merely responsible for consuming it.

1.2 Who is this for?

1.2.1 The wannabe front-end developer with a grasp on the


basics

What are the basics? HTML, CSS, and the fundamentals of JavaScript and/or
TypeScript.
As far as HTML and CSS go, I won’t be taking the time to explain the differ-
ences between a <div> and a <span>, the box model in CSS, or the various
selectors at our disposal. The templates and styling for code will be provided
(using Bootstrap1 which is a popular front-end framework for designing web-
sites) but a detailed explanation of the two will not be provided. However,
any HTML or CSS that’s closely tied to Angular (we’ll see this later when we
tackle forms) will be explained in greater detail.
As for JavaScript and TypeScript, I’m assuming a basic understanding of fun-
damentals such as syntax, variables, data structures (boolean, undefined, Num-
ber, String, and Object), if-else statements, loops, functions, and operators such
as ==, ===, &&, ||, >, <, >=, and so on. If you know JavaScript but have never
used TypeScript, don’t worry. You’ll find the majority of it will be easy to pick
up as we go.
The code provided later will be explained but the basic workings of TypeScript
will not.

1
https://getbootstrap.com/docs/3.3/
1.3. WHAT THIS BOOK IS VS. WHAT THIS BOOK ISN’T 3

1.2.2 The Angular developer who wants to learn automated


testing
If you already know Angular but you want to learn automated testing, great.
This book will provide deep-dives into test setup, unit testing, integration test-
ing, and end-to-end testing, what and what not to test, and how to test using
tools such as Karma, Jasmine, and Cypress.

1.2.3 The front-end developer who wants to learn Angular


If you already know a front-end framework but you want to learn Angular, this
book will be a good fit for you. While there is a heavy emphasis on testing,
the various features provided to us by Angular will be explained as well as we
continue from chapter to chapter.

1.3 What this book is vs. what this book isn’t


As we touched on earlier, this book is not an introduction to HTML, CSS, and
JavaScript or TypeScript. This is not a book to teach you design. This is a book
that will teach you modern web development and automated testing using the
Angular framework.
We’ll be focusing primarily on the development side of front-end development
which is why we’re resorting to other frameworks such as Bootstrap2 to handle
the styling of our website. Bootstrap (or an equivelant) framework is a tool I’ve
used at nearly every development job I’ve had so learning the basics along the
way is valuable knowledge that you’re likely to use again at some point in your
career.
We’ll also be working with Git3 , a popular version control system that is used
2
https://getbootstrap.com/docs/3.3/
3
https://git-scm.com/
4 CHAPTER 1. BOOK METHODOLOGY

for source code management. To host our code that we manage with Git, we’ll
be using GitHub4 . Once again, these are tools I’ve used on most teams as a
developer and ones you can expect to use at some point as well. Our use of
these two within this book will be rather basic and we’ll get into more details
on this later.

1.4 Why am I writing this book?


Despite automated testing being a common interview topic, a lot of compa-
nies just dont do it. I experienced it myself at multiple well-known companies
across an even greater number of teams.
Once I started searching for materials on the topic, I realized there was a few
reasons for this:

1. Documentation for automated testing often feels like an afterthought


2. Tutorials are nearly non-existent
3. Very few people understand how and why we should test our code

Unfortunately, these barriers prevent many developers from ever experiencing


what it actually feels like to work with tested code.
Most importantly, I wanted to contribute a comprehensive resource that I wish
had existed when I was learning front-end development.

1.5 What are we building?


We’re going to imagine we work for a company building an internal tool to help
employees coordinate lunches and happy hours. We’ll refer to the application
as “Let’s Get Lunch”.
4
https://github.com/
1.5. WHAT ARE WE BUILDING? 5

Figure 1.1: Home Page

Like most applications, we’ll provide users the ability to create accounts where
they can choose a username and password and select a set of preferred “dietary
preferences”.
6 CHAPTER 1. BOOK METHODOLOGY

Figure 1.2: Signup

After creating an account, users can then create “events” specifying a start date,
end date, and location. They’ll be able to view these “events” on a dashboard
which contains a user-friendly calendar to display their events.
1.5. WHAT ARE WE BUILDING? 7

Figure 1.3: Event Create


8 CHAPTER 1. BOOK METHODOLOGY

Figure 1.4: Dashboard

Within these events, users can see a list of people who have “subscribed” to
the event (those who are opting-in to attend the event). We will also provide
a comment section within the event so users can further discuss details of the
event.
1.6. CONVENTIONS USED IN THIS BOOK 9

Figure 1.5: Event View

In addition to comments, we’ll also provide the option to suggest locations for
an event. If selected, a request will be made for a list of nearby restaurants that
match the dietary preferences of the users who have subscribed to the event.

1.6 Conventions used in this book


There are a few conventions used within this book that are worth addressing
before moving forward.
Throughout the book we’ll be using command-line interfaces to run commands.
These will typically be put inside formatted blocks like the one shown below.
10 CHAPTER 1. BOOK METHODOLOGY

echo hello

In addition to blocks are inline code, such as const. Inline, formatted text such
as const always relates to something within our code (including some error
messages provided to us by Angular). The inline code is a visual aid to dis-
tinguish between plain English and technically related things such as variable
names, file names, error messages, and occasionally a command-line interface
command.
So we may be talking about providing a user the ability to signup for an ac-
count, which is different from a signup function we may be writing. The
differences between the two within the book should be fairly straightforward.
Occassionally, there are times where a long piece of inline, formatted text
breaks from one line to the next. As expected, this division at the end of a
line requires hyphenation. This can get a bit weird with long function names
or file names such as event-view.module.ts that when hyphenated, ends
up with event-view- being one one line and .module.ts being on the next
line. When this occurs in the book just keep in mind that the hyphenation is
there as a part of the formatting process for this book. Thankfully, this isn’t
that common and even when it is there’s typically additional references to the
file or function names around the hyphenated version that should let you know
it’s just a formatting issue.
An example of an additional reference would be the file names found above
code blocks whenever we’re adding code to a file.

Listing 1.1
src/app/some-file.js
function myNewFunction() {
return true;
}

So if a hyphenated file name does throw you off, you can always refer to the
filepath found at the top of the code blocks as shown above.
Chapter 2

How Web Applications Work

2.1 Overview
I want to begin by explaining how web applications work within the context
of the technologies we’ll be using within this book: MongoDB, Node.js, and
Angular.

2.2 MongoDB
MongoDB is an open-source NoSQL (also referred to as non-relational or
document-oriented) database that represents and stores data in JSON-like doc-
uments1 . Relational databases such as Microsoft SQL Server, MySQL and
PostgreSQL represent and store data in tables using rows.
In relational databases, every table has a schema that defines the columns and
data types for every row in the table. In non-relational or document-oriented
databases, there’s no defined schema and every document can be structured
differently. This gives non-relational databases more flexibility with documents
1
https://www.mongodb.com/scale/relational-vs-non-relational-database

11
12 CHAPTER 2. HOW WEB APPLICATIONS WORK

that may be updated to our needs without the need to modify database schemas
to include any new columns and their data types.
This doesn’t mean that non-relational databases are better than relational databases.
Like most engineering decisions, the right tool depends on the job2 . Since
this book focuses on front-end development, we won’t be doing much with
our database other than writing and reading data through the API. But at the
very least, it’s worth knowing the two types of databases (relational and non-
relational) and some of the most common database names you may see when
developers are talking about databases.

2.3 Node.js
Node.js is an open-source, cross-platform run-time environment for executing
JavaScript code server-side. Historically, JavaScript was used for client-side
scripting with scripts embedded in a webpage’s HTML.
Node.js enables JavaScript to be used for server-side scripting with the possi-
bility to produce dynamic web page content before a page is sent to a user’s
browser.

Box 2.1.

In our case, Node.js won’t be producing our web pages. Instead, it will be receiv-
ing and sending data to our Angular application which will generate the web pages
our user sees. We’ll go into more detail on this shortly.

As a result of Node.js, web application development can now revolve around


a single programming language rather than relying on an entirely separate lan-
guage to write server-side scripts.
2
http://www.jamesserra.com/archive/2015/08/relational-databases-vs-non-relational-databases/
2.4. ANGULAR 13

If you’ve heard of languages like Go, Ruby, Python, PHP, Java, C#, and oth-
ers (especially within the context of APIs) Node.js is yet another server-side
scripting language like these.
Along with Node.js is its package manager, npm. npm is bundled with Node.js
and allows developers to use a variety of packages to extend the functionality
of Node.js.
When we setup our API we’ll be using npm to install some of these packages
that were used to build the API your Angular application will be interacting
with.

2.4 Angular
Angular (also referred to as Angular 2, Angular 2+, Angular 5) is a TypeScript-
based open-source front-end framework built by Google. If you’re familiar
with AngularJS, an earlier framework also written by Google, it is a complete
rewrite so the two are in no way related.
Like many other front-end frameworks, Angular provides us with tools to write
modern web applications. This includes a file structure to organize our applica-
tion, a command-line interface to create files for us, modules and components
to group our HTML, CSS, and client-side scripting (TypeScript/JavaScript),
forms for gathering user input, services to interact with 3rd party APIs, routing
for the various pages in our application, and so on.
Some of these features will become more clear later on as we begin to use
them but the primary takeaway is that a framework gives us an easy way to use
our HTML, CSS, and TypeScript knowledge to build web applications without
having to build every feature ourselves.
If you’ve ever played a video game with a built-in level creator, you can think
of frameworks as a level creator for the web. In a video game’s level creator,
there are certain assets available for you to use such as structures, textures,
enemies, items, and so on. You may even be given the option to build a structure
14 CHAPTER 2. HOW WEB APPLICATIONS WORK

from base components to create a new structure that you can re-use and even
share with others. Web frameworks essentially do the same thing. But rather
than providing us textures and structures that exist within the game’s universe,
we’re given the various tools we need to create modern web applications such
as routing, form builders, form validation, and services to abstract many of the
details of making HTTP requests from us. It provides us the basic building
blocks for creating a web application.

2.5 The Client-Server model


As we begin to build our application, we’ll be building it using a client-server
model. The client-server model is more popular than ever nowadays due to the
variety of clients (devices) that we use every day to access the Internet.
Going back to the technologies we discussed earlier, our client-server model
can be separated like this:

Server

• Database (MongoDB)

• Server/API (Node.js)

Client

• Front-end application (Angular)

With that client-server split in mind, let’s visualize how these parts communi-
cate with each other.
2.5. THE CLIENT-SERVER MODEL 15

Figure 2.1: Client-Server Model

Let’s begin with a real world example. When I go to my Twitter page, https://-
twitter.com/atommorgan, a request is made to Twitter’s API to find the user
“atommorgan”. Twitter’s API then queries their database to see if a user with
the username “atommorgan” exists. If it does, it sends data (containing infor-
mation such as tweets) back to the API which then returns that data in the form
of JSON to the front-end client. From here, the front-end displays the user’s
profile page and all of their related tweets in the browser. An entire client-server
request-response cycle has been completed.
Because of this client-server split the server and the web in general has much
more flexibility. Rather than providing actual web pages, it can merely provide
data that’s used to construct web pages. With this approach, one server can
power a web-based Twitter as well as iOS and Android versions of Twitter
which both use their own frameworks and languages to create apps.
You may even own a console or smart TV with their own apps with entirely
different UIs from the ones you see on your phone. Once again, a single server
16 CHAPTER 2. HOW WEB APPLICATIONS WORK

can send the exact same data to these various clients where the client can decide
how to visualize that data.
As I mentioned earlier, the server which we’ll refer to as the API going forward,
in the client-server model will be provided for you in this book. Our goal is to
consume that API with an Angular application.
Chapter 3

Getting Started and


Installation
Before we get started, there are a number of tools and technologies we’ll need
to download and install.

3.1 Tool requirements


• Sublime Text/Visual Studio Code

• Terminal (Hyper or iTerm2 for Mac/Hyper for Windows)

• Postman

• Robo 3T (formerly Robomongo)

3.2 Technical requirements


• Git

17
18 CHAPTER 3. GETTING STARTED AND INSTALLATION

• Node.js

• npm (included with Node.js)

• nvm

3.3 Text editor


One of the first things you’ll need before we begin writing Angular code is a
text editor to write your code in. Two popular choices at the moment are Visual
Studio Code1 and Sublime Text2 . Either one of these will do just fine, they’re
largely a matter of personal preference.
Visual Studio Code is 100% free and open-source. Sublime Text is free to
download as well but there is an $80 fee for a license which you’ll inevitably
see in a prompt if you use Sublime Text long enough.

3.4 Terminal
Next we’ll need to get a terminal emulator for all of the command-line work
we’ll be doing.
If you’re on Mac, you have the option of either iTerm23 or Hyper4 . Our needs
throughout this book are rather simplistic so again this is largely a matter of
personal preference. I use iTerm2 myself.
If you’re on Windows, the popular option for you would be Hyper5 .
1
https://code.visualstudio.com/
2
https://www.sublimetext.com/3
3
https://www.iterm2.com/
4
https://hyper.is/
5
https://hyper.is/
3.5. GIT 19

3.5 Git

Next you’ll need to install Git6 . Git is an open source version control system
which allows you to track changes in source code and coordinate working on
these files among a team. Git is what you’ll be using to push your code to
Github7 and clone (or download) existing code repostories that other developers
have created which is the focus of Chapter 4.
Once you’ve installed Git you can verify it was installed by running the follow-
ing command:

git --version

3.5.1 Windows users

When running through the installation process, ensure “Use Git from the Win-
dows Command Prompt” is selected. All of the other options in the installation
prompt can be left to their default settings.

6
https://git-scm.com/
7
https://github.com/
20 CHAPTER 3. GETTING STARTED AND INSTALLATION

Figure 3.1: Installing Git

Once Git is installed, you’ll need to edit some preferences within Hyper. First,
open Hyper. Then go to Edit > Preferences. Within the preferences file that
opens you should see a shell, shellArgs, and env property. Update those
values to what’s shown below:

Listing 3.1
.hyper
shell: 'C:\\Program Files\\Git\\git-cmd.exe',
shellArgs: ['--command=usr/bin/bash.exe', '-l', '-i'],
env: {
TERM: 'cygwin'
}
3.6. POSTMAN 21

Figure 3.2: Updated Hyper Preferences

Restart Hyper and you should now be able to verify Git is working correctly by
running:

git --version

3.6 Postman

The next tool you’ll need is Postman8 . This is a GUI tool we’ll be using to
make requests to our API without the need to write actual code. We’ll get into
the finer details of Postman later.

8
https://www.getpostman.com/
22 CHAPTER 3. GETTING STARTED AND INSTALLATION

3.7 Robo 3T
The last tool you’ll need to download is Robo 3T9 (formerly Robomongo).
This provides us with a GUI for viewing the data we’ll eventually create in our
database.

3.8 Node.js
With all of those tools installed, it’s now time to download and install Node.js.
You can do this by visiting the Node.js website10 and downloading the Node.js
installer which will walk you through the process.
Once you’ve finished the installation process, you can double check that every-
thing has been installed correctly by opening a terminal window and running
the following command:

node -v

You should see an output similar to v10.x.x. As long as your version is


v7.6.0 or higher you should be good to go.
You can also verify npm has been installed along with Node by running a sim-
ilar command:

npm -v

Again, you should see an output along the lines of 5.x.x.


9
https://robomongo.org/
10
https://nodejs.org
3.9. NVM 23

3.9 nvm
Finally, we’re going to install nvm which is a handy tool to allow you to manage
and use multiple Node.js versions on one computer. If you happen to be work-
ing on multiple projects, each using a different version of Node, nvm will allow
you to easily switch between them reducing any friction for your development
workflow.

3.9.1 Mac
If you’re on a Mac you can install nvm by running the following command.

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash

You can verify nvm was installed by running the following command.

nvm --version

3.9.2 Windows
If you’re on Windows, you can get the latest version of nvm for windows on the
releases page11 for nvm-windows. The file name should be nvm-setup.zip.
Once you’ve finished going through the installer you can verify nvm was in-
stalled by running the following command.

nvm --version

11
https://github.com/coreybutler/nvm-windows/releases
24 CHAPTER 3. GETTING STARTED AND INSTALLATION
Chapter 4

API Setup and Installation

4.1 Installing MongoDB

4.1.1 Mac installation

The first step to getting our API setup is to install MongoDB, the database the
API uses. The easiest way to install MongoDB is through Homebrew. You
can visit the Homebrew website1 for more details but the install process is
straightforward. You’ll just need to run this in a terminal window.

/usr/bin/ruby -e \
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Once that has completed you can now install MongoDB with Homebrew.

brew install mongodb

1
https://brew.sh/

25
26 CHAPTER 4. API SETUP AND INSTALLATION

4.1.2 Windows installation

On Windows, you can install MongoDB here2 .


In the installation prompt you may be asked to install MongoDB Compass.
Feel free to omit that portion of the installation since we installed Robo3T in
Chapter 3. Everything else can be left to their default settings.

4.1.3 Creating the MongoDB data directory

Once you’ve installed MongoDB, you’ll need to create the data directory that
MongoDB will use to write data to.

mkdir -p /data/db

If you’re using Windows, the command is slightly different.

mkdir \data\db

With the data directory created, verify that MongoDB has been installed and
everything is working correctly.

mongod

Once again, the command for Windows is a bit different.

"C:\Program Files\MongoDB\Server\4.0\bin\mongo.exe"

2
https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/#get-mongodb-community-edition
4.1. INSTALLING MONGODB 27

When you run that you should see a bunch of text logged to your terminal
window.
Near the bottom you should see something like: NETWORK [initandlisten]
waiting for connections on port 27017.

Figure 4.1: Running MongoDB

With that, everything is working correctly so you can close your MongoDB
connection by typing Ctrl + C.
28 CHAPTER 4. API SETUP AND INSTALLATION

4.2 Create GitHub account


While we’re working through this book, we’ll be periodically pushing our code
to GitHub. GitHub is a development platform that’s primarily used for hosting
code. GitHub and other similar services such as GitLab and Bitbucket are
tools you’re likely to encounter in your professional career so it doesn’t hurt to
familiarize yourself with them now.
Head over to GitHub3 and create an account.
Once your account has been created you’ll now have a GitHub profile which
you can access in the top right corner by clicking on the avatar image and
selecting “Your profile”.

Figure 4.2: Accessing your GitHub profile

3
https://github.com
4.3. CREATING A ZOMATO ACCOUNT 29

Figure 4.3: Your GitHub profile

Your profile URL will be needed in our next step as we setup your Zomato
account to get an API key.
Feel free to leave your GitHub window open since we’ll need it at the end of
this chapter when we add this API to your newly created account.

4.3 Creating a Zomato account


As I mentioned earlier, our application is going to have the ability to provide
restaurant recommendations for events. The API will be making this request
using a service known as Zomato.
In order for the API to make that request, it’s going to need an API key which
you’ll provide in a configuration file we’re going to set up next. But first, we
30 CHAPTER 4. API SETUP AND INSTALLATION

need a Zomato API key.


Note: Occasionally services such as Zomato will update their registration flows
which will affect the steps outlined here. If so, you can find the most up-to-date
steps on GitHub in the “updated-account-registration-flows”4 directory of the
source code for this book
To do this, visit Zomato5 and create an account. Once you’ve created your
account, you can then visit the developer’s section6 . Near the bottom of the
screen you should see a section titled “Request An API Key” with a button that
says “Generate API key”. Click that and you should see a window asking for
additional information. Complete the form, adding your GitHub profile as the
“Company or Blog URL”, and you’ll be given an API key. Go ahead and leave
this window open since we’ll need that key shortly.

4
https://github.com/theangulartutorial/lets-get-lunch-code-ng8/tree/master/updated-account-registration-
flows
5
https://www.zomato.com
6
https://developers.zomato.com/api
4.3. CREATING A ZOMATO ACCOUNT 31

Figure 4.4: Generate API Key


32 CHAPTER 4. API SETUP AND INSTALLATION

Figure 4.5: Zomato API Key

4.4 Cloning the API repository


With MongoDB setup and our Zomato API key ready to go, we’re now ready
to setup our API.
First we’ll create a directory for where our code will live.

mkdir book-code && cd book-code

From there, run the following command to “clone” the API code to your direc-
tory.
4.5. CREATING CONFIG FILES 33

git clone https://github.com/theangulartutorial/lets-get-lunch-api.git

If you’re following along with the examples, you’re directory structure should
now look like this:

| book-code
| lets-get-lunch-api

Now that we’ve cloned the repository, go ahead and cd into your new API
directory.

cd lets-get-lunch-api

4.5 Creating config files


If you view the repository7 of the code you just cloned or view the README.md
file in your lets-get-lunch-api directory, you’ll see a section about setting
up two configuration files with an associated structure and details we need to
provide it.
First, create those two files.

touch src/test-config.json && touch src/dev-config.json

If you’re on Windows, the command for touch is a bit different.

7
https://github.com/theangulartutorial/lets-get-lunch-api
34 CHAPTER 4. API SETUP AND INSTALLATION

type NUL > src/test-config.json && type NUL > src/dev-config.json

Open up src/test-config.json and add the following object.

Listing 4.1
src/test-config.json
{
"port": 8080,
"bodyLimit": "100kb",
"db": "mongodb://localhost:27017/name-of-db",
"secret": "supersecretkey",
"zomato": "zomatokey"
}

You can leave the port and bodyLimit property as is. Port specifies the port
our API should use when it’s run and bodyLimit specifies the maximum body
size of incoming requests.
The db property will need to be modified slightly. You’ll need to remove
name-of-db and update it to the name you want for your database. Let’s
use lgl-api-test.
The secret property is the secret key that the API will use when generating
JSON Web Tokens (JWT) which our application will be using for authenticat-
ing users, signing new tokens and verifying existing ones. Feel free to add
whatever string here you’d like or to leave it as is.

Box 4.1.

Since we’re just learning, that secret isn’t a huge deal for us right now. But if
we were releasing a product to the world with real users we’d want to keep that
secret, well, a secret. If a malicious third party gained access to that secret key,
they would be able to make requests as if they were someone else. This is why the
secret and the configuration files aren’t a part of the public code repository itself
and must be manually generated.
4.6. TEST CONFIG VS. DEV CONFIG 35

Finally, you’ll want to update zomato to the API key Zomato provided to you
earlier when you created an account. With all of that, your config file should
now look similar to this.

Listing 4.2
src/test-config.json
{
"port": 8080,
"bodyLimit": "100kb",
"db": "mongodb://localhost:27017/lgl-api-test",
"secret": "sup3rs3c123tk3y",
"zomato": "zomato12345"
}

Save that file and open up your other config file, src/dev-config.json.
The values will be almost exactly the same as your src/test-config.json
with one update to the database name.

Listing 4.3
src/dev-config.json
{
"port": 8080,
"bodyLimit": "100kb",
"db": "mongodb://localhost:27017/lgl-api",
"secret": "sup3rs3c123tk3y",
"zomato": "zomato12345"
}

Here we’ve updated the db property be removing the -test that was in our
test-config.json file. Once again, set your API key as the value for the
zomato property.

4.6 Test config vs. dev config


So what’s the need for these two nearly identical configuration files? These two
configuration files will provide us a test and a development environment.
36 CHAPTER 4. API SETUP AND INSTALLATION

Since testing is such a heavy emphasis within this book, we’ll be using an
entirely separate database (lgl-api-test) when we run our tests that’s wiped
clean between each test. This will be known as our testing environment. This
will help us verify that various features of our application work at the most
basic level.
Our other database (lgl-api) is the database we’ll be using as a developer
while we’re developing our application. This will be known as our development
environment. Both of these environments are local to our own computer.
The final database which we’ll be setting up much later is what’s known as a
production database. This is what real users who are accessing our application
through the Internet would be using - whether they realize it or not.
This split is one you could expect to see working at any tech company. Some
may have more environments while some may have less. The key takeaway
is that our test and development environments are local to our own computer.
They allow us to use our application without polluting the database that real
users are interacting with.

4.7 Running the API


Now that we have our configuration files set, we can install our API’s depen-
dencies. First, use nvm to install version 7.6 of Node.js since that’s what is
needed for the API.

nvm install 7.6.0

Then set your current version of Node.js to this newly installed version.

nvm use

Now in the root of your project directory (lets-get-lunch-api) run the


following command to install the project’s dependencies.
4.7. RUNNING THE API 37

npm install

Once that has finished, we’ll need to build the project files compiling all of the
TypeScript files into JavaScript files that Node.js can run. First, you’ll need to
install gulp8 .

npm install gulp-cli@1.4.0 -g

Then you can run the gulp command to build the project’s JavaScript files.

gulp

8
https://gulpjs.com/
38 CHAPTER 4. API SETUP AND INSTALLATION

Figure 4.6: Running gulp

With that, you can now list your project’s files:

ls -l

On Windows, the command for this is:

dir

A new /built directory should be visible which contains the JavaScript files
necessary for Node.js to run the project.
4.7. RUNNING THE API 39

If MongoDB isn’t already running, open another terminal window (Cmd + T


or Ctrl + Shift + T to create another tab) and run the command listed
earlier in Section 4.1.3 for your OS to start MongoDB.
Back in your API’s directory run:

npm run api-test

After a few seconds you should see a ready message similar to this:

Ready on port 8080


Ready on DB mongodb://localhost:27017/lgl-api-test
40 CHAPTER 4. API SETUP AND INSTALLATION

Figure 4.7: Running the API

Congratulations! You’ve successfully set up the API and have verified that it’s
running.
To give you some perspective, this is a process that has sometimes taken me
days at real companies so don’t sell yourself short. It’s the closest thing to a
real-world, paid version of syllabus week9 .

9
https://www.urbandictionary.com/define.php?term=syllabus%20week
4.8. VERIFYING THE DATABASE 41

Box 4.2.

It’s worth noting that you’ll need to run nvm use every time you’re in the API
directory before running any commands like gulp or npm run api-test unless
you haven’t closed your terminal window since the last time you ran it. Without
setting your Node version using nvm use you may run into some issues since
later versions of Node may have introduced some breaking changes.
If you want to avoid running nvm use every time, you can set 7.6 as your default
Node.js version using nvm by running nvm alias default 7.6.
And if you’re ever curious about what version of Node you’re currently using just
run nvm current.

4.8 Verifying the database

Now that our API is running, let’s verify that our database has been created.
Open Robo 3T and a window should open with an empty list of connections.
At the top, click “Create”.
Enter a name for your connection, something like angular-book, and verify
the address is set to localhost and the port number next to it is 27017.
42 CHAPTER 4. API SETUP AND INSTALLATION

Figure 4.8: Robo3T Connection Settings

Click “Save”, select your new connection, and click “Connect”.


4.8. VERIFYING THE DATABASE 43

Figure 4.9: Robo3T Connections

In the list of connections on the left-hand side you should see a database with
the name you specified earlier in src/test-config.json. If you used the
one in the example you should see lgl-api-test.
Note: Due to recent changes in Robo3T the database name may not appear
yet. That’s okay. In Section 9.2 you’ll learn how to read API documentation
and create a user using Postman. Once that user is in the database you’ll then
be able to verify the database exists.
44 CHAPTER 4. API SETUP AND INSTALLATION

Figure 4.10: Robo3T Test Database

Box 4.3. Robo3T on Windows

If you’re using Windows and you aren’t seeing the database, that’s expected. For
whatever reason, Robo3T doesn’t show the database in this list until a record has
actually been created in the database which we’ll do in Section 9.2. As long as
MongoDB ran successfully in Section 4.1.3 everything should be fine. We’ll get
back to verifying this later.

Go back to your terminal window that’s running the API and enter Ctrl-C to
stop the API. Now run npm run api-dev to run the API using our develop-
ment environment.
4.8. VERIFYING THE DATABASE 45

Go back to Robo 3T, right click angular-book in the left-hand sidebar, click
“refresh”, and you should see your second development database lgl-api
displayed in the list.

Figure 4.11: Robo3T Development Database

Note: Once again, due to recent changes in Robo3T the database name may
not appear yet. That’s okay. In Section 9.2 you’ll learn how to read API docu-
mentation and create a user using Postman. Once that user is in the database
you’ll then be able to verify the database exists.
46 CHAPTER 4. API SETUP AND INSTALLATION

4.9 Add to GitHub

Now we’re ready to add our API to GitHub. First, go back to the home page of
GitHub and click “Start a project”.

Figure 4.12: Start a GitHub project

Then you’ll be asked to provide a name for your repository (project). Your
directory name (lets-get-lunch-api) and repository name don’t have to
be the same but it may help so that you can keep the two aligned. Then, click
“Create repository”.
4.9. ADD TO GITHUB 47

Figure 4.13: Creating a GitHub Repository

Since the API was cloned from an existing repository, you’ll need to update its
remote repository URL. Run the following command:

git remote set-url origin \


https://github.com/your-username-here/lets-get-lunch-api.git

You should see your updated URL by running:

git remote -v

From there, you can push your API up to your GitHub repository by running:
48 CHAPTER 4. API SETUP AND INSTALLATION

git push origin master

4.10 Conclusion
The API and our two databases have now been setup and we’ve pushed them
to a GitHub repository. We’ve now finished the grunt work of setting up our
backend so it’s time to move on to Angular!
Chapter 5

Introduction to Angular

5.1 The approach


Before we get into some of the specifics of Angular, its features, and how
everything fits together to create a web application I want to take this chapter
to create a very simple Angular application.
Rather than explain a bunch of features and components of Angular that will
feel very abstract to you at this time, I think it’ll be helpful to get your feet wet
with Angular so you at least have some familiarity with the framework once
we start getting into the details.
From there, we’ll look at Angular within the bigger picture to see how these
pieces fit together to build an application with Angular.

5.2 Install the CLI


The first thing we’ll need to do is install the Angular CLI1 . The CLI is a huge
addition that wasn’t available previously with AngularJS. The CLI creates a
1
https://cli.angular.io/

49
50 CHAPTER 5. INTRODUCTION TO ANGULAR

basic, working application for us right out of the box following Angular’s own
best practices. As a developer, this saves us a lot of setup time.
Install the CLI by running:

npm install -g @angular/cli@8.1.2

Next, check that you aren’t in the API directory (lets-get-lunch-api). If


you are, return to the previous directory.

cd ..

That should get you back into the book-code directory. From there, generate
a new Angular app by running:

ng new exploring-angular
5.2. INSTALL THE CLI 51

Figure 5.1: Creating the Exploring Angular app

When you run the ng new command you should see some command prompts
asking you “Would you like to add Angular routing?” and “Which stylesheet
format would you like to use?”. Just hit the “Enter” key twice to select the
default values for both prompts (No and CSS).
52 CHAPTER 5. INTRODUCTION TO ANGULAR

Figure 5.2: Angular CLI Prompts

Once that has finished, move into your new directory and run the application.

cd exploring-angular
ng serve

Once you see webpack: Compiled successfully you can open a browser
window and direct your browser to http://localhost:4200. You should see an
Angular logo with a few links listed below it.
5.2. INSTALL THE CLI 53

Figure 5.3: Running an Angular app


54 CHAPTER 5. INTRODUCTION TO ANGULAR

Figure 5.4: The default Angular app

5.3 Exploring the code


Let’s now take a look at some of the files within our new application. Open the
project in a text editor and you should see a root file structure similar to the one
below:

Listing 5.1
| exploring-angular
| e2e
| src
- .editorconfig
- .gitignore
- angular.json
- package-lock.json
5.4. ROOT FOLDER 55

- package.json
- README.md
- tsconfig.json
- tslint.json

5.4 Root folder


Most of these root files are configuration files that you won’t be interacting
with much, if at all. We’ll be modifying a few of them later on in the book but
for now just leave them as is. (More detailed information on these files can be
found online in the Angular QuickStart guide under “Project file review”2 .)
In addition to the two configuration files are two directories: e2e and src.
e2e is where the provided end-to-end tests (using the Protractor framework)
live. We’ll be using Cypress in this book so again, feel free to ignore this for
now. The other directory, src, is where our Angular application lives.

5.5 Src folder


Within src are some additional configuration files and some other project setup
files we won’t be touching much. Like I said earlier, the CLI takes care a lot
of the grunt work for us so we can focus on writing code. Two files here worth
looking at are index.html and styles.css.
If you open index.html you’ll probably notice it’s relatively empty. It doesn’t
contain anything that you were seeing earlier in your browser. This is the sin-
gle page that Angular uses to inject the various parts of the application into
when the user visits your site. Once the application has been built to view in a
browser, it’s the page that contains all of the styles and scripts necessary to run
your application. Once again, the CLI’s magic at work.
2
https://angular.io/guide/quickstart#project-file-review
56 CHAPTER 5. INTRODUCTION TO ANGULAR

While we’re on the subject of styles, styles.css is our first CSS stylesheet.
Open that file and you’ll see a comment at the top mentioning it’s the location
for global styles. If you want a style to apply across your entire application,
this is where it goes. We’ll see more stylesheets shortly.

5.6 AppComponent
Within the src folder you’ll see another folder named app. This is where the
real code for our application lives. The first files we want to look at are all of
the app.component files followed with .css, .html, .spec.ts, and .ts.
The first file, app.component.css, is the stylesheet that’s specific to our
component. At the moment it’s empty. Unlike the styles.css stylesheet we
saw earlier, the styles within app.component.css will only affect this single
component. We’ll see how this relation is made shortly.
Next we have app.component.html. This is the HTML template for our
component. Since this is the only component in our application at the moment,
this template contains all of the HTML we saw when we opened our app in our
browser earlier.
Next is app.component.spec.ts. This is the test file containing the unit
tests for this component. This file may look overwhelming at the moment but
it’s one we’ll become much more familiar with throughout this book.
Finally, we have app.component.ts. Within this file you’ll see an import of
the Component symbol from Angular’s core library at the very top. This is a
part of every Angular component and much of what we see in this file will be
automatically generated for us when we use the CLI to create new components
for us.
Below that you’ll see a @Component decorator function that specifies the meta-
data for this component. The selector property defines the name of the
custom HTML element we’re creating that we use in our HTML to use this
component. Take a glance at index.html again and you’ll see <app-root>-
5.7. APPMODULE 57

</app-root> within the <body> tag. Next we have our templateUrl and
styleUrls. These two correspond to app.component.html and
app.component.css which we just covered. This decorator function is what
glues all of these files together.
Finally, there’s the name of our exported class at the bottom: AppComponent.
In this class is a property title with a value exploring-angular. If you go
back to
app.component.html you’ll notice {{ title }} on line 4. The double
curly braces is what’s known as “interpolation”. Here we’re using it to display
the component property title. Update the value of title in app.component-
.ts to My Angular App, save, and go back to your browser to see your up-
dated value.
These three files (excluding app.component.spec.ts) come together to
create our AppComponent.

5.7 AppModule
So now the question is, how does our application know to use AppComponent?
The mere existence of the files in our project isn’t enough. This is where
AppModule comes in.
Open up app.module.ts and inside you’ll see an import statement for our
AppComponent. If AppComponent was the glue for our html, css, and com-
ponent code, AppModule is what glues our entire application together.
Along with the import for AppComponent you’ll see import statements for
BrowserModule and NgModule. BrowserModule is a module provided to
us by Angular for running our app in a browser.

Box 5.1.
58 CHAPTER 5. INTRODUCTION TO ANGULAR

Modules like BrowserModule are one of the many modules provided to us that
are truly the core of Angular - the “level creator building blocks” for the web. Later
on we’ll be importing modules for forms, routing, and http requests that save us
the time from having to write these features ourselves. These, and their associated
counterparts found in other frameworks, are the individual tools provided to us in
the technical toolbox known as a front-end framework.

The import for NgModule and its @NgModule decorator is similar to @Component
which we saw earlier in app.component.ts.

5.8 NgModule
NgModules, or modules, are a way for our application to organize itself. They
also provide us a way to extend the capabilities of our application to include
external libraries that aren’t provided to us by Angular itself.
NgModules combine the various parts of Angular such as components, direc-
tives, pipes, and services into blocks of functionality. (We’ve already seen
our first component. We’ll get to directives, pipes, and services shortly. The
takeaway here is that NgModules aggregate these various parts into one larger,
related unit known as a module.)
Every Angular app must have at least one module known as the root module
which we see in app.module.ts. This module is “bootstrapped” to launch
our application. Similar to @Component we see metadata for our @NgModule
as well.
The first is declarations. This declares the components, directives, and
pipes that belong to the module. In our case, we’re declaring AppComponent
as our first and only declaration. This is what allows us to view AppComponent
in the browser. Remove this line and our page would turn into a blank screen
along with some helpful errors printed to the console by Angular telling us
5.8. NGMODULE 59

AppComponent isn’t a part of any NgModule.


Next is imports. imports is what imports other modules into our appli-
cation that this module needs to function. In our case we’ve imported the
BrowserModule so that our app can run in a browser.
Below that is providers. This is where we list the services our app needs.
It’s empty for now but we’ll be creating one shortly.
Finally we have bootstrap. When Angular launches our application it “boot-
straps” our AppModule and in the process it creates the components (almost
always one) listed in bootstrap inserting each one into the browser DOM.
Here we see AppComponent listed as the one component in the bootstrap
array.

Box 5.2.

This “bootstrapping” within AppModule with AppComponent as the sole compo-


nent in bootstrap serves as our application’s “shell”. AppModule is the aggre-
gation of various components, modules, and services that come together to create
our application. To view our application in the browser, we need at least one “boot-
strapped” component which is AppComponent. Since all of this was generated for
us by the CLI, this “shell” is one you will see across all Angular projects.
This may seem a bit abstract at the moment and that’s fine. The key takeaway is
that, going forward, AppComponent won’t be doing much. It’s HTML will serve
as the shell for our entire application displaying whatever view it is that the user
is seeing. We’ll solidify this point in Chapter 7 when we setup routing and create
our first component.

If you’d like to see where our exported class AppModule is used, open src/-
main.ts and you’ll see our application’s bootstrapping in action. Near the
end of the file there’s a .bootstrapModule() call and our AppModule is
provided. Don’t worry too much about this file though since we won’t be going
60 CHAPTER 5. INTRODUCTION TO ANGULAR

back to it often. It’s just a small detail to show how AppModule is being used
in one of the files generated for us by the CLI.

5.9 Services
Now that we’ve seen how our AppComponent fits into AppModule it’s time to
go back to the other features of Angular (services, directives, and pipes) which
can, along with components, be aggregated into modules.
In Angular, we use services to manage the data that’s used within an applica-
tion. Examples would include a service to manage users. This service could
provide us a way to create new users, retrieve additional details about a user, the
ability to update details about the user, or even delete the user entirely. These
services can then be used throughout our application within any component or
module that needs to leverage its functionality. Within this book, we’ll be using
services primarily to make HTTP requests to an API to either save, update, or
retrieve data from a database. For now, we’ll start with a simple service that
returns some hard-coded data.
First we’ll use the CLI to generate a service for us.

ng g service ng-features

This creates two new files for us: our service (ng-features.service.ts)
and its associated test file (ng-features.service.spec.ts). Take a look
inside ng-features.service.ts and you’ll see a providedIn property
set to root inside the service’s @Injectable decorator.

Listing 5.2
src/app/ng-features.service.ts
@Injectable({
providedIn: 'root'
})
5.9. SERVICES 61

export class NgFeaturesService {

constructor() { }
}

This providedIn property is what tells Angular to “provide” this service in


the root injector meaning this service will be available throughout our entire
application.
Inside ng-features.service.ts just below our constructor add a get-
Features method that returns an array of objects containing the four features
we’re covering in this chapter.

Listing 5.3
src/app/ng-features.service.ts
constructor() { }

getFeatures() {
return [
{ 'name': 'Components' },
{ 'name': 'Services' },
{ 'name': 'Directives' },
{ 'name': 'Pipes' }
];
}

We now have our first basic service set up. Since its providedIn property is
set to root other application components like AppComponent can use it.
Open up app.component.ts and add an import statement for our service.

Listing 5.4
src/app/app.component.ts
import { NgFeaturesService } from './ng-features.service';

Then declare a property in our class named features of type Array<object>.


We’ll set this to the values (an array of objects) we receive when we call our
62 CHAPTER 5. INTRODUCTION TO ANGULAR

service. Then add a private ngFeatures parameter of type NgFeatures-


Service to our constructor.

Listing 5.5
src/app/app.component.ts
export class AppComponent {
title = 'My Angular App';
features: Array<object>;

constructor(private ngFeatures: NgFeaturesService) { }


}

Box 5.3. Dependency Injection

The parameter in our constructor defines a private ngFeatures property and


identifies it as a NgFeaturesService injection site. When Angular is cre-
ating our AppComponent, Angular’s Dependency Injection system sets our
ngFeatures parameter to the instance of the NgFeaturesService we set in
app.module.ts.
Our constructor is what triggers Angular’s dependency injection system. It’s basi-
cally a component’s way of saying, “Here’s a service or list of services I need to
work.” From there, Angular uses dependency injection to provide our component
with the service it needs.

The only thing left is to call our service and set the return value to our local
features property. We could technically make this call within our construc-
tor but it’s not a best practice. Our constructor shouldn’t do anything except
dependency injection.
Instead we’re going to make this call in one of Angular’s lifecycle hooks ngOn-
Init. This is a hook that Angular itself will call, a single time, after it has
constructed our component.
5.10. DIRECTIVES 63

To use this lifecycle hook, we’ll first need to import it. Add OnInit after the
Component import.

Listing 5.6
src/app/app.component.ts
import { Component, OnInit } from '@angular/core';

Then we need to update our AppComponent class to implement this lifecycle


hook.

Listing 5.7
src/app/app.component.ts
export class AppComponent implements OnInit {
...
}

Now we can add the ngOnInit method, call our service’s getFeatures
method, and set its return value to our local features property.

Listing 5.8
src/app/app.component.ts
export class AppComponent implements OnInit {
title = 'My Angular App';
features: Array<object>;

constructor(private ngFeatures: NgFeaturesService) { }

ngOnInit() {
this.features = this.ngFeatures.getFeatures();
}
}

5.10 Directives
Similar to title our features property is ready to be interpolated to our
view. But unlike title which was a simple string features is an array of
64 CHAPTER 5. INTRODUCTION TO ANGULAR

objects. This is where we use our first built-in directive: ngFor. You can see a
complete list of Angular’s built-in directives here3 .
ngFor is what’s known as a structural directive. Structural directives reshape
the DOM’s structure usually adding, removing, or modifying elements. They
can be identified within HTML with a preceded asterisk (*) to the directive’s
name.
At the bottom of app.component.html, we can use this structural directive
for our array of features interpolating the feature name to the view.

Listing 5.9
src/app/app.component.html
<h2>Angular features:</h2>
<ul>
<li *ngFor="let feature of features">{{feature.name}}</li>
</ul>

We’ve created an unordered list and applied the *ngFor directive to an individ-
ual list element. We apply the directive to the element we want to be duplicated.
The let keyword declares a template variable that we reference within this
template. Here we’re looping through the features (declared in our compo-
nent) and setting each element within the array to the feature variable in our
template. We then use our template variable feature to interpolate the name
of our feature using double curly braces.
Check your browser again and you should see a new list at the bottom of the
page containing the features provided to us in our service.

5.11 Pipes
Our last remaining feature is what’s known as a pipe. Pipes are a way to for-
mat strings, currencies, dates, and other display data. Angular comes with a
3
https://angular.io/guide/template-syntax#built-in-directives
5.12. CONCLUSION 65

few built-in pipes and we’ll use our first one here to illustrate the concept. A
complete list of Angular’s built-in pipes can be found here4 .
Within our template where we interpolated the feature name, add a pipe opera-
tor, |, followed by the name of the built-in pipe we want to use, uppercase.

Listing 5.10
src/app/app.component.html
<li *ngFor="let feature of features">{{feature.name | uppercase}}

Go back to your browser window and you should see the feature names listed in
capital letters. The word uppercase to the right of the pipe operator activated
Angular’s UppercasePipe5 .

5.12 Conclusion
In this chapter we saw the structure of an Angular application and the building
blocks of Angular that we use to create components and modules. This is all
we’ll be doing within the exploring-angular application. You can either
leave the project as is if you want it for reference or delete it. If you’d like to
delete the application, first move up a directory.

cd ..

Then delete the exploring-angular project.

rm -rf exploring-angular

4
https://angular.io/guide/pipes#built-in-pipes
5
https://angular.io/api/common/UpperCasePipe
66 CHAPTER 5. INTRODUCTION TO ANGULAR
Chapter 6

How Angular Works


In Chapter 2 we discussed how web applications worked specifically within the
context of a client-server model. Now it’s time to take a closer look at Angular
to see how all of its various features come together to create the client-side of
the client-server model.

6.1 Refresher

In the previous chapter we started by creating a simple service. We con-


sumed this service from our single component and used that component to
manage a template displaying the data that was returned from our service. We
then saw how our AppComponent was bootstrapped through the root module
AppModule in app.module.ts.
This was a simple example. We were becoming familiar with the various parts
of Angular at the most basic level, even restricting ourselves to a single page.
Most applications you use span across multiple different views, making HTTP
requests to servers, and respond to a user as they interact with our application.

67
68 CHAPTER 6. HOW ANGULAR WORKS

6.2 Feature modules

As we start working on the various features throughout this book we’re go-
ing to be creating additional modules referred to as “feature modules”. These
feature modules will contain their own *.module.ts file along with all the
*.html, *.css, and *.component.ts files that come together to create a
component. If the component requires services, directives, or pipes, they will
will be contained in the feature module as well.

Figure 6.1: Feature Modules


6.3. ROUTING AND MODULES 69

These various feature modules come together to create our application. So our
application which is boostrapped by the root module (AppModule), is com-
posed of even more modules (feature modules) that provide the user the various
functionality of our application in a browser.

6.3 Routing and modules

In the app we made earlier, we restricted ourselves to a single page. We hadn’t


implemented routing using Angular’s router which allows the user to navigate
from one view to another.
Let’s imagine a user comes to our app and wants to create an account (one of
the first features we’ll be building). They navigate to /signup. Navigating
to this view triggers Angular to present one of our feature modules associated
with the route /signup.
As we discussed earlier this feature module will contain its own HTML tem-
plate, stylesheet, and component class to manage the component’s functional-
ity. In this case it will also have a service that will interact with our API to
create a user for us. More importantly, our feature module will also be com-
posed of other modules which may be modules we create ourselves or modules
provided to us by Angular. Since our /signup page will contain a form for
the user to complete we’ll be leveraging Angular’s built-in FormsModule.
This is what makes modules so powerful. Code is separated into modules
to handle specific (potentially reusable) functionality. Then we use modules,
whether they’re created by us, a third-party, or by Angular itself, to create even
more modules (“feature modules”) to build our application.
70 CHAPTER 6. HOW ANGULAR WORKS

6.4 Conclusion
We’ve now seen at a high level how our application will be leveraging feature
modules and how modules can also be composed of other modules. If this
seems like inception at the moment, that’s perfectly normal. These concepts
will hopefully start to make more sense once we begin creating some of our
features.
We also briefly introduced the concept of routing and the ability for users to
view multiple pages within our application. Since our application will be lever-
aging many routes, let’s move on to the first part of our real application starting
with implementing the router.
Chapter 7

Home Page and an


Introduction to Routing
The first thing we’re going to do within our application is create our first com-
ponent. This component will be rather basic since it’s just the home page for
our application. More importantly, we’re going to set up the scaffolding for the
routing our application is going to be relying on going forward.

7.1 Create the app


Since our previous app was just an example to illustrate concepts we’re going
to create a new app that we’ll be using for the remainder of this book. First,
verify that you’re in the book-code directory and not within the exploring-
-angular project if you decided to keep it.
Then create the new app.

ng new lets-get-lunch --routing --style=css

At this point, your book-code directory should look like this.

71
72 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING

Listing 7.1
| book-code
| exploring-angular (unless deleted)
| lets-get-lunch
| lets-get-lunch-api

We added the --routing flag to here which tells Angular to set up a routing
module for us. The scaffolding for our app’s routing has now been created in
src/app/app-routing.module.ts. We’ve also added the --style flag
setting its value to css to specify our stylesheet format. This is simply a way
to set default values for the CLI prompts we saw earlier in Section 5.2 of Chap-
ter 5.
In the file we see a variable routes set to an empty array. This is where we’ll
configure our routes. Below that is an @NgModule decorator which imports the
RouterModule. Here it has a call to .forRoot() with our routes variable
being passed in as an argument. forRoot is what configures our application’s
router using our application’s routes in routes that we define.
Below that is the exports metadata with RouterModule set as its export.
This exposes our routing module so that it can be imported in AppModule.
As a result of generating our app with the --routing flag you can see that
our AppRoutingModule has already been imported and added to imports in
AppModule.

7.2 HomeComponent
With our routing module set up it’s time to set up our first route and direct it to
our home component. First, change directories into the new lets-get-lunch
directory.
7.2. HOMECOMPONENT 73

cd lets-get-lunch

Then create the component.

ng g component home

Next we’ll update the template file to be a bit more descriptive.

Listing 7.2
src/app/home/home.component.html
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="jumbotron">
<h1>Let's Get Lunch</h1>
<p>Coordinate and find local lunch spots with your coworkers.</p>
</div>
</div>
</div>
</div>

With our HomeComponent created and our template file updated, we can now
configure this component within our router.
In app-routing.module.ts first import our component and then add our
new route object to the routes array.

Listing 7.3
src/app/app-routing.module.ts
import { HomeComponent } from './home/home.component';

const routes: Routes = [


{ path: '', component: HomeComponent }
];

Here we’ve specified an empty path since we want our home page to display
at the root path of our application. We then set its component property to our
74 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING

imported HomeComponent. This tells the router, “When a user is viewing the
root path of our application, use this component.” Run ng serve and you
should see our markup at the bottom of the page.

Figure 7.1: Updated home page

Box 7.1.

If you want the CLI to automatically open a browser window for you rather than
navigating to http://localhost:4200 manually you can run ng serve -o.

But how did our application know where to inject our component’s template?
Open app.component.html and at the bottom you’ll see a new directive
7.2. HOMECOMPONENT 75

<router-outlet>. This directive (automatically added by the CLI earlier


when we created our app using --routing) tells our router where to display
our view. This is why we referred to AppComponent as our application’s
shell in Section 5.8 of Chapter 5. All of the routed features we create going
forward will be viewable because of the <router-outlet> directive in the
AppComponent template.
Since our home page is now working, delete all of the old markup automati-
cally created by the CLI in app.component.html so that it only contains the
router-outlet directive.

Listing 7.4
src/app/app.component.html
<router-outlet></router-outlet>

Take another look at your browser and you should see the home page containing
only the markup that’s contained in home/home.component.html.
76 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING

Figure 7.2: Home Page

There’s an issue here because none of the Bootstrap styles we’re using in the
template are working. Let’s install and configure Bootstrap to fix this.

7.3 Adding Bootstrap


First we’ll need to install Bootstrap via npm.

npm install bootstrap@3.3.7 --save

Bootstrap requires jQuery so we’ll need to install that as well.


7.3. ADDING BOOTSTRAP 77

npm install jquery --save

Now we need to configure the Angular CLI so that it can use Bootstrap when
it’s serving our application. In angular.json you should see a "styles"
property set to an array containing "styles.css". Add a path to Bootstrap’s
CSS just above it.

Listing 7.5
angular.json
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"styles.css"
]

Below that should be a "scripts" property set to an empty array. Add a path
to jQuery and Bootstrap’s JS.

Listing 7.6
angular.json
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/bootstrap/dist/js/bootstrap.min.js"
]

To get these changes to take effect, we’ll need to restart the app so Angular’s
CLI can use our latest updates to its configuration. If your application is already
running, type Ctrl + C to stop the application. Then run it again.

ng serve -o

Now you should see our styled home page using our very first component and
our very first route.
78 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING

Figure 7.3: Home Page

7.4 Updating the AppComponent test


We haven’t yet gotten to testing our app but we’ll get to that shortly. For now,
we just need to update the existing tests that Angular generated for us when we
created our app. In another terminal window run the command ng test.

Box 7.2.

It’s fine to leave your existing terminal window with ng serve running. As you
make changes to your app ng serve and ng test will both automatically update
to reflect your latest changes and the two of these running at the same time don’t
conflict with one another.
7.4. UPDATING THE APPCOMPONENT TEST 79

The one scenario where you may run into issues is when you create new files (such
as components, services, or modules) using the CLI. After running commands like
these it’s worth restarting ng serve and ng test to ensure they’re picking up
the latest files that have been added.

Figure 7.4: Failing AppComponent Test

The result of ng test should log errors stating a broken test with a message
like, "AppComponent should render title in a h1 tag FAILED".
This test is failing because we removed the HTML code that was previously
80 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING

there replacing it with <router-outlet>. Open app.component.spec.ts


and remove the bottom two tests.

Listing 7.7
src/app/app.component.spec.ts
// Delete these
it(`should have as title 'lets-get-lunch'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('lets-get-lunch');
}));

it('should render title in a h1 tag', async(() => {


const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent)
.toContain('Welcome to lets-get-lunch!');
}));

While we’re at it we can also remove title from app.component.ts since


it’s no longer being used leaving us with an empty AppComponent class.

Listing 7.8
src/app/app.component.ts
export class AppComponent { }

Now all of your tests should be passing with a message like, "Executed 2
of 2 SUCCESS".
7.5. ADD TO GITHUB 81

Figure 7.5: Passing AppComponent Test

7.5 Add to GitHub


Just as we did in Section 4.9 of Chapter 4, create a new repository on GitHub
for our Angular project with a name like lets-get-lunch-fe. (You can
create a new repository by clicking plus sign “+” next to your avatar image and
selecting “New repository”.)
From there add your repository URL to your project.
82 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING

git remote add origin https://github.com/your-username-here/lets-get-lunch-fe.git

Add your latest changes along with a commit message.

git add .
git commit -m "Add home component and routing"

Then push your changes to GitHub.

git push origin master

7.6 Conclusion
In this chapter we set up the Angular project that we’ll be building on for the
remainder of this book. We created our first component, HomeComponent, and
configured its routing which displayed its view using the <router-outlet>
directive in AppComponent, our application’s shell. We then updated config-
uration files to include Bootstrap and jQuery so that our template in Home-
Component was styled correctly.
In the next chapter, we’ll introduce some concepts and the tools we’ll be using
for automated testing.
Chapter 8

Introduction to Testing
Before we get into creating and testing our features it’s worth taking some time
to discuss the tools and languages we’ll be using to test our code along with
the different types of tests. We’ll wrap up with basic examples that show us the
thought process to keep in mind while coding which is one of the most difficult
aspects for people to pick up when they first begin testing.

8.1 Karma
Karma is a tool that’s a direct product of the Angular team. It’s a tool that’s
used entirely for running our application’s test code. When we ran the ng
test command earlier, the Angular CLI used Karma to run our test in what’s
known as a “headless browser” which is a browser without a graphical user
interface (GUI).
The primary advantage of a headless browser when testing code is performance
since headless browsers avoid draw operations which handle rendering of the
UI on the screen. Just like a regular browser, a headless browser still under-
stands HTML, CSS, and JavaScript. As a result, a headless browser allows
our tests to run in a browser environment faster than it would in a traditional
browser while still providing us the ability to verify our application is behaving

83
84 CHAPTER 8. INTRODUCTION TO TESTING

as we expect it to.
Aside from the occasional configuration you won’t be interacting directly with
Karma other than through the ng test command.

8.2 Jasmine
Jasmine is a behavior-driven development framework created for testing JavaScript.
Jasmine is included within Angular app’s so it’s the testing framework we’ll be
using within this book.
A few other frameworks you may hear thrown around that are near equivalents
to Jasmine would be Mocha, AVA, and Jest.

8.3 Unit testing


When we run ng test, Karma is started and begins to run what are known as
“unit tests”. Unit tests are tests that test individual units of code verifying that
they behave as expected. The goal of a unit test is to test our units of code in
isolation without interacting with an API or even other code we have written.
Imagine we had a function for capitalizing the first letter of a string.

function capitalize(word) {
return word.charAt(0).toUpperCase() + word.slice(1);
}

Here we would use a “unit test” to verify that when our capitalize function is
called with a string that the return value is indeed the exact same string with the
first letter capitalized. We don’t check to see if the word is actually capitalized
by opening a browser. We’re testing the code, in isolation, as its own unit.
8.4. INTEGRATION TESTING 85

8.4 Integration testing

Integration tests are a bit more difficult to explain and the term “integration test-
ing” is often used interchangeably with end-to-end testing which we’ll cover
next. Even within the testing section1 of the Angular documentation, the term
“integration testing” is briefly mentioned but rarely addressed afterwards.
While unit tests are for individual units of code at the smallest level, integration
tests are tests that integrate with some other parts of our application. In Angular
applications, these tests are also written using the Jasmine framework which are
run by Karma when we use the ng test command provided by the Angular
CLI.
Using our previous example with a function that capitalized a word, in a unit
test we would test this function in isolation passing it a string to verify its
expected behavior. In an integration test, we would test the integration of this
function and the other parts of our application. We may have a function that first
makes a request to an API and once it receives a response from the API, then
calls the capitalize function to capitalize certain words which are displayed
to the user. An integration test would test all of these various parts together.
Within Angular applications, the distinction between unit tests and integration
tests becomes even blurrier considering the two types of tests are typically writ-
ten within the exact same file. The key distinction to remember here is a unit
test is isolated from other parts of our application while integration tests inte-
grate with other parts of our application. If this seems a bit confusing at the
moment, don’t worry. We’ll get into more of the details later once we begin
creating features and writing tests.

1
https://angular.io/guide/testing#testing
86 CHAPTER 8. INTRODUCTION TO TESTING

8.5 End-to-end (E2E) testing


Next we have what is known as “end-to-end” testing. End-to-end testing is a
form of testing that tests our application behaves as we expect it to by interact-
ing with our application as a user would, in a browser.
If unit tests various bits of code in isolation, end-to-end tests test our code at
a larger scale to ensure the various parts come together to create a working
application.
Imagine a user attempting to create an account in our application. If they enter
a username and password we may redirect them to another part of our appli-
cation. If they leave one of the inputs blank or provide invalid values, we may
display an error message asking them to make changes.
End-to-end tests actually open a browser, type in values into the user signup
form, and click the submit button to create an account. The tests then verify
whether the redirect happens or if the error message is shown depending on the
specific scenario it is that we’re testing.
End-to-end tests save us the effort of having to open a browser and go through
this process ourselves. Instead, we can write code to do it for us. It’s a process
that will really feel like magic once we get there.
The tool we’ll be using for this is known as Cypress2 .

Box 8.1.

If you’re familiar at all with the Angular world (AngularJS or Angular) you may
have heard of another tool Protractor which is also maintained by the Angular
team. We touched on this earlier in Section 5.4 when we were looking at the
directories created for us by the Angular CLI (the e2e directory that Protractor
uses).

2
https://www.cypress.io/
8.6. THE TESTING PYRAMID 87

Protractor is a tool similar to Cypress. They’re both used for end-to-end tests.
However, after much use I’ve found Cypress to be far easier to use and much more
reliable for end-to-end tests. End-to-end testing in general can be very finnicky
and in my experience Cypress reduces a lot of that.

8.6 The testing pyramid


Within the world of automated testing is a metaphor known as the “Testing
Pyramid”. There are various versions of this pyramid which you can find by
doing a Google image search for “testing pyramid”.
The point of the test pyramid is that it gives us an idea for the distribution of
our tests between the testing groups: unit, integration, and E2E.

Figure 8.1: Testing Pyramid

As shown in the figure above, as you get higher in the pyramid the fewer tests
you have for the respective level. This is due to two factors: speed and relia-
bility. Unit tests provide us our fastest and most reliable tests while end-to-end
88 CHAPTER 8. INTRODUCTION TO TESTING

are the slowest and most prone to failure. While end-to-end tests are the most
brittle of the three, it isn’t entirely due to the testing framework that’s being
used for end-to-end testing. There are various quirks across browsers, anima-
tions, timing issues, and many others that can lead to end-to-end tests failing
even if the feature may be functional.
As a result of this trade-off between speed and reliability we have metaphors
like the “Testing Pyramid” to give us an idea of how our tests should be dis-
tributed between the three types of tests. Some developers attempt to quantify
the distribution of these tests as percentages but I think this is a bit much. I find
it best to keep the testing pyramid in mind and use what’s best suited for your
application and your needs.
Within the application we’ll be building in this book the large majority of our
tests will be unit and integration tests with a smaller subset of end-to-end tests.

8.7 Why do we test?


The reason we test is to prevent software defects. We write code with the
expectation that it will behave a certain way so we write automated tests to
verify this behavior.
I’ve worked on a variety of development teams in the past where a small piece
of code in the codebase is updated and the developer manually opens their
browser to verify that the code still works. In the worst cases, updates to a spe-
cific section of code made by one developer would then cause another devel-
oper to open their browser to run a few tests of their own to verify the changes
didn’t break any existing functionality they’d created.
As codebases grow, manual QA such as this becomes expensive, time con-
suming, and error prone. When a feature is removed does every developer re-
member all of its potential side-effects? Is each and every developer manually
testing in the same way? Probably not.
We write automated tests to do this for us. Not only do we automate the human
8.8. HOW TO APPROACH TESTING 89

element out of the process including all of its flaws but also have a source to
refer to that states how various parts of our application should behave.

8.8 How to approach testing


One of the most difficult aspects of testing for beginners is knowing how to
test. More specifically, we’re talking about what should or shouldn’t be tested.
I think it’s best to illustrate this with an example.
Imagine we had an alien servant named Adder who follows us everywhere we
go. Other than being a cute alien companion Adder can really only do one
thing, add two numbers together.
To verify Adder’s ability to add two numbers we could generate a set of “test
cases” to see if Adder provides us the correct answer. So we could provide
Adder with two positive numbers (2, 4), a positive number and a zero (3, 0), a
positive number and a negative number (5, -2), and so on.
The crucial point here is that when we test Adder, we aren’t necessarily con-
cerned with how Adder arrives at the answer. We only care about the answer
Adder provides us. In other words, we only care that Adder behaves as ex-
pected - we have no concern for Adder’s implementation.
Adder could be a super-intelligent alien actually performing the calculation
itself in its own brain similar to humans. Or Adder could simply be an interme-
diary taking in our input, offloading that calculation to a supercomputer back
on its home planet, merely vocalizing the result to us. Regardless of Adder’s
implementation we don’t care as long as the result is correct.

8.9 Testing Adder


To introduce some of the basics of testing we’re going to take a look at what
our tests for Adder would look like using the Jasmine framework. We won’t
90 CHAPTER 8. INTRODUCTION TO TESTING

be running these tests, we just want to see what tests look like at a basic level.
We’ll get to running tests in the next chapter once we start testing within An-
gular.
When we use Jasmine to test our code we group tests together with what Jas-
mine refers to as a “test suite”3 .

describe('Adder', () => {
// Tests go here
});

We begin our test suite by calling Jasmine’s describe function. This function
takes two parameters: a string and a function. The string serves as a title and
the function contains the code that implements our tests.
Within this describe (or test suite) is where we add our “specs”. Specs look
similar to test suites since they also take a string and a function as arguments
but rather than call describe we call it instead.

describe('Adder', () => {
it('should be able to add two whole numbers', () => {
// Test expectations go here
});
});

Just like our describe we’ve provide it with a brief summary of our test.
It helps to be descriptive here because the titles in our describe and it
combined read like a sentence telling us how our code should behave: “Adder
should be able to add two whole numbers”.
Within this it block is where we state our test’s expectations.

describe('Adder', () => {
it('should be able to add two whole numbers', () => {
expect(Adder.add(2, 2)).toEqual(4);
});
});

3
https://jasmine.github.io/2.4/introduction.html#section-Standalone_Distribution
8.9. TESTING ADDER 91

Our first test uses Jasmine’s expect method which we’ve provided with what
is referred to as an “actual”4 . This “actual” is the value, as in the actual value,
that we’re testing. In this case we’ve assumed Adder is an object with an add
method passing it two numbers.
Following our expect is what is known as a “matcher”5 function. This is
one of many matchers included with Jasmine but the one we’ve used here is
toEqual. We pass our matcher function, toEqual, the expected value we
expect to receive from Adder.add(2, 2). Let’s add one more test.

describe('Adder', () => {
it('should be able to add two whole numbers', () => {
expect(Adder.add(2, 2)).toEqual(4);
});

it('should be able to add a whole number and a negative number', () => {


expect(Adder.add(2, -1)).toEqual(1);
});
});

Similar to our previous test we’ve created another it passing it a description


of our test, and provided it with an expectation and a matcher for the condition
we want to test.
Note how our tests are only concerned with the return value of Adder.add().
All that matters is it returns the value we expect to receive. For all we know,
this could be the implementation of Adder.add().

function add(first, second) {


if (true) { // why?
if (true) { // why??
if (1 === 1) { // why?!?1
return first + second;
}
}
}
}

4
https://jasmine.github.io/2.4/introduction.html#section-Expectations
5
https://jasmine.github.io/2.4/introduction.html#section-Matchers
92 CHAPTER 8. INTRODUCTION TO TESTING

The implementation of add here may be bit crazy (so much for Adder being a
super-intelligent alien) but it would still return the sum of two numbers. When
we test code, the expected value is our only concern. A common mistake is to
test the implementation of code.

8.10 Test-driven development


Within the world of automated testing is a technique known as “test-driven
development” or TDD. Test-driven development is the practice of writing a
failing test for code first and then writing the code to get the test to pass.
One benefit of TDD is that your tests are the first “consumer” of your code.
The tests are using the code that has yet to be created stating an expected result
that it isn’t receiving. From there, you implement the code to make it pass
satisfying the expectations of the first consumer. From there, another failing
test can be added which is then implemented to get the test to pass. At this
point, you’ve confirmed that your new addition not only gets the second test to
pass but it also doesn’t negatively affect the first test either (assuming it’s still
passing).
Once the feature has been completed with all test cases passing you may decide
that your code is a bit sloppy and could be rewritten another way. With tests
already written you can now refactor your code (changing its form without
changing its function) with confidence verifying your tests are still passing
after the change.
Test-driven development, just like programming languages themselves, inevitably
leads to very heated debates among programmers. Some think it’s a good idea
while others think it’s bad. Some believe you should always follow TDD no
matter what while others avoid testing altogether.

Programming languages are like religions. You fall in love with


the first one that’s introduced to you.
8.10. TEST-DRIVEN DEVELOPMENT 93

Within this book we’re going to take a reasonable approach with TDD. We’ll
be using TDD when it’s convenient and makes sense but we’re not going to be
religious about it. At the very least I want you to at least be familiar with the
practice and what it is since it’s well known within software development and
likely to be an interview topic even if there isn’t a single developer within the
company who practices TDD themselves.

You might also like