Ng Book 2 The Guide To Angular 4

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 681

DownloadNg-book 2 Ng-book-The--Guide-to-Angular-4
Open PDF In BrowserView PDF
ng-book
The Complete Guide to Angular

Written by Nate Murray, Felipe Coury, Ari Lerner, and Carlos Taborda
© 2017 Fullstack.io
All rights reserved. No portion of the book manuscript may be reproduced, stored in a retrieval
system, or transmitted in any form or by any means beyond the number of purchased copies,
except for a single backup or archival copy. The code may be used freely in your projects,
commercial or otherwise.
The authors and publisher have taken care in preparation of this book, but make no expressed
or implied warranty of any kind and assume no responsibility for errors or omissions. No
liability is assumed for incidental or consequential damagers in connection with or arising out
of the use of the information or programs container herein.
Published in San Francisco, California by Fullstack.io.

FULLSTACK .io

We’d like to thank:

• Our technical editors: Frode Fikke, Travas Nolte, Daniel Rauf
• Nic Raboy, and Burke Holland for contributing the NativeScript chapter

Contents
Book Revision . . . . . . . . . .
Bug Reports . . . . . . . . . . .
Chat With The Community! . .
Vote for New Content (new!) . .
Be notified of updates via Twitter
We’d love to hear from you! . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

1
1
1
1
1
1

How to Read This Book . . . . . . . . .
Running Code Examples . . . . . .
Angular CLI . . . . . . . . . . . .
Code Blocks and Context . . . . . .
Code Block Numbering . . . . .
A Word on Versioning . . . . . . . .
Getting Help . . . . . . . . . . . . .
Emailing Us . . . . . . . . . . . . .
Technical Support Response Time
Chapter Overview . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

2
2
3
3
3
4
4
5
5
6

Writing Your First Angular Web Application
Simple Reddit Clone . . . . . . . . . . . . .
Getting started . . . . . . . . . . . . . . . .
Node.js and npm . . . . . . . . . . . . . .
TypeScript . . . . . . . . . . . . . . . . .
Browser . . . . . . . . . . . . . . . . . .
Special instruction for Windows users . . .
Angular CLI . . . . . . . . . . . . . . . .
Example Project . . . . . . . . . . . . .
Writing Application Code . . . . . . . .
Running the application . . . . . . . . . . .
Making a Component . . . . . . . . . . .
Importing Dependencies . . . . . . . . .
Component Decorators . . . . . . . . . .
Adding a template with templateUrl . .
Adding a template . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

1
1
4
4
4
4
5
5
6
10
10
12
13
14
14
15

CONTENTS

Adding CSS Styles with styleUrls . . . . . . .
Loading Our Component . . . . . . . . . . . . .
Adding Data to the Component . . . . . . . . . . .
Working With Arrays . . . . . . . . . . . . . . . .
Using the User Item Component . . . . . . . . . .
Rendering the UserItemComponent . . . . . . .
Accepting Inputs . . . . . . . . . . . . . . . . .
Passing an Input value . . . . . . . . . . . . . .
Bootstrapping Crash Course . . . . . . . . . . . .
declarations . . . . . . . . . . . . . . . . . . .
imports . . . . . . . . . . . . . . . . . . . . . .
providers . . . . . . . . . . . . . . . . . . . . .
bootstrap . . . . . . . . . . . . . . . . . . . . .
Expanding our Application . . . . . . . . . . . . .
Adding CSS . . . . . . . . . . . . . . . . . . . .
The Application Component . . . . . . . . . . .
Adding Interaction . . . . . . . . . . . . . . . .
Adding the Article Component . . . . . . . . .
Rendering Multiple Rows . . . . . . . . . . . . . .
Creating an Article class . . . . . . . . . . . . .
Storing Multiple Articles . . . . . . . . . . . .
Configuring the ArticleComponent with inputs
Rendering a List of Articles . . . . . . . . . . .
Adding New Articles . . . . . . . . . . . . . . . .
Finishing Touches . . . . . . . . . . . . . . . . . .
Displaying the Article Domain . . . . . . . . .
Re-sorting Based on Score . . . . . . . . . . . .
Deployment . . . . . . . . . . . . . . . . . . . . .
Building Our App for Production . . . . . . . .
Uploading to a Server . . . . . . . . . . . . . .
Installing now . . . . . . . . . . . . . . . . . . .
Full Code Listing . . . . . . . . . . . . . . . . . . .
Wrapping Up . . . . . . . . . . . . . . . . . . . . .
Getting Help . . . . . . . . . . . . . . . . . . . . .
TypeScript . . . . . . . . . . . . . .
Angular 4 is built in TypeScript .
What do we get with TypeScript?
Types . . . . . . . . . . . . . . .
Trying it out with a REPL . .
Built-in types . . . . . . . . . . .
Classes . . . . . . . . . . . . . .
Properties . . . . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

15
16
17
20
23
24
25
25
27
29
29
29
29
30
31
32
34
38
47
47
52
53
55
57
58
58
59
60
61
62
62
62
63
63

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

64
64
65
66
67
68
70
70

CONTENTS

Methods . . . . . . .
Constructors . . . .
Inheritance . . . . .
Utilities . . . . . . . . .
Fat Arrow Functions
Template Strings . .
Wrapping up . . . . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

71
73
74
76
76
78
79

How Angular Works . . . . . . . . . . . . . . . . . . . . . . . . . .
Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The Navigation Component . . . . . . . . . . . . . . . . . . .
The Breadcrumbs Component . . . . . . . . . . . . . . . . . .
The Product List Component . . . . . . . . . . . . . . . . . .
How to Use This Chapter . . . . . . . . . . . . . . . . . . . . . .
Product Model . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Component Decorator . . . . . . . . . . . . . . . . . . . . . . . .
Component selector . . . . . . . . . . . . . . . . . . . . . .
Component template . . . . . . . . . . . . . . . . . . . . . .
Adding A Product . . . . . . . . . . . . . . . . . . . . . . . .
Viewing the Product with Template Binding . . . . . . . . . .
Adding More Products . . . . . . . . . . . . . . . . . . . . . .
Selecting a Product . . . . . . . . . . . . . . . . . . . . . . . .
Listing products using  . . . . . . . . . . . .
The ProductsListComponent . . . . . . . . . . . . . . . . . . . .
Configuring the ProductsListComponent @Component Options
Component inputs . . . . . . . . . . . . . . . . . . . . . . . .
Component outputs . . . . . . . . . . . . . . . . . . . . . . .
Emitting Custom Events . . . . . . . . . . . . . . . . . . . . .
Writing the ProductsListComponent Controller Class . . . . .
Writing the ProductsListComponent View Template . . . . . .
The Full ProductsListComponent Component . . . . . . . . .
The ProductRowComponent Component . . . . . . . . . . . . . . .
ProductRowComponent Configuration . . . . . . . . . . . . . .
ProductRowComponent template . . . . . . . . . . . . . . . . .
The ProductImageComponent Component . . . . . . . . . . . . .
The PriceDisplayComponent Component . . . . . . . . . . . . .
The ProductDepartmentComponent . . . . . . . . . . . . . . . . .
NgModule and Booting the App . . . . . . . . . . . . . . . . . . .
Booting the app . . . . . . . . . . . . . . . . . . . . . . . . . .
The Completed Project . . . . . . . . . . . . . . . . . . . . . . .
Deploying the App . . . . . . . . . . . . . . . . . . . . . . . . . .
A Word on Data Architecture . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

80
80
81
81
82
84
85
86
88
88
88
89
91
92
93
94
97
98
98
100
101
103
104
106
108
109
110
111
111
112
113
115
116
116
117

CONTENTS

Built-in Directives . . .
Introduction . . . .
NgIf . . . . . . . .
NgSwitch . . . . . .
NgStyle . . . . . . .
NgClass . . . . . . .
NgFor . . . . . . . .
Getting an index
NgNonBindable . . .
Conclusion . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

119
119
119
120
122
125
128
133
134
135

Forms in Angular . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Forms are Crucial, Forms are Complex . . . . . . . . . . . . . . .
FormControls and FormGroups . . . . . . . . . . . . . . . . . . .
FormControl . . . . . . . . . . . . . . . . . . . . . . . . . . .
FormGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Our First Form . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Loading the FormsModule . . . . . . . . . . . . . . . . . . . .
Simple SKU Form: @Component Decorator . . . . . . . . . .
Simple SKU Form: template . . . . . . . . . . . . . . . . . . .
Simple SKU Form: Component Definition Class . . . . . . . .
Try it out! . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Using FormBuilder . . . . . . . . . . . . . . . . . . . . . . . . .
Reactive Forms with FormBuilder . . . . . . . . . . . . . . . . .
Using FormBuilder . . . . . . . . . . . . . . . . . . . . . . . .
Using myForm in the view . . . . . . . . . . . . . . . . . . . . .
Try it out! . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Adding Validations . . . . . . . . . . . . . . . . . . . . . . . . .
Explicitly setting the sku FormControl as an instance variable
Custom Validations . . . . . . . . . . . . . . . . . . . . . . . .
Watching For Changes . . . . . . . . . . . . . . . . . . . . . . . .
ngModel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Wrapping Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

136
136
136
136
137
138
139
140
140
144
144
146
147
147
148
149
151
152
158
160
161
163

Dependency Injection . . . . . . . . . . . .
Injections Example: PriceService . . .
Dependency Injection Parts . . . . . . .
Playing with an Injector . . . . . . . . .
Providing Dependencies with NgModule
Providers are the Key . . . . . . . . .
Providers . . . . . . . . . . . . . . . . .
Using a Class . . . . . . . . . . . . .
Using a Factory . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

164
165
169
170
174
176
176
176
181

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

CONTENTS

Dependency Injection in Apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
More Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
HTTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . .
Using @angular/http . . . . . . . . . . . . . . . . . . . . . .
import from @angular/http . . . . . . . . . . . . . . . . .
A Basic Request . . . . . . . . . . . . . . . . . . . . . . . . .
Building the SimpleHttpComponent Component Definition
Building the SimpleHttpComponent template . . . . . . . .
Building the SimpleHttpComponent Controller . . . . . . .
Full SimpleHttpComponent . . . . . . . . . . . . . . . . . .
Writing a YouTubeSearchComponent . . . . . . . . . . . . . .
Writing a SearchResult . . . . . . . . . . . . . . . . . . .
Writing the YouTubeSearchService . . . . . . . . . . . . .
Writing the SearchBoxComponent . . . . . . . . . . . . . .
Writing SearchResultComponent . . . . . . . . . . . . . .
Writing YouTubeSearchComponent . . . . . . . . . . . . . .
@angular/http API . . . . . . . . . . . . . . . . . . . . . . .
Making a POST request . . . . . . . . . . . . . . . . . . . .
PUT / PATCH / DELETE / HEAD . . . . . . . . . . . . . . . . .
RequestOptions . . . . . . . . . . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

186
186
187
187
188
189
189
190
192
192
194
195
200
207
208
212
212
213
214
215

Routing . . . . . . . . . . . . . . . . . . . . . . .
Why Do We Need Routing? . . . . . . . . . .
How client-side routing works . . . . . . . .
The beginning: using anchor tags . . . . .
The evolution: HTML5 client-side routing
Writing our first routes . . . . . . . . . . . .
Components of Angular routing . . . . . . .
Imports . . . . . . . . . . . . . . . . . . .
Routes . . . . . . . . . . . . . . . . . . . .
Installing our Routes . . . . . . . . . . . .
RouterOutlet using  . .
RouterLink using [routerLink] . . . . . .
Putting it all together . . . . . . . . . . . . .
Creating the Components . . . . . . . . .
HomeComponent . . . . . . . . . . . . . . .
AboutComponent . . . . . . . . . . . . . .
ContactComponent . . . . . . . . . . . . .
Application Component . . . . . . . . . .
Configuring the Routes . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

216
216
217
218
218
219
219
219
220
221
222
224
224
226
226
227
227
228
229

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

CONTENTS

Routing Strategies . . . . . . . . . . . . . .
Running the application . . . . . . . . . . .
Route Parameters . . . . . . . . . . . . . .
ActivatedRoute . . . . . . . . . . . . .
Music Search App . . . . . . . . . . . . . .
First Steps . . . . . . . . . . . . . . . . .
The SpotifyService . . . . . . . . . . .
The SearchComponent . . . . . . . . . .
Trying the search . . . . . . . . . . . . .
TrackComponent . . . . . . . . . . . . .
Wrapping up music search . . . . . . . .
Router Hooks . . . . . . . . . . . . . . . . .
AuthService . . . . . . . . . . . . . . .
LoginComponent . . . . . . . . . . . . .
ProtectedComponent and Route Guards .
Nested Routes . . . . . . . . . . . . . . . .
Configuring Routes . . . . . . . . . . . .
ProductsModule . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

231
232
234
235
236
238
239
240
249
251
253
253
254
255
257
264
265
266
270

Data Architecture in Angular 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
An Overview of Data Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
Data Architecture in Angular 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
Data Architecture with Observables - Part 1: Services
Observables and RxJS . . . . . . . . . . . . . . . . .
Note: Some RxJS Knowledge Required . . . . . .
Learning Reactive Programming and RxJS . . . .
Chat App Overview . . . . . . . . . . . . . . . . . .
Components . . . . . . . . . . . . . . . . . . . .
Models . . . . . . . . . . . . . . . . . . . . . . .
Services . . . . . . . . . . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . . . .
Implementing the Models . . . . . . . . . . . . . . .
User . . . . . . . . . . . . . . . . . . . . . . . . .
Thread . . . . . . . . . . . . . . . . . . . . . . . .
Message . . . . . . . . . . . . . . . . . . . . . . .
Implementing UsersService . . . . . . . . . . . . .
currentUser stream . . . . . . . . . . . . . . . .
Setting a new user . . . . . . . . . . . . . . . . .
UsersService.ts . . . . . . . . . . . . . . . . . .
The MessagesService . . . . . . . . . . . . . . . . .
the newMessages stream . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

273
273
273
273
275
276
277
278
278
279
279
279
280
281
282
283
284
285
285

CONTENTS

the messages stream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The Operation Stream Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Sharing the Stream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Adding Messages to the messages Stream . . . . . . . . . . . . . . . . . . . . . . .
Our completed MessagesService . . . . . . . . . . . . . . . . . . . . . . . . . . .
Trying out MessagesService . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The ThreadsService . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
A map of the current set of Threads (in threads) . . . . . . . . . . . . . . . . . . .
A chronological list of Threads, newest-first (in orderedthreads) . . . . . . . . . .
The currently selected Thread (in currentThread) . . . . . . . . . . . . . . . . . .
The list of Messages for the currently selected Thread (in currentThreadMessages)
Our Completed ThreadsService . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Data Model Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

287
287
289
290
293
296
298
298
303
303
305
308
310

Data Architecture with Observables - Part 2: View Components
Building Our Views: The AppComponent Top-Level Component .
The ChatThreadsComponent . . . . . . . . . . . . . . . . . . . .
ChatThreadsComponent template . . . . . . . . . . . . . . .
The Single ChatThreadComponent . . . . . . . . . . . . . . . . .
ChatThreadComponent Controller and ngOnInit . . . . . . .
ChatThreadComponent template . . . . . . . . . . . . . . . .
The ChatWindowComponent . . . . . . . . . . . . . . . . . . . .
The ChatMessageComponent . . . . . . . . . . . . . . . . . . . .
The ChatMessageComponent template . . . . . . . . . . . . .
The ChatNavBarComponent . . . . . . . . . . . . . . . . . . . .
The ChatNavBarComponent @Component . . . . . . . . . . . .
The ChatNavBarComponent template . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

311
311
314
315
315
317
317
318
328
330
331
331
333
334

Introduction to Redux with TypeScript . . . .
Redux . . . . . . . . . . . . . . . . . . . . .
Redux: Key Ideas . . . . . . . . . . . . .
Core Redux Ideas . . . . . . . . . . . . . .
What’s a reducer? . . . . . . . . . . . .
Defining Action and Reducer Interfaces
Creating Our First Reducer . . . . . . .
Running Our First Reducer . . . . . . . .
Adjusting the Counter With actions . . .
Reducer switch . . . . . . . . . . . . . .
Action “Arguments” . . . . . . . . . . .
Storing Our State . . . . . . . . . . . . . .
Using the Store . . . . . . . . . . . . . .
Being Notified with subscribe . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

336
337
337
338
338
339
340
340
341
342
344
345
346
346

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

CONTENTS

The Core of Redux . . . . . .
A Messaging App . . . . . . . .
Messaging App state . . . .
Messaging App actions . . .
Messaging App reducer . . .
Trying Out Our Actions . . .
Action Creators . . . . . . . .
Using Real Redux . . . . . . .
Using Redux in Angular . . . . .
Planning Our App . . . . . . . .
Setting Up Redux . . . . . . . .
Defining the Application State
Defining the Reducers . . . .
Defining Action Creators . .
Creating the Store . . . . . .
Providing the Store . . . . . . .
Bootstrapping the App . . . . . .
The AppComponent . . . . . . . .
imports . . . . . . . . . . . .
The template . . . . . . . . .
The constructor . . . . . . .
Putting It All Together . . . .
What’s Next . . . . . . . . . . .
References . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

350
351
351
352
353
356
357
359
361
362
362
362
362
363
364
366
367
368
368
369
370
372
373
373

Intermediate Redux in Angular . . . . . . . .
Context For This Chapter . . . . . . . . . .
Chat App Overview . . . . . . . . . . . . .
Components . . . . . . . . . . . . . . .
Models . . . . . . . . . . . . . . . . . .
Reducers . . . . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . .
Implementing the Models . . . . . . . . . .
User . . . . . . . . . . . . . . . . . . . .
Thread . . . . . . . . . . . . . . . . . . .
Message . . . . . . . . . . . . . . . . . .
App State . . . . . . . . . . . . . . . . . . .
A Word on Code Layout . . . . . . . . .
The Root Reducer . . . . . . . . . . . . .
The UsersState . . . . . . . . . . . . . .
The ThreadsState . . . . . . . . . . . .
Visualizing Our AppState . . . . . . . .
Building the Reducers (and Action Creators)

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

374
374
375
375
376
377
377
378
378
378
379
380
380
381
381
382
383
384

CONTENTS

Set Current User Action Creators . . . . .
UsersReducer - Set Current User . . . . .
Thread and Messages Overview . . . . . .
Adding a New Thread Action Creators . .
Adding a New Thread Reducer . . . . . .
Adding New Messages Action Creators . .
Adding A New Message Reducer . . . . .
Selecting A Thread Action Creators . . . .
Selecting A Thread Reducer . . . . . . . .
Reducers Summary . . . . . . . . . . . . .
Building the Angular Chat App . . . . . . . .
The top-level AppComponent . . . . . . . .
The ChatPage . . . . . . . . . . . . . . . .
Container vs. Presentational Components .
Building the ChatNavBarComponent . . . . . .
Redux Selectors . . . . . . . . . . . . . . .
Threads Selectors . . . . . . . . . . . . . .
Unread Messages Count Selector . . . . .
Building the ChatThreadsComponent . . . . .
ChatThreadsComponent Controller . . . . .
ChatThreadsComponent template . . . . .
The Single ChatThreadComponent . . . . . . .
ChatThreadComponent template . . . . . .
Building the ChatWindowComponent . . . . . .
The ChatMessageComponent . . . . . . . . . .
Setting incoming . . . . . . . . . . . . . .
The ChatMessageComponent template . . .
Summary . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

384
385
386
386
387
388
389
391
392
393
393
395
397
398
399
401
402
403
404
405
407
408
409
410
417
418
419
420

Advanced Components . . . . . . . . . . . . . . . . . . . . . . .
Styling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
View (Style) Encapsulation . . . . . . . . . . . . . . . . . .
Shadow DOM Encapsulation . . . . . . . . . . . . . . . .
No Encapsulation . . . . . . . . . . . . . . . . . . . . . . .
Creating a Popup - Referencing and Modifying Host Elements
Popup Structure . . . . . . . . . . . . . . . . . . . . . . . .
Using ElementRef . . . . . . . . . . . . . . . . . . . . . .
Binding to the host . . . . . . . . . . . . . . . . . . . . . .
Adding a Button using exportAs . . . . . . . . . . . . . .
Creating a Message Pane with Content Projection . . . . . . .
Changing the Host’s CSS . . . . . . . . . . . . . . . . . . .
Using ng-content . . . . . . . . . . . . . . . . . . . . . .
Querying Neighbor Directives - Writing Tabs . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

422
422
425
429
431
434
434
436
438
441
443
444
444
446

CONTENTS

ContentTabComponent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
ContentTabsetComponent Component . . . . . . . . . . . . . . . . . . . . . . . . . . 448
Using the ContentTabsetComponent . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450

Lifecycle Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
OnInit and OnDestroy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
OnChanges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
DoCheck . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
AfterContentInit, AfterViewInit, AfterContentChecked and AfterViewChecked
Advanced Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Rewriting ngIf - ngBookIf . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Rewriting ngFor - NgBookFor . . . . . . . . . . . . . . . . . . . . . . . . . . .
Change Detection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Customizing Change Detection . . . . . . . . . . . . . . . . . . . . . . . . . .
Zones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Observables and OnPush . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Testing . . . . . . . . . . . . . . . .
Test driven? . . . . . . . . . . .
End-to-end vs. Unit Testing . . .
Testing Tools . . . . . . . . . . .
Jasmine . . . . . . . . . . . .
Karma . . . . . . . . . . . . .
Writing Unit Tests . . . . . . . .
Angular Unit testing framework
Setting Up Testing . . . . . . . .
Testing Services and HTTP . . .
HTTP Considerations . . . .
Stubs . . . . . . . . . . . . .
Mocks . . . . . . . . . . . . .
Http MockBackend . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
TestBed.configureTestingModule and Providers
Testing getTrack . . . . . . . . . . . . . . . . . .
Testing Routing to Components . . . . . . . . . . . .
Creating a Router for Testing . . . . . . . . . . .
Mocking dependencies . . . . . . . . . . . . . . .
Spies . . . . . . . . . . . . . . . . . . . . . . . . .
Back to Testing Code . . . . . . . . . . . . . . . . .
fakeAsync and advance . . . . . . . . . . . . . .
inject . . . . . . . . . . . . . . . . . . . . . . . .
Testing ArtistComponent’s Initialization . . . . .
Testing ArtistComponent Methods . . . . . . . .
Testing ArtistComponent DOM Template Values .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

452
453
457
463
476
483
484
486
492
496
503
504
508

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

509
509
509
510
510
511
511
511
512
514
515
515
516
517
517
518
525
525
528
529
531
534
535
535
536
538

CONTENTS

Testing Forms . . . . . . . . . . .
Creating a ConsoleSpy . . . . .
Installing the ConsoleSpy . . .
Configuring the Testing Module
Testing The Form . . . . . . . .
Refactoring Our Form Test . . .
Testing HTTP requests . . . . . .
Testing a POST . . . . . . . . . .
Testing DELETE . . . . . . . . .
Testing HTTP Headers . . . . .
Testing YouTubeSearchService
Conclusion . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

540
543
544
545
545
547
551
551
553
554
555
562

Converting an AngularJS 1.x App to Angular 4 . . . .
Peripheral Concepts . . . . . . . . . . . . . . . . . .
What We’re Building . . . . . . . . . . . . . . . . .
Mapping AngularJS 1 to Angular 4 . . . . . . . . . .
Requirements for Interoperability . . . . . . . . . . .
The AngularJS 1 App . . . . . . . . . . . . . . . . .
The ng1-app HTML . . . . . . . . . . . . . . . .
Code Overview . . . . . . . . . . . . . . . . . . .
ng1: PinsService . . . . . . . . . . . . . . . . . .
ng1: Configuring Routes . . . . . . . . . . . . . .
ng1: HomeController . . . . . . . . . . . . . . . .
ng1: / HomeController template . . . . . . . . . .
ng1: pin Directive . . . . . . . . . . . . . . . . .
ng1: pin Directive template . . . . . . . . . . . .
ng1: AddController . . . . . . . . . . . . . . . .
ng1: AddController template . . . . . . . . . . .
ng1: Summary . . . . . . . . . . . . . . . . . . .
Building A Hybrid . . . . . . . . . . . . . . . . . . .
Hybrid Project Structure . . . . . . . . . . . . . .
Bootstrapping our Hybrid App . . . . . . . . . . .
What We’ll Upgrade . . . . . . . . . . . . . . . .
A Minor Detour: Typing Files . . . . . . . . . . .
Writing ng2 PinControlsComponent . . . . . . . .
Using ng2 PinControlsComponent . . . . . . . . .
Downgrading ng2 PinControlsComponent to ng1
Adding Pins with ng2 . . . . . . . . . . . . . . .
Upgrading ng1 PinsService and $state to ng2 .
Writing ng2 AddPinComponent . . . . . . . . . . .
Using AddPinComponent . . . . . . . . . . . . . .
Exposing an ng2 service to ng1 . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

563
563
564
565
567
567
569
570
570
572
573
573
574
574
576
578
581
581
582
584
586
589
592
594
595
597
598
599
605
605

CONTENTS

Writing the AnalyticsService . . . . . .
Downgrade ng2 AnalyticsService to ng1
Using AnalyticsService in ng1 . . . . . .
Summary . . . . . . . . . . . . . . . . . . . .
References . . . . . . . . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

606
606
607
608
609

NativeScript: Mobile Applications for the Angular Developer . . . . . . . .
What is NativeScript? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Where NativeScript Differs from Other Popular Frameworks . . . . . .
What are the System and Development Requirements for NativeScript?
Creating your First Mobile Application with NativeScript and Angular . .
Adding Build Platforms for Cross Platform Deployment . . . . . . . . .
Building and Testing for Android and iOS . . . . . . . . . . . . . . . .
Installing JavaScript, Android, and iOS Plugins and Packages . . . . . .
Understanding the Web to NativeScript UI and UX Differences . . . . . .
Planning the NativeScript Page Layout . . . . . . . . . . . . . . . . . .
Adding UI Components to the Page . . . . . . . . . . . . . . . . . . . .
Styling Components with CSS . . . . . . . . . . . . . . . . . . . . . . .
Developing a Geolocation Based Photo Application . . . . . . . . . . . . .
Creating a Fresh NativeScript Project . . . . . . . . . . . . . . . . . . .
Creating a Multiple Page Master-Detail Interface . . . . . . . . . . . . .
Creating a Flickr Service for Obtaining Photos and Data . . . . . . . . .
Creating a Service for Calculating Device Location and Distance . . . .
Including Mapbox Functionality in the NativeScript Application . . . .
Implementing the First Page of the Geolocation Application . . . . . . .
Implementing the Second Page of the Geolocation Application . . . . .
Try it out! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
NativeScript for Angular Developers . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

610
610
611
612
614
614
614
615
616
616
618
619
620
621
622
626
631
635
636
642
643
644

Changelog . . . . . . . . . . . .
Revision 62 - 2017-06-23 .
Revision 61 - 2017-05-24 .
Revision 60 - 2017-04-27 .
Revision 59 - 2017-04-07 .
Revision 58 - 2017-03-24 .
Revision 57 - 2017-03-23 .
Revision 56 - 2017-03-22 .
Revision 55 - 2017-03-17 .
Revision 54 - 2017-03-10 .
Revision 53 - 2017-03-01 .
Revision 52 - 2017-02-22 .
Revision 51 - 2017-02-14 .
Revision 50 - 2017-02-10 .

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

645
645
645
645
646
646
646
646
647
647
647
647
648
648

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

CONTENTS

Revision 49 - 2017-01-18 .
Revision 48 - 2017-01-13 .
Revision 47 - 2017-01-06 .
Revision 46 - 2017-01-03 .
Revision 45 - 2016-12-05 .
Revision 44 - 2016-11-17 .
Revision 43 - 2016-11-08 .
Revision 42 - 2016-10-14 .
Revision 41 - 2016-09-28 .
Revision 40 - 2016-09-20 .
Revision 39 - 2016-09-03 .
Revision 38 - 2016-08-29 .
Revision 37 - 2016-08-02 .
Revision 36 - 2016-07-20 .
Revision 35 - 2016-06-30 .
Revision 34 - 2016-06-15 .
Revision 33 - 2016-05-11 .
Revision 32 - 2016-05-06 .
Revision 31 - 2016-04-28 .
Revision 30 - 2016-04-20 .
Revision 29 - 2016-04-08 .
Revision 28 - 2016-04-01 .
Revision 27 - 2016-03-25 .
Revision 26 - 2016-03-24 .
Revision 25 - 2016-03-21 .
Revision 24 - 2016-03-10 .
Revision 23 - 2016-03-04 .
Revision 22 - 2016-02-24 .
Revision 21 - 2016-02-20 .
Revision 20 - 2016-02-11 .
Revision 19 - 2016-02-04 .
Revision 18 - 2016-01-29 .
Revision 17 - 2016-01-28 .
Revision 16 - 2016-01-14 .
Revision 15 - 2016-01-07 .
Revision 14 - 2015-12-23 .
Revision 13 - 2015-12-17 .
Revision 12 - 2015-11-16 .
Revision 11 - 2015-11-09 .
Revision 10 - 2015-10-30 .
Revision 9 - 2015-10-15 .
Revision 8 - 2015-10-08 .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

648
648
648
648
648
648
649
649
649
650
650
650
650
650
650
650
651
651
651
652
652
652
652
652
652
652
652
653
653
653
654
654
654
654
654
654
655
655
656
656
656
657

CONTENTS

Revision 7 - 2015-09-23
Revision 6 - 2015-08-28
Revision 5 - 2015-08-01
Revision 4 - 2015-07-30
Revision 3 - 2015-07-21
Revision 2 - 2015-07-15
Revision 1 - 2015-07-01

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

657
657
657
657
658
658
658

CONTENTS

1

Book Revision
Revision 62 - Covers up to Angular 4 (4.2.0, 2017-06-23)

Bug Reports
If you’d like to report any bugs, typos, or suggestions just email us at: us@fullstack.io¹.

Chat With The Community!
We’re experimenting with a community chat room for this book using Gitter. If you’d like to hang
out with other people learning Angular, come join us on Gitter²!

Vote for New Content (new!)
We’re constantly updating the book, writing new blog posts, and producing new material. You can
now cast your vote for new content here³.

Be notified of updates via Twitter
If you’d like to be notified of updates to the book on Twitter, follow @fullstackio⁴

We’d love to hear from you!
Did you like the book? Did you find it helpful? We’d love to add your face to our list of testimonials
on the website! Email us at: us@fullstack.io⁵.

¹mailto:us@fullstack.io?Subject=ng-book%202%20feedback
²https://gitter.im/ng-book/ng-book
³https://fullstackio.canny.io/ng-book
⁴https://twitter.com/fullstackio
⁵mailto:us@fullstack.io?Subject=ng-book%202%20testimonial

How to Read This Book
This book aims to be the single most useful resource on learning Angular. By the time you’re done
reading this book, you (and your team) will have everything you need to build reliable, powerful
Angular apps.
Angular is a rich and feature-filled framework, but that also means it can be tricky to understand all
of its parts. In this book, we’ll walk through everything from installing the tools, writing components,
using forms, routing between pages, and calling APIs.
But before we dig in, there are a few guidelines I want to give you in order to get the most out of
this book. Briefly, I want to tell you:
• how to approach the code examples and
• how to get help if something goes wrong

Running Code Examples
This book comes with a library of runnable code examples. The code is available to download from
the same place where you downloaded this book.
We use the program npm⁶ to run every example in this book. This means you can type the following
commands to run any example:
1
2

npm install
npm start

If you’re unfamiliar with npm, we cover how to get it installed in the Getting Started section
in the first chapter.

After running npm start, you will see some output on your screen that will tell you what URL to
open to view your app.
If you’re ever unclear on how to run a particular sample app, check out the README.md in that
project’s directory. Every sample project contains a README.md that will give you the instructions
you need to run each app.
⁶https://www.npmjs.com/

How to Read This Book

3

Angular CLI
With a couple of minor exceptions, every project in this book was built on Angular CLI⁷. Unless
specified otherwise, you can use the ng commands in each project.
For instance, to run an example you can run ng serve (this is, generally, what is run when you type
npm start). For most projects you can compile them to JavaScript with ng build (we’ll talk about
this more in the first chapter). And you can run end-to-end tests with ng e2e, etc.
Without getting too far into the details, Angular CLI is based on Webpack, a tool which helps process
and bundle our various TypeScript, JavaScript, CSS, HTML, and image files. Angular CLI is not a
requirement for using Angular. It’s simply a wrapper around Webpack (and some other tooling)
that makes it easy to get started.

Code Blocks and Context
Nearly every code block in this book is pulled from a runnable code example, which you can find
in the sample code. For example, here is a code block pulled from the first chapter:
code/first-app/angular-hello-world/src/app/app.component.ts

8
9
10

export class AppComponent {
title = 'app works!';
}

Notice that the header of this code block states the path to the file which contains this code:
code/first-app/angular-hello-world/src/app/app.component.ts.
If you ever feel like you’re missing the context for a code example, open up the full code file using
your favorite text editor. This book is written with the expectation that you’ll also be looking
at the example code alongside the manuscript.
For example, we often need to import libraries to get our code to run. In the early chapters of the
book we show these import statements, because it’s not clear where the libraries are coming from
otherwise. However, the later chapters of the book are more advanced and they focus on key concepts
instead of repeating boilerplate code that was covered earlier in the book. If at any point you’re
not clear on the context, open up the code example on disk.

Code Block Numbering
In this book, we sometimes build up a larger example in steps. If you see a file being loaded that has
a numeric suffix, that generally means we’re building up to something bigger.
⁷https://github.com/angular/angular-cli

How to Read This Book

4

For instance, in the Dependency Injection chapter you may see a code block with the filename:
price.service.1.ts. When you see the .N.ts syntax that means we’re building up to the ultimate
file, which will not have a number. So, in this case, the final version would be: price.service.ts.
We do it this way so that a) we can unit test the intermediate code and b) you can see the whole file
in context at a particular stage.

A Word on Versioning
As you may know, the Angular covered in this book is a descendant of an earlier framework called
“AngularJS”. This can sometimes be confusing, particularly when reading supplementary blogs or
documentation.
The official branding guidelines state that “AngularJS” is a term reserved for AngularJS 1.x, that is,
the early versions of “Angular”.
Because the new version of Angular used TypeScript (instead of JavaScript) as the primary language,
the ‘JS’ was dropped, leaving us with just Angular. For a long time the only consistent way to
distinguish the two was folks referred to the new Angular as Angular 2.
However, the Angular team in 2017 switched to semantic versioning with a new major-release
upgrade slated for every 6 months. Instead of calling the next versions Angular 4, Angular 5, and so
on, the number is also dropped and it’s just Angular.
In this book, when we’re referring to Angular we’ll just say Angular or sometimes Angular 4, just
to avoid confusion. When we’re talking about “the old-style JavaScript Angular” we’ll use the term
AngularJS or AngularJS 1.x.

Getting Help
While we’ve made every effort to be clear, precise, and accurate you may find that when you’re
writing your code you run into a problem.
Generally, there are three types of problems:
• A “bug” in the book (e.g. how we describe something is wrong)
• A “bug” in our code
• A “bug” in your code
If you find an inaccuracy in how we describe something, or you feel a concept isn’t clear, email us!
We want to make sure that the book is both accurate and clear.
Similarly, if you’ve found a bug in our code we definitely want to hear about it.
If you’re having trouble getting your own app working (and it isn’t our example code), this case is
a bit harder for us to handle.

How to Read This Book

5

Your first line of defense, when getting help with your custom app, should be our unofficial
community chat room⁸. We (the authors) are there from time-to-time, but there are hundreds of
other readers there who may be able to help you faster than we can.
If you’re still stuck, we’d still love to hear from you, and here are some tips for getting a clear, timely
response.

Emailing Us
If you’re emailing us asking for technical help, here’s what we’d like to know:
•
•
•
•
•
•
•

What revision of the book are you referring to?
What operating system are you on? (e.g. Mac OS X 10.8, Windows 95)
Which chapter and which example project are you on?
What were you trying to accomplish?
What have you tried⁹ already?
What output did you expect?
What actually happened? (Including relevant log output.)

The absolute best way to get technical support is to send us a short, self-contained example of the
problem. Our preferred way to receive this would be for you to send us a Plunkr link by using this
URL¹⁰.
That URL contains a runnable, boilerplate Angular app. If you can copy and paste your code into
that project, reproduce your error, and send it to us you’ll greatly increase the likelihood of a
prompt, helpful response.
When you’ve written down these things, email us at us@fullstack.io¹¹. We look forward to hearing
from you.

Technical Support Response Time
We perform our free, technical support once per week.
If you need a faster response time, and help getting any of your team’s questions answered, then
you may consider our premium support option¹².
⁸https://gitter.im/ng-book/ng-book
⁹http://mattgemmell.com/what-have-you-tried/
¹⁰https://angular.io/resources/live-examples/quickstart/ts/eplnkr.html
¹¹mailto:us@fullstack.io
¹²mailto:us@fullstack.io?Subject=Angular%20Premium%20Support&Body=Hello%21%20I%27m%20interested%20in%20premium%20Angular%

20support%20for%20our%20team

How to Read This Book

6

Chapter Overview
Before we dive in, I want to give you a feel for the rest of the book and what you can expect inside.
The first few chapters provide the foundation you need to get up and running with Angular. You’ll
create your first apps, use the built-in components, and start creating your components.
Next we’ll move into intermediate concepts such as using forms, using APIs, routing to different
pages, and using Dependency Injection to organize our code.
After that, we’ll move into more advanced concepts. We spend a good part of the book talking
about data architectures. Managing state in client/server applications is hard and we dive deep into
two popular approaches: using RxJS Observables and using Redux. In these chapters, we’ll show
how to build the same app, two different ways, so you can compare and contrast and evaluate what’s
best for you and your team.
After that, we’ll discuss how to write complex, advanced components using Angular’s most
powerful features. Then we talk about how to write tests for our app and how we can upgrade
our Angular 1 apps to Angular 4+. Finally, we close with a chapter on writing native mobile apps
with Angular using NativeScript.
By using this book, you’re going to learn how to build real Angular apps faster than spending
hours parsing out-dated blog posts.
So hold on tight - you’re about to become an Angular expert, and have a lot of fun along the way.
Let’s dig in!
• Nate (@eigenjoy¹³)

¹³https://twitter.com/eigenjoy

Writing Your First Angular Web
Application
Simple Reddit Clone
In this chapter we’re going to build an application that allows the user to post an article (with a
title and a URL) and then vote on the posts.
You can think of this app as the beginnings of a site like Reddit¹⁴ or Product Hunt¹⁵.
In this simple app we’re going to cover most of the essentials of Angular including:
•
•
•
•
•

Building custom components
Accepting user input from forms
Rendering lists of objects into views
Intercepting user clicks and acting on them
Deploying our app to a server

By the time you’re finished with this chapter you’ll know how to take an empty folder, build a basic
Angular application, and deploy it to production. After working through this chapter you’ll have a
good grasp on how Angular applications are built and a solid foundation to build your own Angular
app.
Here’s a screenshot of what our app will look like when it’s done:
¹⁴http://reddit.com
¹⁵http://producthunt.com

2

Writing Your First Angular Web Application

Completed application

First, a user will submit a new link and after submitting the users will be able to upvote or downvote
each article. Each link will have a score and we can vote on which links we find useful.

3

Writing Your First Angular Web Application

App with new article

In this project, and throughout the book, we’re going to use TypeScript. TypeScript is a superset of
JavaScript ES6 that adds types. We’re not going to talk about TypeScript in depth in this chapter, but
we’ll go over TypeScript more in depth in the next chapter.
Don’t worry if you’re having trouble with some of the new syntax. If you’re familiar with ES5
(“normal” JavaScript) / ES6 (ES2015) you should be able to follow along and we’ll talk more about
TypeScript in a bit.

Writing Your First Angular Web Application

4

Getting started
Node.js and npm
To get started with Angular, you’ll need to have Node.js installed. There are a couple of different
ways you can install Node.js, so please refer to the Node.js website¹⁶ for detailed information.
Make sure you install Node 6.9.0 or higher.
If you’re on a Mac, your best bet is to install Node.js directly from the Node.js website
instead of through another package manager (like Homebrew). Installing Node.js via
Homebrew is known to cause some issues.

The Node Package Manager (npm for short) is installed as a part of Node.js. To check if npm is available
as a part of our development environment, we can open a terminal window and type:
$ npm -v

If a version number is not printed out and you receive an error, make sure to download a Node.js
installer that includes npm.
Your npm version should be 3.0.0 or higher.

TypeScript
Once you have Node.js setup, the next step is to install TypeScript. Make sure you install at least
version 2.1 or greater. To install it, run the following npm command:
1

$ npm install -g typescript

Do I have to use TypeScript? No, you don’t have to use TypeScript to use Angular, but you
probably should. Angular does have an ES5 API, but Angular is written in TypeScript and
generally that’s what everyone is using. We’re going to use TypeScript in this book because
it’s great and it makes working with Angular easier. That said, it isn’t strictly required.

Browser
We highly recommend using the Google Chrome Web Browser¹⁷ to develop Angular apps. We’ll
use the Chrome developer toolkit throughout this book. To follow along with our development and
debugging we recommend downloading it now.
¹⁶https://nodejs.org/download/
¹⁷https://www.google.com/chrome/

Writing Your First Angular Web Application

5

Special instruction for Windows users
Throughout this book, we will be using Unix/Mac commands in the terminal. Most of these
commands, like ls and cd, are cross-platform. However, sometimes these commands are Unix/Macspecific or contain Unix/Mac-specific flags (like ls -1p).
As a result, be alert that you may have to occasionally determine the equivalent of a Unix/Mac
command for your shell. Fortunately, the amount of work we do in the terminal is minimal and you
will not encounter this issue often.
Windows users should be aware that our terminal examples use Unix/Mac commands.

Angular CLI
Angular provides a utility to allow users to create and manage projects from the command line. It
automates tasks like creating projects, adding new controllers, etc. It’s generally a good idea to use
Angular CLI as it will help create and maintain common patterns across our application.
To install Angular CLI, just run the following command:
1

$ npm install -g @angular/cli@1.0.0

Once it’s installed you’ll be able to run it from the command line using the ng command. When you
do, you’ll see a lot of output, but if you scroll back, you should be able to see the following:
1

$ ng --version

If everything installed correctly, you should see the current version output to your terminal.
Congratulations!
If you’re running OSX or Linux, you might receive this line in the output:
1

Could not start watchman; falling back to NodeWatcher for file system events.

This means that we don’t have a tool called watchman installed. This tool helps Angular
CLI when it needs to monitor files in your filesystem for changes. If you’re running OSX,
it’s recommended to install it using Homebrew with the following command:
1

$ brew install watchman

Writing Your First Angular Web Application

6

If you’re on OSX and got an error when running brew, it means that you probably don’t
have Homebrew installed. Please refer to the page http://brew.sh/ to learn how to install it
and try again.
If you’re on Linux, you may refer to the page https://ember-cli.com/user-guide/#watchman
for more information about how to install watchman.
If you’re on Windows instead, you don’t need to install anything and Angular CLI will use
the native Node.js watcher.

If you’re curious about all of the things that Angular CLI can do, try out this command:
1

$ ng --help

Don’t worry about understanding all of the options - we’ll be covering the important ones in this
chapter.
Now that we have Angular CLI and its dependencies installed, let’s use this tool to create our first
application.

Example Project
Open up the terminal and run the ng new command to create a new project from scratch:
1

$ ng new --ng4 angular-hello-world

If you’re using a version of Angular CLI that is newer than @angular/cli@1.0.0-rc.4,
you may omit the --ng4 option as Angular 4 will be the default.

Once you run it, you’ll see the following output:
1
2
3
4
5
6
7
8
9
10
11

installing ng2
create .editorconfig
create README.md
create src/app/app.component.css
create src/app/app.component.html
create src/app/app.component.spec.ts
create src/app/app.component.ts
create src/app/app.module.ts
create src/assets/.gitkeep
create src/environments/environment.prod.ts
create src/environments/environment.ts

Writing Your First Angular Web Application

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

7

create src/favicon.ico
create src/index.html
create src/main.ts
create src/polyfills.ts
create src/styles.css
create src/test.ts
create src/tsconfig.json
create .angular-cli.json
create e2e/app.e2e-spec.ts
create e2e/app.po.ts
create e2e/tsconfig.json
create .gitignore
create karma.conf.js
create package.json
create protractor.conf.js
create tslint.json
Successfully initialized git.
Installing packages for tooling via npm.
Installed packages for tooling via npm.

Note: the exact files that your project generates may vary slightly depending on the version
of @angular/cli that was installed.

This will run for a while while it’s installing npm dependencies. Once it finishes we’ll see a success
message:
1

Project 'angular-hello-world' successfully created.

There are a lot of files generated! Don’t worry about understanding all of them yet. Throughout the
book we’ll walk through what each one means and what it’s used for.
Let’s go inside the angular-hello-world directory, which the ng command created for us and see
what has been created:

Writing Your First Angular Web Application

1
2
3
4
5
6
7
8
9
10
11
12

$ cd angular-hello-world
$ tree -F -L 1
.
├── README.md
├── .angular-cli.json
├── e2e/
├── karma.conf.js
├── node_modules/
├── package.json
├── protractor.conf.js
├── src/
└── tslint.json

//
//
//
//
//
//
//
//
//

8

an useful README
angular-cli configuration file
end to end tests
unit test configuration
installed dependencies
npm configuration
e2e test configuration
application source
linter config file

The tree command is completely optional. But if you’re on OSX it can be installed via
brew install tree

For now, the folder we’re interested in is src, where we’ll put our custom application code. Let’s
take a look at what was created there:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

$ cd src
$ tree -F
.
|-- app/
|
|-- app.component.css
|
|-- app.component.html
|
|-- app.component.spec.ts
|
|-- app.component.ts
|
`-- app.module.ts
|-- assets/
|-- environments/
|
|-- environment.prod.ts
|
`-- environment.ts
|-- favicon.ico
|-- index.html
|-- main.ts
|-- polyfills.ts
|-- styles.css
|-- test.ts
`-- tsconfig.json

Using your favorite text editor, let’s open index.html. You should see this code:

Writing Your First Angular Web Application

9

code/first-app/angular-hello-world/src/index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14





AngularHelloWorld





Loading...



Let’s break it down a bit:
code/first-app/angular-hello-world/src/index.html

1
2
3
4
5
6
7
8
9
10





AngularHelloWorld





If you’re familiar with writing HTML files, this first part is straightforward, we’re declaring the core
structure of the HTML document and a few bits of metadata such as page charset, title and base
href.
If we continue to the template body, we see the following:

Writing Your First Angular Web Application

10

code/first-app/angular-hello-world/src/index.html

11
12
13
14


Loading...



The app-root tag is where our application will be rendered. The text Loading... is a placeholder
that will be displayed before our app code loads. For instance, we could put a loading “spinner” img
tag here and the user would see this as our JavaScript and Angular app is loading.
But what is the app-root tag and where does it come from? app-root is a component that is defined
by our Angular application. In Angular we can define our own HTML tags and give them custom
functionality. The app-root tag will be the “entry point” for our application on the page.
Let’s try running this app as-is and then we’ll dig in to see how this component is defined.

Writing Application Code

Running the application
Before making any changes, let’s load our app from the generated application into the browser.
Angular CLI has a built in HTTP server that we can use to run our app.
To use it, head back to the terminal, and change directories into the root of our application.
1
2
3
4
5
6
7

$ cd angular-hello-world
$ ng serve
** NG Live Development Server is running on http://localhost:4200. **
// ...
// a bunch of other messages
// ...
Compiled successfully.

Our application is now running on localhost port 4200. Let’s open the browser and visit:
http://localhost:4200¹⁸
¹⁸http://localhost:4200

11

Writing Your First Angular Web Application

Note that if you get the message:
1

Port 4200 is already in use. Use '--port' to specify a different port

This means that you already have another service running on port 4200. If this is the case
you can either 1. shut down the other service or 2. use the --port flag when running ng
serve like this:
1

ng serve --port 9001

The above command would change the URL you open in your browser to something like:
http://localhost:9001

Another thing to notice is that, on some machines, the domain localhost may not work.
You may see a set of numbers such as 127.0.0.1. When you run ng serve it should show
you what URL the server is running on, so be sure to read the messages on your machine
to find your exact development URL.

Running application

Now that we have the application setup, and we know how to run it, it’s time to start writing some
code.

Writing Your First Angular Web Application

12

Making a Component
One of the big ideas behind Angular is the idea of components.
In our Angular apps, we write HTML markup that becomes our interactive application, but the
browser only understands a limited set of markup tags; Built-ins like 
We’re creating a template that defines two input tags: one for the title of the article and the other for the link URL. When we load the browser you should see the rendered form: 34 Writing Your First Angular Web Application Form Adding Interaction Now we have the form with input tags but we don’t have any way to submit the data. Let’s add some interaction by adding a submit button to our form. When the form is submitted, we’ll want to call a function to create and add a link. We can do this by adding an interaction event on the Now, when the button is clicked, it will call a function called addArticle(), which we need to define on the AppComponent class. Let’s do that now: Writing Your First Angular Web Application 35 code/first-app/angular-reddit/src/app/app.component.ts 8 9 10 11 12 13 export class AppComponent { addArticle(title: HTMLInputElement, link: HTMLInputElement): boolean { console.log(`Adding article title: ${title.value} and link: ${link.value}`); return false; } } With the addArticle() function added to the AppComponent and the (click) event added to the Notice that in the input tags we used the # (hash) to tell Angular to assign those tags to a local variable. By adding the #newtitle and #newlink to the appropriate elements, we can pass them as variables into the addArticle() function on the button! To recap what we’ve done, we’ve made four changes: Writing Your First Angular Web Application 36 1. Created a button tag in our markup that shows the user where to click 2. We created a function named addArticle that defines what we want to do when the button is clicked 3. We added a (click) attribute on the button that says “call the function addArticle when this button is pressed”. 4. We added the attribute #newtitle and #newlink to the tags Let’s cover each one of these steps in reverse order: Binding inputs to values Notice in our first input tag we have the following: 1 This markup tells Angular to bind this to the variable newtitle. The #newtitle syntax is called a resolve. The effect is that this makes the variable newtitle available to the expressions within this view. newtitle is now an object that represents this input DOM element (specifically, the type is HTMLInputElement). Because newtitle is an object, that means we get the value of the input tag using newtitle.value. Similarly we add #newlink to the other tag, so that we’ll be able to extract the value from it as well. Binding actions to events On our button tag we add the attribute (click) to define what should happen when the button is clicked on. When the (click) event happens we call addArticle with two arguments: newtitle and newlink. Where did this function and two arguments come from? 1. addArticle is a function on our component definition class AppComponent 2. newtitle comes from the resolve (#newtitle) on our tag named title 3. newlink comes from the resolve (#newlink) on our tag named link All together: Writing Your First Angular Web Application 1 2 3 4 37 The markup class="ui positive right floated button" comes from Semantic UI and it gives the button the pleasant green color. Defining the Action Logic On our class AppComponent we define a new function called addArticle. It takes two arguments: title and link. Again, it’s important to realize that title and link are both objects of type HTMLInputElement and not the input values directly. To get the value from the input we have to call title.value. For now, we’re just going to console.log out those arguments. code/first-app/angular-reddit/src/app/app.component.ts 9 10 11 12 addArticle(title: HTMLInputElement, link: HTMLInputElement): boolean { console.log(`Adding article title: ${title.value} and link: ${link.value}`); return false; } Notice that we’re using backtick strings again. This is a really handy feature of ES6: backtick strings will expand template variables! Here we’re putting ${title.value} in the string and this will be replaced with the value of title.value in the string. Try it out! Now when you click the submit button, you can see that the message is printed on the console: 38 Writing Your First Angular Web Application Clicking the Button Adding the Article Component Now we have a form to submit new articles, but we aren’t showing the new articles anywhere. Because every article submitted is going to be displayed as a list on the page, this is the perfect candidate for a new component. Let’s create a new component to represent the individual submitted articles. A reddit-article For that, let’s use the ng tool to generate a new component: Writing Your First Angular Web Application 1 39 ng generate component article We have three parts to defining this new component: 1. Define the ArticleComponent view in the template 2. Define the ArticleComponent properties by annotating the class with @Component 3. Define a component-definition class (ArticleComponent) which houses our component logic Let’s talk through each part in detail: Creating the ArticleComponent template We define the template using the file article.component.html: code/first-app/angular-reddit/src/app/article/article.component.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
{{ votes }}
Points
{{ title }}
There’s a lot of markup here, so let’s break it down : A Single reddit-article Row We have two columns: 1. the number of votes on the left and 2. the article information on the right. We specify these columns with the CSS classes four wide column and twelve wide column respectively (remember that these come from SemanticUI’s CSS). We’re showing votes and the title with the template expansion strings {{ votes }} and {{ title }}. The values come from the value of votes and title property of the ArticleComponent class, which we’ll define in a minute. Notice that we can use template strings in attribute values, as in the href of the a tag: href="{{ link }}". In this case, the value of the href will be dynamically populated with the value of link from the component class On our upvote/downvote links we have an action. We use (click) to bind voteUp()/voteDown() to their respective buttons. When the upvote button is pressed, the voteUp() function will be called on the ArticleComponent class (similarly with downvote and voteDown()). Creating the ArticleComponent Writing Your First Angular Web Application 41 code/first-app/angular-reddit/src/app/article/article.component.ts 7 8 9 10 11 @Component({ selector: 'app-article', templateUrl: './article.component.html', styleUrls: ['./article.component.css'], }) First, we define a new Component with @Component. The selector says that this component is placed on the page by using the tag (i.e. the selector is a tag name). So the most essential way to use this component would be to place the following tag in our markup: 1 2 These tags will remain in our view when the page is rendered. Creating the ArticleComponent Definition Class Finally, we create the ArticleComponent definition class: code/first-app/angular-reddit/src/app/article/article.component.ts 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 export class ArticleComponent implements OnInit { @HostBinding('attr.class') cssClass = 'row'; votes: number; title: string; link: string; constructor() { this.title = 'Angular 2'; this.link = 'http://angular.io'; this.votes = 10; } voteUp() { this.votes += 1; } voteDown() { this.votes -= 1; } Writing Your First Angular Web Application 31 32 33 34 35 42 ngOnInit() { } } Here we create four properties on ArticleComponent: 1. 2. 3. 4. cssClass - the CSS class we want to apply to the “host” of this component votes - a number representing the sum of all upvotes, minus the downvotes title - a string holding the title of the article link - a string holding the URL of the article We want each app-article to be on its own row. We’re using Semantic UI, and Semantic provides a CSS class for rows²³ called row. In Angular, a component host is the element this component is attached to. We can set properties on the host element by using the @HostBinding() decorator. In this case, we’re asking Angular to keep the value of the host elements class to be in sync with the property cssClass. We import HostBinding from the package @angular/core. For instance we can add HostBinding like this: 1 import { Component, HostBinding } from '@angular/core'; By using @HostBinding() the host element (the app-article tag) we want to set the class attribute to have “row”. Using the @HostBinding() is nice because it means we can encapsulate the app-article markup within our component. That is, we don’t have to both use an app-article tag and require a class="row" in the markup of the parent view. By using the @HostBinding decorator, we’re able to configure our host element from within the component. In the constructor() we set some default attributes: ²³http://semantic-ui.com/collections/grid.html Writing Your First Angular Web Application 43 code/first-app/angular-reddit/src/app/article/article.component.ts 18 19 20 21 22 constructor() { this.title = 'Angular 2'; this.link = 'http://angular.io'; this.votes = 10; } And we define two functions for voting, one for voting up voteUp and one for voting down voteDown: code/first-app/angular-reddit/src/app/article/article.component.ts 24 25 26 27 28 29 30 voteUp() { this.votes += 1; } voteDown() { this.votes -= 1; } In voteUp we increment this.votes by one. Similarly we decrement for voteDown. Using the app-article Component In order to use this component and make the data visible, we have to add a tag somewhere in our markup. In this case, we want the AppComponent to render this new component, so let’s update the code in that component. Add the tag to the AppComponent’s template right after the closing tag: 1 2 3 4 5 6 7 8 9 10
Writing Your First Angular Web Application 44 If we generated the ArticleComponent using Angular CLI (via ng generate component), by default it should have “told” Angular about our app-article tag (more on that below). However, if we created this component “by hand” and we reload the browser now, we might see that the tag wasn’t compiled. Oh no! Whenever hitting a problem like this, the first thing to do is open up your browser’s developer console. If we inspect our markup (see screenshot below), we can see that the app-article tag is on our page, but it hasn’t been compiled into markup. Why not? Unexpanded tag when inspecting the DOM This happens because the AppComponent component doesn’t know about the ArticleComponent component yet. Angular 1 Note: If you’ve used Angular 1 it might be surprising that our app doesn’t know about our new app-article component. This is because in Angular 1, directives match globally. However, in Angular you need to explicitly specify which components (and therefore, which selectors) you want to use. On the one hand, this requires a little more configuration. On the other hand, it’s great for building scalable apps because it means we don’t have to share our directive selectors in a global namespace. Writing Your First Angular Web Application 45 In order to tell our AppComponent about our new ArticleComponent component, we need to add the ArticleComponent to the list of declarations in this NgModule. We add ArticleComponent to our declarations because ArticleComponent is part of this module (AppModule). However, if ArticleComponent were part of a different module, then we might import it with imports. We’ll discuss more about NgModules later on, but for now, know that when you create a new component, you have to put in a declarations in NgModules. code/first-app/angular-reddit/src/app/app.module.ts 6 7 8 9 10 11 12 13 import { AppComponent } from './app.component'; import { ArticleComponent } from './article/article.component'; @NgModule({ declarations: [ AppComponent, ArticleComponent // <-- added this ], See here that we are: 1. importing ArticleComponent and then 2. Adding ArticleComponent to the list of declarations After you’ve added ArticleComponent to declarations in the NgModule, if we reload the browser we should see the article properly rendered: Writing Your First Angular Web Application 46 Rendered ArticleComponent component However, clicking on the vote up or vote down links will cause the page to reload instead of updating the article list. JavaScript, by default, propagates the click event to all the parent components. Because the click event is propagated to parents, our browser is trying to follow the empty link, which tells the browser to reload. To fix that, we need to make the click event handler to return false. This will ensure the browser won’t try to refresh the page. Let’s update our code so that each of the functions voteUp() and voteDown() return a boolean value of false (tells the browser not to propagate the event upwards): 1 2 3 4 5 voteDown(): boolean { this.votes -= 1; return false; } // and similarly with `voteUp()` Now when we click the links we’ll see that the votes increase and decrease properly without a page refresh. Writing Your First Angular Web Application 47 Rendering Multiple Rows Right now we only have one article on the page and there’s no way to render more, unless we paste another tag. And even if we did that all the articles would have the same content, so it wouldn’t be very interesting. Creating an Article class A good practice when writing Angular code is to try to isolate the data structures we are using from the component code. To do this, let’s create a data structure that represents a single article. Let’s add a new file article.model.ts to define an Article class that we can use. code/first-app/angular-reddit/src/app/article/article.model.ts 1 2 3 4 5 6 7 8 9 10 11 export class Article { title: string; link: string; votes: number; constructor(title: string, link: string, votes?: number) { this.title = title; this.link = link; this.votes = votes || 0; } } Here we are creating a new class that represents an Article. Note that this is a plain class and not an Angular component. In the Model-View-Controller pattern this would be the Model. Each article has a title, a link, and a total for the votes. When creating a new article we need the title and the link. The votes parameter is optional (denoted by the ? at the end of the name) and defaults to zero. Now let’s update the ArticleComponent code to use our new Article class. Instead of storing the properties directly on the ArticleComponent component let’s store the properties on an instance of the Article class. First let’s import the class: code/first-app/angular-reddit/src/app/article/article.component.ts 6 import { Article } from './article.model'; Then let’s use it: Writing Your First Angular Web Application 48 code/first-app/angular-reddit/src/app/article/article.component.ts 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 export class ArticleComponent implements OnInit { @HostBinding('attr.class') cssClass = 'row'; article: Article; constructor() { this.article = new Article( 'Angular 2', 'http://angular.io', 10); } voteUp(): boolean { this.article.votes += 1; return false; } voteDown(): boolean { this.article.votes -= 1; return false; } ngOnInit() { } } Notice what we’ve changed: instead of storing the title, link, and votes properties directly on the component, we’re storing a reference to an article. What’s neat is that we’ve defined the type of article to be our new Article class. When it comes to voteUp (and voteDown), we don’t increment votes on the component, but rather, we need to increment the votes on the article. However, this refactoring introduces another change: we need to update our view to get the template variables from the right location. To do that, we need to change our template tags to read from article. That is, where before we had {{ votes }}, we need to change it to {{ article.votes }}, and same with title and link: Writing Your First Angular Web Application 49 code/first-app/angular-reddit/src/app/article/article.component.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
{{ article.votes }}
Points
Reload the browser and everything still works. This situation is better but something in our code is still off: our voteUp and voteDown methods break the encapsulation of the Article class by changing the article’s internal properties directly. voteUp and voteDown currently break the Law of Demeter²⁴ which says that a given object should assume as little as possible about the structure or properties of other objects. The problem is that our ArticleComponent component knows too much about the Article class ²⁴http://en.wikipedia.org/wiki/Law_of_Demeter Writing Your First Angular Web Application 50 internals. To fix that, let’s add voteUp and voteDown methods on the Article class (we’ll also add a domain function, which we’ll talk about in a moment): code/first-app/angular-reddit/src/app/article/article.model.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 export class Article { title: string; link: string; votes: number; constructor(title: string, link: string, votes?: number) { this.title = title; this.link = link; this.votes = votes || 0; } voteUp(): void { this.votes += 1; } voteDown(): void { this.votes -= 1; } // domain() is a utility function that extracts // the domain from a URL, which we'll explain shortly domain(): string { try { // e.g. http://foo.com/path/to/bar const domainAndPath: string = this.link.split('//')[1]; // e.g. foo.com/path/to/bar return domainAndPath.split('/')[0]; } catch (err) { return null; } } } We can then change ArticleComponent to call these methods: Writing Your First Angular Web Application code/first-app/angular-reddit/src/app/article/article.component.ts 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 export class ArticleComponent implements OnInit { @HostBinding('attr.class') cssClass = 'row'; article: Article; constructor() { this.article = new Article( 'Angular 2', 'http://angular.io', 10); } voteUp(): boolean { this.article.voteUp(); return false; } voteDown(): boolean { this.article.voteDown(); return false; } ngOnInit() { } } Why do we have a voteUp function in both the model and the component? The reason we have a voteUp() and a voteDown() on both classes is because each function does a slightly different thing. The idea is that the voteUp() on the ArticleComponent relates to the component view, whereas the Article model voteUp() defines what mutations happen in the model. That is, it allows the Article class to encapsulate what functionality should happen to a model when voting happens. In a “real” app, the internals of the Article model would probably be more complicated, e.g. make an API request to a webserver, and you wouldn’t want to have that sort of model-specific code in your component controller. Similarly, in the ArticleComponent we return false; as a way to say “don’t propagate the event” - this is a view-specific piece of logic and we shouldn’t allow the Article model’s voteUp() function to have to knowledge about that sort of view-specific API. That is, the Article model should allow voting apart from the specific view. 51 Writing Your First Angular Web Application 52 After reloading our browser, we’ll notice everything works the same way, but we now have clearer, simpler code. Checkout our ArticleComponent component definition now: it’s so short! We’ve moved a lot of logic out of our component and into our models. The corresponding MVC guideline here might be Fat Models, Skinny Controllers²⁵. The idea is that we want to move most of our logic to our models so that our components do the minimum work possible. Storing Multiple Articles Let’s write the code that allows us to have a list of multiple Articles. Let’s start by changing AppComponent to have a collection of articles: code/first-app/angular-reddit/src/app/app.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { Component } from '@angular/core'; import { Article } from './article/article.model'; // <-- import this @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { articles: Article[]; // <-- component property constructor() { this.articles = [ new Article('Angular 2', 'http://angular.io', 3), new Article('Fullstack', 'http://fullstack.io', 2), new Article('Angular Homepage', 'http://angular.io', 1), ]; } Notice that our AppComponent has the line: 1 articles: Article[]; ²⁵http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model Writing Your First Angular Web Application 53 The Article[] might look a little unfamiliar. We’re saying here that articles is an Array of Articles. Another way this could be written is Array
. The word for this pattern is generics. It’s a concept seen in Java, C#, and other languages. The idea is that our collection (the Array) is typed. That is, the Array is a collection that will only hold objects of type Article. In order to have access to the Article class, we first have to import it, as we do up top. We populate this Array by setting this.articles in the constructor: code/first-app/angular-reddit/src/app/app.component.ts 12 13 14 15 16 17 18 constructor() { this.articles = [ new Article('Angular 2', 'http://angular.io', 3), new Article('Fullstack', 'http://fullstack.io', 2), new Article('Angular Homepage', 'http://angular.io', 1), ]; } Configuring the ArticleComponent with inputs Now that we have a list of Article models, how can we pass them to our ArticleComponent component? Here again we use Inputs. Previously we had our ArticleComponent class defined like this: code/first-app/angular-reddit/src/app/article/article.component.ts 13 14 15 16 17 18 19 20 21 22 export class ArticleComponent implements OnInit { @HostBinding('attr.class') cssClass = 'row'; article: Article; constructor() { this.article = new Article( 'Angular 2', 'http://angular.io', 10); } The problem here is that we’ve hard coded a particular Article in the constructor. The point of making components is not only encapsulation, but also reusability. What we would really like to do is to configure the Article we want to display. If, for instance, we had two articles, article1 and article2, we would like to be able to reuse the app-article component by passing an Article as a “parameter” to the component like this: Writing Your First Angular Web Application 1 2 54 Angular allows us to do this by using the Input decorator on a property of a Component: 1 2 3 class ArticleComponent { @Input() article: Article; // ... Now if we have an Article in a variable myArticle we could pass it to our ArticleComponent in our view. Remember, we can pass a variable in an element by surrounding it in square brackets [variableName], like so: 1 Notice the syntax here: we put the name of the input in brackets as in: [article] and the value of the attribute is what we want to pass into that input. Then, and this is important, the this.article on the ArticleComponent instance will be set to myArticle. We can think about the variable myArticle as being passed as a parameter (i.e. input) to our components. Here’s what our ArticleComponent component now looks like using @Input: code/first-app/angular-reddit/src/app/article/article.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { Component, OnInit, Input, // <-- added, HostBinding } from '@angular/core'; import { Article } from './article.model'; // <-- added @Component({ selector: 'app-article', templateUrl: './article.component.html', styleUrls: ['./article.component.css'] }) export class ArticleComponent implements OnInit { @HostBinding('attr.class') cssClass = 'row'; @Input() article: Article; Writing Your First Angular Web Application 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 55 constructor() { // article is populated by the Input now, // so we don't need anything here } voteUp(): boolean { this.article.voteUp(); return false; } voteDown(): boolean { this.article.voteDown(); return false; } ngOnInit() { } } Don’t forget to import! Notice that we import the Input class from @angular/core. We’ve also imported our Article model as we did with the AppComponent earlier. Rendering a List of Articles Earlier we configured our AppComponent to store an array of articles. Now let’s configure AppComponent to render all the articles. To do so, instead of having the tag alone, we are going to use the NgFor directive to iterate over the list of articles and render a app-article for each one: Let’s add this in the template of the AppComponent @Component, just below the closing
tag: Writing Your First Angular Web Application 56 Submit link
Remember when we rendered a list of names as a bullet list using the NgFor directive earlier in the chapter? This syntax also works for rendering multiple components. The *ngFor="let article of articles" syntax will iterate through the list of articles and create the local variable article (for each item in the list). To specify the article input on a component, we are using the [inputName]="inputValue" expression. In this case, we’re saying that we want to set the article input to the value of the local variable article set by ngFor. We are using the variable article many times in that previous code snippet, it’s (potentially) clearer if we rename the temporary variable created by NgFor to foobar: 1 2 3 4 So here we have three variables: 1. articles which is an Array of Articles, defined on the AppComponent 2. foobar which is a single element of articles (an Article), defined by NgFor 3. article which is the name of the field defined on inputs of the ArticleComponent Basically, NgFor generates a temporary variable foobar and then we’re passing it in to app-article Reloading our browser now, we will see all articles will be rendered: Writing Your First Angular Web Application 57 Multiple articles being rendered Adding New Articles Now we need to change addArticle to actually add new articles when the button is pressed. Change the addArticle method to match the following: Writing Your First Angular Web Application 58 code/first-app/angular-reddit/src/app/app.component.ts 20 21 22 23 24 25 26 addArticle(title: HTMLInputElement, link: HTMLInputElement): boolean { console.log(`Adding article title: ${title.value} and link: ${link.value}`); this.articles.push(new Article(title.value, link.value, 0)); title.value = ''; link.value = ''; return false; } This will: 1. create a new Article instance with the submitted title and URL 2. add it to the array of Articles and 3. clear the input field values How are we clearing the input field values? Well, if you recall, title and link are HTMLInputElement objects. That means we can set their properties. When we change the value property, the input tag on our page changes. After adding a new article in our input fields and clicking the Submit Link we will see the new article added! Finishing Touches Displaying the Article Domain As a nice touch, let’s add a hint next to the link that shows the domain where the user will be redirected to when the link is clicked. Let’s add a domain method to the Article class: Writing Your First Angular Web Application 59 code/first-app/angular-reddit/src/app/article/article.model.ts 22 23 24 25 26 27 28 29 30 31 domain(): string { try { // e.g. http://foo.com/path/to/bar const domainAndPath: string = this.link.split('//')[1]; // e.g. foo.com/path/to/bar return domainAndPath.split('/')[0]; } catch (err) { return null; } } Let’s add a call to this function on the ArticleComponent’s template: 1 2 3 4 5 6 7 8 9
{{ article.title }}
({{ article.domain() }})
*ngIf="str == 'yes'">
*ngIf="myFunc()">
displayed if a is more than b --> displayed if str is the string "yes" --> displayed if myFunc returns truthy --> Note for AngularJS 1.x Users If you’ve used AngularJS 1.x, you may have used the ngIf directive before. You can think of the Angular 4 version as a direct substitute. On the other hand, Angular 4 offers no built-in alternative for ng-show. So, if your goal is to just change the CSS visibility of an element, you should look into either the ngStyle or the class directives, described later in this chapter. NgSwitch Sometimes you need to render different elements depending on a given condition. When you run into this situation, you could use ngIf several times like this: 1 2 3 4 5
Var is A
Var is B
Var is something else
But as you can see, the scenario where myVar is neither A nor B is verbose when all we’re trying to express is an else. To illustrate this growth in complexity, say we wanted to handle a new value C. In order to do that, we’d have to not only add the new element with ngIf, but also change the last case: 1 2 3 4 5 6 7
Var is A
Var is B
Var is C
Var is something els\ e
Built-in Directives 121 For cases like this, Angular introduces the ngSwitch directive. If you’re familiar with the switch statement then you’ll feel very at home. The idea behind this directive is the same: allow a single evaluation of an expression, and then display nested elements based on the value that resulted from that evaluation. Once we have the result then we can: • Describe the known results, using the ngSwitchCase directive • Handle all the other unknown cases with ngSwitchDefault Let’s rewrite our example using this new set of directives: 1 2 3 4 5
Var is A
Var is B
Var is something else
Then if we want to handle the new value C we insert a single line: 1 2 3 4 5 6
Var is A
Var is B
Var is C
Var is something else
And we don’t have to touch the default (i.e. fallback) condition. Having the ngSwitchDefault element is optional. If we leave it out, nothing will be rendered when myVar fails to match any of the expected values. You can also declare the same *ngSwitchCase value for different elements, so you’re not limited to matching only a single time. Here’s an example: Built-in Directives 122 code/built-in-directives/src/app/ng-switch-example/ng-switch-example.component.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

Current choice is {{ choice }}

In the example above when the choice is 2, both the second and fifth lis will be rendered. NgStyle With the NgStyle directive, you can set a given DOM element CSS properties from Angular expressions. The simplest way to use this directive is by doing [style.]="value". For example: code/built-in-directives/src/app/ng-style-example/ng-style-example.component.html 5 6 7
Uses fixed yellow background
This snippet is using the NgStyle directive to set the background-color CSS property to the literal string 'yellow'. Another way to set fixed values is by using the NgStyle attribute and using key value pairs for each property you want to set, like this: Built-in Directives code/built-in-directives/src/app/ng-style-example/ng-style-example.component.html 13 14 15
Uses fixed white text on blue background
Notice that in the ng-style specification we have single quotes around background-color but not around color. Why is that? Well, the argument to ng-style is a JavaScript object and color is a valid key, without quotes. With background-color, however, the dash character isn’t allowed in an object key, unless it’s a string so we have to quote it. Generally I’d leave out quoting as much as possible in object keys and only quote keys when we have to. Here we are setting both the color and the background-color properties. But the real power of the NgStyle directive comes with using dynamic values. In our example, we are defining two input boxes with an apply settings button: code/built-in-directives/src/app/ng-style-example/ng-style-example.component.html 56 57 58 59 60 61 62 63 64 65 66 67
And then using their values to set the CSS properties for three elements. On the first one, we’re setting the font size based on the input value: 123 Built-in Directives 124 code/built-in-directives/src/app/ng-style-example/ng-style-example.component.html 21 22 23 24 25
red text
It’s important to note that we have to specify units where appropriate. For instance, it isn’t valid CSS to set a font-size of 12 - we have to specify a unit such as 12px or 1.2em. Angular provides a handy syntax for specifying units: here we used the notation [style.font-size.px]. The .px suffix indicates that we’re setting the font-size property value in pixels. You could easily replace that by [style.font-size.em] to express the font size in ems or even in percentage using [style.font-size.%]. The other two elements use the #colorinput to set the text and background colors: code/built-in-directives/src/app/ng-style-example/ng-style-example.component.html 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

ngStyle with object property from variable

{{ color }} text

style from variable

{{ color }} background
This way, when we click the Apply settings button, we call a method that sets the new values: 125 Built-in Directives code/built-in-directives/src/app/ng-style-example/ng-style-example.component.ts 32 33 34 35 apply(color: string, fontSize: number): void { this.color = color; this.fontSize = fontSize; } And with that, both the color and the font size will be applied to the elements using the NgStyle directive. NgClass The NgClass directive, represented by a ngClass attribute in your HTML template, allows you to dynamically set and change the CSS classes for a given DOM element. The first way to use this directive is by passing in an object literal. The object is expected to have the keys as the class names and the values should be a truthy/falsy value to indicate whether the class should be applied or not. Let’s assume we have a CSS class called bordered that adds a dashed black border to an element: code/built-in-directives/src/styles.css 8 9 10 .bordered { border: 1px dashed black; background-color: #eee; } Let’s add two div elements: one always having the bordered class (and therefore always having the border) and another one never having it: code/built-in-directives/src/app/ng-class-example/ng-class-example.component.html 2 3
This is never bordered
This is always bordered
As expected, this is how those two divs would be rendered: Simple class directive usage Of course, it’s a lot more useful to use the NgClass directive to make class assignments dynamic. To make it dynamic we add a variable as the value for the object value, like this: Built-in Directives code/built-in-directives/src/app/ng-class-example/ng-class-example.component.html 5 6 7
Using object literal. Border {{ isBordered ? "ON" : "OFF" }}
Alternatively, we can define a classesObj object in our component: code/built-in-directives/src/app/ng-class-example/ng-class-example.component.ts 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Component({ selector: 'app-ng-class-example', templateUrl: './ng-class-example.component.html' }) export class NgClassExampleComponent implements OnInit { isBordered: boolean; classesObj: Object; classList: string[]; constructor() { } ngOnInit() { this.isBordered = true; this.classList = ['blue', 'round']; this.toggleBorder(); } toggleBorder(): void { this.isBordered = !this.isBordered; this.classesObj = { bordered: this.isBordered }; } And use the object directly: 126 Built-in Directives 127 code/built-in-directives/src/app/ng-class-example/ng-class-example.component.html 9 10 11
Using object var. Border {{ classesObj.bordered ? "ON" : "OFF" }}
Again, be careful when you have class names that contains dashes, like bordered-box. JavaScript requires that object-literal keys with dashes be quoted like a string, as in: 1
...
We can also use a list of class names to specify which class names should be added to the element. For that, we can either pass in an array literal: code/built-in-directives/src/app/ng-class-example/ng-class-example.component.html 31 32 33 34
This will always have a blue background and round corners
Or assign an array of values to a property in our component: 1 this.classList = ['blue', 'round']; And pass it in: code/built-in-directives/src/app/ng-class-example/ng-class-example.component.html 36 37 38 39
This is {{ classList.indexOf('blue') > -1 ? "" : "NOT" }} blue and {{ classList.indexOf('round') > -1 ? "" : "NOT" }} round
In this last example, the [ngClass] assignment works alongside existing values assigned by the HTML class attribute. The resulting classes added to the element will always be the set of the classes provided by usual class HTML attribute and the result of the evaluation of the [class] directive. In this example: 128 Built-in Directives code/built-in-directives/src/app/ng-class-example/ng-class-example.component.html 31 32 33 34
This will always have a blue background and round corners
The element will have all three classes: base from the class HTML attribute and also blue and round from the [class] assignment: Classes from both the attribute and directive NgFor The role of this directive is to repeat a given DOM element (or a collection of DOM elements) and pass an element of the array on each iteration. The syntax is *ngFor="let item of items". • The let item syntax specifies a (template) variable that’s receiving each element of the items array; • The items is the collection of items from your controller. To illustrate, we can take a look at the code example. We declare an array of cities on our component controller: 1 this.cities = ['Miami', 'Sao Paulo', 'New York']; And then, in our template we can have the following HTML snippet: 129 Built-in Directives code/built-in-directives/src/app/ng-for-example/ng-for-example.component.html 1 2 3 4 5 6 7

Simple list of strings

{{ c }}
And it will render each city inside the div as you would expect: Result of the ngFor directive usage We can also iterate through an array of objects like these: code/built-in-directives/src/app/ng-for-example/ng-for-example.component.ts 17 18 19 20 21 this.people = [ { name: 'Anderson', age: 35, city: 'Sao Paulo' }, { name: 'John', age: 12, city: 'Miami' }, { name: 'Peter', age: 22, city: 'New York' } ]; And then render a table based on each row of data: code/built-in-directives/src/app/ng-for-example/ng-for-example.component.html 9 10 11 12 13 14 15 16 17

List of objects

130 Built-in Directives 18 19 20 21 22 23 24 25 26
Name AgeCity
{{ p.name }} {{ p.age }} {{ p.city }}
Getting the following result: Rendering array of objects We can also work with nested arrays. If we wanted to have the same table as above, broken down by city, we could easily declare a new array of objects: code/built-in-directives/src/app/ng-for-example/ng-for-example.component.ts 22 23 24 25 26 27 28 29 30 31 32 33 34 this.peopleByCity = [ { city: 'Miami', people: [ { name: 'John', age: 12 }, { name: 'Angel', age: 22 } ] }, { city: 'Sao Paulo', people: [ { name: 'Anderson', age: 35 }, { name: 'Felipe', age: 36 } Built-in Directives 35 36 37 38 ] } ]; } And then we could use NgFor to render one h2 for each city: code/built-in-directives/src/app/ng-for-example/ng-for-example.component.html 32 33

{{ item.city }}

And use a nested directive to iterate through the people for a given city: code/built-in-directives/src/app/ng-for-example/ng-for-example.component.html 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Name Age City
{{ p.name }} {{ p.age }} {{ p.city }}
Resulting in the following template code: 131 Built-in Directives code/built-in-directives/src/app/ng-for-example/ng-for-example.component.html 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

Nested data

{{ item.city }}

Name Age
{{ p.name }} {{ p.age }}
And it would render one table for each city: 132 133 Built-in Directives Rendering nested arrays Getting an index There are times that we need the index of each item when we’re iterating an array. We can get the index by appending the syntax let idx = index to the value of our ngFor directive, separated by a semi-colon. When we do this, ng2 will assign the current index into the variable we provide (in this case, the variable idx). Note that, like JavaScript, the index is always zero based. So the index for first element is 0, 1 for the second and so on… Making some changes to our first example, adding the let num = index snippet like below: code/built-in-directives/src/app/ng-for-example/ng-for-example.component.html 53 54 55
{{ num+1 }} - {{ c }}
134 Built-in Directives It will add the position of the city before the name, like this: Using an index NgNonBindable We use ngNonBindable when we want tell Angular not to compile or bind a particular section of our page. Let’s say we want to render the literal text {{ content }} in our template. Normally that text will be bound to the value of the content variable because we’re using the {{ }} template syntax. So how can we render the exact text {{ content }}? We use the ngNonBindable directive. Let’s say we want to have a div that renders the contents of that content variable and right after we want to point that out by outputting <- this is what {{ content }} rendered next to the actual value of the variable. To do that, here’s the template we’d have to use: code/built-in-directives/src/app/ng-non-bindable-example/ng-non-bindable-example.component.html 1 2 3 4 5 6
{{ content }} ← This is what {{ content }} rendered
And with that ngNonBindable attribute, ng2 will not compile within that second span’s context, leaving it intact: Result of using ngNonBindable Built-in Directives 135 Conclusion Angular has only a few core directives, but we can combine these simple pieces to create dynamic, powerful apps. However, all of these directives help us output dynamic data, they don’t let us accept user interaction. In the next chapter we’ll learn how to let our user input data using forms. Forms in Angular Forms are Crucial, Forms are Complex Forms are probably the most crucial aspect of your web application. While we often get events from clicking on links or moving the mouse, it’s through forms where we get the majority of our rich data input from users. On the surface, forms seem straightforward: you make an input tag, the user fills it out, and hits submit. How hard could it be? It turns out, forms can be very complex. Here’s a few reasons why: • • • • • • Form inputs are meant to modify data, both on the page and the server Changes often need to be reflected elsewhere on the page Users have a lot of leeway in what they enter, so you need to validate values The UI needs to clearly state expectations and errors, if any Dependent fields can have complex logic We want to be able to test our forms, without relying on DOM selectors Thankfully, Angular has tools to help with all of these things. • FormControls encapsulate the inputs in our forms and give us objects to work with them • Validators give us the ability to validate inputs, any way we’d like • Observers let us watch our form for changes and respond accordingly In this chapter we’re going to walk through building forms, step by step. We’ll start with some simple forms and build up to more complicated logic. FormControls and FormGroups The two fundamental objects in Angular forms are FormControl and FormGroup. FormControl A FormControl represents a single input field - it is the smallest unit of an Angular form. FormControls encapsulate the field’s value, and states such as being valid, dirty (changed), or has errors. For instance, here’s how we might use a FormControl in TypeScript: 137 Forms in Angular 1 2 3 4 5 6 7 8 9 10 // create a new FormControl with the value "Nate" let nameControl = new FormControl("Nate"); let name = nameControl.value; // -> Nate // now we can query this nameControl.errors // -> nameControl.dirty // -> nameControl.valid // -> // etc. control for certain values: StringMap of errors false true To build up forms we create FormControls (and groups of FormControls) and then attach metadata and logic to them. Like many things in Angular, we have a class (FormControl, in this case) that we attach to the DOM with an attribute (formControl, in this case). For instance, we might have the following in our form: 1 2 This will create a new FormControl object within the context of our form. We’ll talk more about how that works below. FormGroup Most forms have more than one field, so we need a way to manage multiple FormControls. If we wanted to check the validity of our form, it’s cumbersome to iterate over an array of FormControls and check each FormControl for validity. FormGroups solve this issue by providing a wrapper interface around a collection of FormControls. Here’s how you create a FormGroup: 1 2 3 4 5 let personInfo = new FormGroup({ firstName: new FormControl("Nate"), lastName: new FormControl("Murray"), zip: new FormControl("90210") }) FormGroup and FormControl have a common ancestor (AbstractControl⁴³). That means we can check the status or value of personInfo just as easily as a single FormControl: ⁴³https://angular.io/docs/ts/latest/api/forms/index/AbstractControl-class.html 138 Forms in Angular 1 2 3 4 5 6 7 8 9 10 11 12 personInfo.value; // -> { // firstName: "Nate", // lastName: "Murray", // zip: "90210" // } // now we can query this control group for certain values, which have sensible // values depending on the children FormControl's values: personInfo.errors // -> StringMap of errors personInfo.dirty // -> false personInfo.valid // -> true // etc. Notice that when we tried to get the value from the FormGroup we received an object with key-value pairs. This is a really handy way to get the full set of values from our form without having to iterate over each FormControl individually. Our First Form There are lots of moving pieces to create a form, and several important ones we haven’t touched on. Let’s jump in to a full example and I’ll explain each piece as we go along. You can find the full code listing for this section in the code download under forms/ Here’s a screenshot of the very first form we’re going to build: Demo Form with Sku: Simple Version In our imaginary application we’re creating an e-commerce-type site where we’re listing products for sale. In this app we need to store the product’s SKU, so let’s create a simple form that takes the SKU as the only input field. 139 Forms in Angular SKU is an abbreviation for “stockkeeping unit”. It’s a term for a unique id for a product that is going to be tracked in inventory. When we talk about a SKU, we’re talking about a human-readable item ID. Our form is super simple: we have a single input for sku (with a label) and a submit button. Let’s turn this form into a Component. If you recall, there are three parts to defining a component: • Configure the @Component() decorator • Create the template • Implement custom functionality in the component definition class Let’s take these in turn: Loading the FormsModule In order to use the new forms library we need to first make sure we import the forms library in our NgModule. There are two ways of using forms in Angular and we’ll talk about them both in this chapter: using FormsModule or using ReactiveFormsModule. Since we’ll use both, we’ll import them both into our module. To do this we do the following in our app.ts where we bootstrap the app: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import { FormsModule, ReactiveFormsModule } from '@angular/forms'; // farther down... @NgModule({ declarations: [ FormsDemoApp, DemoFormSkuComponent, // ... our declarations ], imports: [ BrowserModule, FormsModule, // ReactiveFormsModule // ], bootstrap: [ FormsDemoApp }) class FormsDemoAppModule {} here <-- add this <-- and this ] Forms in Angular 140 This ensures that we’re able to use the form directives in our views. At the risk of jumping ahead, the FormsModule gives us template driven directives such as: • ngModel and • NgForm Whereas ReactiveFormsModule gives us directives like • formControl and • ngFormGroup … and several more. We haven’t talked about how to use these directives or what they do, but we will shortly. For now, just know that by importing FormsModule and ReactiveFormsModule into our NgModule means we can use any of the directives in that list in our view template or inject any of their respective providers into our components. Simple SKU Form: @Component Decorator Now we can start creating our component: code/forms/src/app/demo-form-sku/demo-form-sku.component.ts 1 2 3 4 5 import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-demo-form-sku', templateUrl: './demo-form-sku.component.html', Here we define a selector of app-demo-form-sku. If you recall, selector tells Angular what elements this component will bind to. In this case we can use this component by having a appdemo-form-sku tag like so: 1 Simple SKU Form: template Let’s look at our template: Forms in Angular 141 code/forms/src/app/demo-form-sku/demo-form-sku.component.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Demo Form: Sku

form & NgForm Now things get interesting: because we imported FormsModule, that makes NgForm available to our view. Remember that whenever we make directives available to our view, they will get attached to any element that matches their selector. NgForm does something handy but non-obvious: it includes the form tag in its selector (instead of requiring you to explicitly add ngForm as an attribute). What this means is that if you import FormsModule, NgForm will get automatically attached to any
tags you have in your view. This is really useful but potentially confusing because it happens behind the scenes. There are two important pieces of functionality that NgForm gives us: 1. A FormGroup named ngForm 2. A (ngSubmit) output You can see that we use both of these in the tag in our view: Forms in Angular 142 code/forms/src/app/demo-form-sku/demo-form-sku.component.html 3 4 tags (because of the default NgForm selector), which means we don’t have to add an ngForm attribute to use NgForm. But here we’re putting ngForm in an attribute (value) tag. Is this a typo? No, it’s not a typo. If ngForm were the key of the attribute then we would be telling Angular that we want to use NgForm on this attribute. In this case, we’re using ngForm as the attribute when we’re assigning a reference. That is, we’re saying the value of the evaluated expression ngForm should be assigned to a local template variable f. ngForm is already on this element and you can think of it as if we are “exporting” this FormGroup so that we can reference it elsewhere in our view. We bind to the ngSubmit action of our form by using the syntax: (ngSubmit)="onSubmit(f.value)". • (ngSubmit) - comes from NgForm • onSubmit() - will be implemented in our component definition class (below) • f.value - f is the FormGroup that we specified above. And .value will return the key/value pairs of this FormGroup Put it all together and that line says “when I submit the form, call onSubmit on my component instance, passing the value of the form as the arguments”. input & NgModel Our input tag has a few things we should touch on before we talk about NgModel: Forms in Angular 143 code/forms/src/app/demo-form-sku/demo-form-sku.component.html 3 4 5 6 7 8 9 10 11 12 13
• class="ui form" and class="field" - these classes are totally optional. They come from the CSS framework Semantic UI⁴⁴. I’ve added them in some of our examples just to give them a nice coat of CSS but they’re not part of Angular. • The label “for” attribute and the input “id” attribute are to match, as per W3C standard⁴⁵ • We set a placeholder of “SKU”, which is just a hint to the user for what this input should say when it is blank The NgModel directive specifies a selector of ngModel. This means we can attach it to our input tag by adding this sort of attribute: ngModel="whatever". In this case, we specify ngModel with no attribute value. There are a couple of different ways to specify ngModel in your templates and this is the first. When we use ngModel with no attribute value we are specifying: 1. a one-way data binding 2. we want to create a FormControl on this form with the name sku (because of the name attribute on the input tag) NgModel creates a new FormControl that is automatically added to the parent FormGroup (in this case, on the form) and then binds a DOM element to that new FormControl. That is, it sets up an association between the input tag in our view and the FormControl and the association is matched by a name, in this case "sku". ⁴⁴http://semantic-ui.com/ ⁴⁵http://www.w3.org/TR/WCAG20-TECHS/H44.html Forms in Angular 144 NgModel vs. ngModel: what’s the difference? Generally, when we use PascalCase, like NgModel, we’re specifying the class and referring to the object as it’s defined in code. The lower case (CamelCase), as in ngModel, comes from the selector of the directive and it’s only used in the DOM / template. It’s also worth pointing out that NgModel and FormControl are separate objects. NgModel is the directive that you use in your view, whereas FormControl is the object used for representing the data and validations in your form. Sometimes we want to do two-way binding with ngModel like we used to do in Angular 1. We’ll look at how to do that towards the end of this chapter. Simple SKU Form: Component Definition Class Now let’s look at our class definition: code/forms/src/app/demo-form-sku/demo-form-sku.component.ts 8 9 10 11 12 13 14 15 16 17 18 19 export class DemoFormSkuComponent implements OnInit { constructor() { } ngOnInit() { } onSubmit(form: any): void { console.log('you submitted value:', form); } } Here our class defines one function: onSubmit. This is the function that is called when the form is submitted. For now, we’ll just console.log out the value that is passed in. Try it out! Putting it all together, here’s what our code listing looks like: Forms in Angular code/forms/src/app/demo-form-sku/demo-form-sku.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-demo-form-sku', templateUrl: './demo-form-sku.component.html', styles: [] }) export class DemoFormSkuComponent implements OnInit { constructor() { } ngOnInit() { } onSubmit(form: any): void { console.log('you submitted value:', form); } } and the template: code/forms/src/app/demo-form-sku/demo-form-sku.component.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Demo Form: Sku

145 146 Forms in Angular If we try this out in our browser, here’s what it looks like: Demo Form with Sku: Simple Version, Submitted Using FormBuilder Building our FormControls and FormGroups implicitly using ngForm and ngControl is convenient, but doesn’t give us a lot of customization options. A more flexible and common way to configure forms is to use a FormBuilder. FormBuilder is an aptly-named helper class that helps us build forms. As you recall, forms are made up of FormControls and FormGroups and the FormBuilder helps us make them (you can think of it as a “factory” object). Let’s add a FormBuilder to our previous example. Let’s look at: • how to use the FormBuilder in our component definition class • how to use our custom FormGroup on a form in the view Forms in Angular 147 Reactive Forms with FormBuilder For this component we’re going to be using the formGroup and formControl directives which means we need to import the appropriate classes. We start by importing them like so: code/forms/src/app/demo-form-sku-with-builder/demo-form-sku-with-builder.component.ts 1 2 3 4 5 import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; Using FormBuilder We inject FormBuilder by creating an argument in the constructor of our component class: What does inject mean? We haven’t talked much about dependency injection (DI) or how DI relates to the hierarchy tree, so that last sentence may not make a lot of sense. We talk a lot more about dependency injection in the Dependency Injection chapter, so go there if you’d like to learn more about it in depth. At a high level, Dependency Injection is a way to tell Angular what dependencies this component needs to function properly. code/forms/src/app/demo-form-sku-with-builder/demo-form-sku-with-builder.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; @Component({ selector: 'app-demo-form-sku-with-builder', templateUrl: './demo-form-sku-with-builder.component.html', styles: [] }) export class DemoFormSkuWithBuilderComponent implements OnInit { myForm: FormGroup; constructor(fb: FormBuilder) { this.myForm = fb.group({ Forms in Angular 17 18 19 20 21 22 23 24 25 26 27 28 148 'sku': ['ABC123'] }); } ngOnInit() { } onSubmit(value: string): void { console.log('you submitted value: ', value); } } During injection an instance of FormBuilder will be created and we assign it to the fb variable (in the constructor). There are two main functions we’ll use on FormBuilder: • control - creates a new FormControl • group - creates a new FormGroup Notice that we’ve setup a new instance variable called myForm on this class. (We could have just as easily called it form, but I want to differentiate between our FormGroup and the form we had before.) myForm is typed to be a FormGroup. We create a FormGroup by calling fb.group(). .group takes an object of key-value pairs that specify the FormControls in this group. In this case, we’re setting up one control sku, and the value is ["ABC123"] - this says that the default value of this control is "ABC123". (You’ll notice that is an array. That’s because we’ll be adding more configuration options there later.) Now that we have myForm we need to use that in the view (i.e. we need to bind it to our form element). Using myForm in the view We want to change our
to use myForm. If you recall, in the last section we said that ngForm is applied for us automatically when we use FormsModule. We also mentioned that ngForm creates its own FormGroup. Well, in this case, we don’t want to use an outside FormGroup. Instead we want to use our instance variable myForm, which we created with our FormBuilder. How can we do that? Angular provides another directive that we use when we have an existing FormGroup: it’s called formGroup and we use it like this: Forms in Angular 149 code/forms/src/app/demo-form-sku-with-builder/demo-form-sku-with-builder.component.html 2 3

Demo Form: Sku with Builder

element? There is an exception: NgForm won’t be applied to a that has formGroup. If you’re curious, the selector for NgForm is: 1 form:not([ngNoForm]):not([formGroup]),ngForm,[ngForm] This means you could have a form that doesn’t get NgForm applied by using the ngNoForm attribute. We also need to change onSubmit to use myForm instead of f, because now it is myForm that has our configuration and values. There’s one last thing we need to do to make this work: bind our FormControl to the input tag. Remember that ngControl creates a new FormControl object, and attaches it to the parent FormGroup. But in this case, we used FormBuilder to create our own FormControls. When we want to bind an existing FormControl to an input we use formControl: code/forms/src/app/demo-form-sku-with-builder/demo-form-sku-with-builder.component.html 8 9 10 11 12 Here we are instructing the formControl directive to look at myForm.controls and use the existing sku FormControl for this input. Try it out! Here’s what it looks like all together: Forms in Angular code/forms/src/app/demo-form-sku-with-builder/demo-form-sku-with-builder.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; @Component({ selector: 'app-demo-form-sku-with-builder', templateUrl: './demo-form-sku-with-builder.component.html', styles: [] }) export class DemoFormSkuWithBuilderComponent implements OnInit { myForm: FormGroup; constructor(fb: FormBuilder) { this.myForm = fb.group({ 'sku': ['ABC123'] }); } ngOnInit() { } onSubmit(value: string): void { console.log('you submitted value: ', value); } } and the template: code/forms/src/app/demo-form-sku-with-builder/demo-form-sku-with-builder.component.html 1 2 3 4 5 6 7 8

Demo Form: Sku with Builder

150 Forms in Angular 9 10 11 12 13 14 15 16 17 151
Remember: To create a new FormGroup and FormControls implicitly use: • ngForm and • ngModel But to bind to an existing FormGroup and FormControls use: • formGroup and • formControl Adding Validations Our users aren’t always going to enter data in exactly the right format. If someone enters data in the wrong format, we want to give them feedback and not allow the form to be submitted. For this we use validators. Validators are provided by the Validators module and the simplest validator is Validators.required which simply says that the designated field is required or else the FormControl will be considered invalid. To use validators we need to do two things: 1. Assign a validator to the FormControl object 2. Check the status of the validator in the view and take action accordingly To assign a validator to a FormControl object we simply pass it as the second argument to our FormControl constructor: Forms in Angular 1 152 let control = new FormControl('sku', Validators.required); Or in our case, because we’re using FormBuilder we will use the following syntax: code/forms/src/app/demo-form-with-validations-explicit/demo-form-with-validations-explicit.component.ts 18 19 20 21 22 23 24 constructor(fb: FormBuilder) { this.myForm = fb.group({ 'sku': ['', Validators.required] }); this.sku = this.myForm.controls['sku']; } Now we need to use our validation in the view. There are two ways we can access the validation value in the view: 1. We can explicitly assign the FormControl sku to an instance variable of the class - which is more verbose, but gives us easy access to the FormControl in the view. 2. We can lookup the FormControl sku from myForm in the view. This requires less work in the component definition class, but is slightly more verbose in the view. To make this difference clearer, let’s look at this example both ways: Explicitly setting the sku FormControl as an instance variable Here’s a screenshot of what our form is going to look like with validations: 153 Forms in Angular Demo Form with Validations The most flexible way to deal with individual FormControls in your view is to set each FormControl up as an instance variable in your component definition class. Here’s how we could setup sku in our class: code/forms/src/app/demo-form-with-validations-explicit/demo-form-with-validations-explicit.component.ts 14 15 16 17 18 19 20 21 22 23 24 25 export class DemoFormWithValidationsExplicitComponent { myForm: FormGroup; sku: AbstractControl; constructor(fb: FormBuilder) { this.myForm = fb.group({ 'sku': ['', Validators.required] }); this.sku = this.myForm.controls['sku']; } Forms in Angular 26 27 28 29 30 154 onSubmit(value: string): void { console.log('you submitted value: ', value); } } Notice that: 1. We setup sku: AbstractControl at the top of the class and 2. We assign this.sku after we’ve created myForm with the FormBuilder This is great because it means we can reference sku anywhere in our component view. The downside is that by doing it this way, we’d have to setup an instance variable for every field in our form. For large forms, this can get pretty verbose. Now that we have our sku being validated, I want to look at four different ways we can use it in our view: 1. 2. 3. 4. Checking the validity of our whole form and displaying a message Checking the validity of our individual field and displaying a message Checking the validity of our individual field and coloring the field red if it’s invalid Checking the validity of our individual field on a particular requirement and displaying a message Form message We can check the validity of our whole form by looking at myForm.valid: code/forms/src/app/demo-form-with-validations-explicit/demo-form-with-validations-explicit.component.html 20
SKU is invalid
it will show the input tag with a red border. To do this, we can use the property syntax to set conditional classes: code/forms/src/app/demo-form-with-validations-explicit/demo-form-with-validations-explicit.component.html 7 8
Notice here that we have two conditions for setting the .error class: We’re checking for !sku.valid and sku.touched. The idea here is that we only want to show the error state if the user has tried editing the form (“touched” it) and it’s now invalid. To try this out, enter some data into the input tag and then delete the contents of the field. Specific validation A form field can be invalid for many reasons. We often want to show a different message depending on the reason for a failed validation. To look up a specific validation failure we use the hasError method: code/forms/src/app/demo-form-with-validations-explicit/demo-form-with-validations-explicit.component.html 17 18
SKU is required
Note that hasError is defined on both FormControl and FormGroup. This means you can pass a second argument of path to lookup a specific field from FormGroup. For example, we could have written the previous example as: Forms in Angular 1 2 156
SKU is required
Putting it together Here’s the full code listing of our form with validations with the FormControl set as an instance variable: code/forms/src/app/demo-form-with-validations-explicit/demo-form-with-validations-explicit.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import { Component } from '@angular/core'; import { FormBuilder, FormGroup, Validators, AbstractControl } from '@angular/forms'; @Component({ selector: 'app-demo-form-with-validations-explicit', templateUrl: './demo-form-with-validations-explicit.component.html', styles: [] }) export class DemoFormWithValidationsExplicitComponent { myForm: FormGroup; sku: AbstractControl; constructor(fb: FormBuilder) { this.myForm = fb.group({ 'sku': ['', Validators.required] }); this.sku = this.myForm.controls['sku']; } onSubmit(value: string): void { console.log('you submitted value: ', value); } } And the template: Forms in Angular 157 code/forms/src/app/demo-form-with-validations-explicit/demo-form-with-validations-explicit.component.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

Demo Form: with validations (explicit)

SKU is invalid
SKU is required
Form is invalid
Removing the sku instance variable In the example above we set sku: AbstractControl as an instance variable. We often won’t want to create an instance variable for each AbstractControl, so how would we reference this FormControl in our view without an instance variable? Instead we can use the myForm.controls property as in: Forms in Angular 158 code/forms/src/app/demo-form-with-validations-shorthand/demo-form-with-validations-shorthand.component.html 10 11 12 13 14 15 16 17
SKU is invalid
{ return isBlank(c.value) || c.value == "" ? {"required": true} : null; } A validator: - Takes a FormControl as its input and - Returns a StringMap where the key is “error code” and the value is true if it fails Writing the Validator Let’s say we have specific requirements for our sku. For example, say our sku needs to begin with 123. We could write a validator like so: Forms in Angular 159 code/forms/src/app/demo-form-with-custom-validation/demo-form-with-custom-validation.component.ts 18 19 20 21 22 function skuValidator(control: FormControl): { [s: string]: boolean } { if (!control.value.match(/^123/)) { return {invalidSku: true}; } } This validator will return an error code invalidSku if the input (the control.value) does not begin with 123. Assigning the Validator to the FormControl Now we need to add the validator to our FormControl. However, there’s one small problem: we already have a validator on sku. How can we add multiple validators to a single field? For that, we use Validators.compose: code/forms/src/app/demo-form-with-custom-validation/demo-form-with-custom-validation.component.ts 33 34 35 36 37 constructor(fb: FormBuilder) { this.myForm = fb.group({ 'sku': ['', Validators.compose([ Validators.required, skuValidator])] }); Validators.compose wraps our two validators and lets us assign them both to the FormControl. The FormControl is not valid unless both validations are valid. Now we can use our new validator in the view: code/forms/src/app/demo-form-with-custom-validation/demo-form-with-custom-validation.component.html 19 20
SKU must begin with 123
Note that in this section, I’m using “explicit” notation of adding an instance variable for each FormControl. That means that in the view in this section, sku refers to a FormControl. If you run the sample code, one neat thing you’ll notice is that if you type something in to the field, the required validation will be fulfilled, but the invalidSku validation may not. This is great - it means we can partially-validate our fields and show the appropriate messages. Forms in Angular 160 Watching For Changes So far we’ve only extracted the value from our form by calling onSubmit when the form is submitted. But often we want to watch for any value changes on a control. Both FormGroup and FormControl have an EventEmitter that we can use to observe changes. EventEmitter is an Observable, which means it conforms to a defined specification for watching for changes. If you’re interested in the Observable spec, you can find it here⁴⁶ To watch for changes on a control we: 1. get access to the EventEmitter by calling control.valueChanges. Then we 2. add an observer using the .subscribe method Here’s an example: code/forms/src/app/demo-form-with-events/demo-form-with-events.component.ts 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 constructor(fb: FormBuilder) { this.myForm = fb.group({ 'sku': ['', Validators.required] }); this.sku = this.myForm.controls['sku']; this.sku.valueChanges.subscribe( (value: string) => { console.log('sku changed to:', value); } ); this.myForm.valueChanges.subscribe( (form: any) => { console.log('form changed to:', form); } ); } ⁴⁶https://github.com/jhusain/observable-spec Forms in Angular 161 Here we’re observing two separate events: changes on the sku field and changes on the form as a whole. The observable that we pass in is an object with a single key: next (there are other keys you can pass in, but we’re not going to worry about those now). next is the function we want to call with the new value whenever the value changes. If we type ‘kj’ into the text box we will see in our console: 1 2 3 4 sku changed to: k form changed to: Object {sku: "k"} sku changed to: kj form changed to: Object {sku: "kj"} As you can see each keystroke causes the control to change, so our observable is triggered. When we observe the individual FormControl we receive a value (e.g. kj), but when we observe the whole form, we get an object of key-value pairs (e.g. {sku: "kj"}). ngModel NgModel is a special directive: it binds a model to a form. ngModel is special in that it implements two-way data binding. Two-way data binding is almost always more complicated and difficult to reason about vs. one-way data binding. Angular is built to generally have data flow one-way: topdown. However, when it comes to forms, there are times where it is easier to opt-in to a two-way bind. Just because you’ve used ng-model in Angular 1 in the past, don’t rush to use ngModel right away. There are good reasons to avoid two-way data binding⁴⁷. Of course, ngModel can be really handy, but know that we don’t necessarily rely on two-way data binding as much as we did in Angular 1. Let’s change our form a little bit and say we want to input productName. We’re going to use ngModel to keep the component instance variable in sync with the view. First, here’s our component definition class: ⁴⁷https://www.quora.com/Why-is-the-two-way-data-binding-being-dropped-in-Angular-2 Forms in Angular 162 code/forms/src/app/demo-form-ng-model/demo-form-ng-model.component.ts 12 13 14 15 16 17 18 19 20 21 22 23 24 25 export class DemoFormNgModelComponent { myForm: FormGroup; productName: string; constructor(fb: FormBuilder) { this.myForm = fb.group({ 'productName': ['', Validators.required] }); } onSubmit(value: string): void { console.log('you submitted value: ', value); } } Notice that we’re simply storing productName: string as an instance variable. Next, let’s use ngModel on our input tag: code/forms/src/app/demo-form-ng-model/demo-form-ng-model.component.html 13 14 15 16 17 18 Now notice something - the syntax for ngModel is funny: we are using both brackets and parentheses around the ngModel attribute! The idea this is intended to invoke is that we’re using both the input [] brackets and the output () parentheses. It’s an indication of the two-way bind. Notice something else here: we’re still using formControl to specify that this input should be bound to the FormControl on our form. We do this because ngModel is only binding the input to the instance variable - the FormControl is completely separate. But because we still want to validate this value and submit it as part of the form, we keep the formControl directive. Last, let’s display our productName value in the view: 163 Forms in Angular code/forms/src/app/demo-form-ng-model/demo-form-ng-model.component.html 4 5 6
The product name is: {{productName}}
Here’s what it looks like: Demo Form with ngModel Easy! Wrapping Up Forms have a lot of moving pieces, but Angular makes it fairly straightforward. Once you get a handle on how to use FormGroups, FormControls, and Validations, it’s pretty easy going from there! Dependency Injection As our programs grow in size, parts of the app need to communicate with other modules. When module A requires module B to run, we say that B is a dependency of A. One of the most common ways to get access to dependencies is to simply import a file. For instance, in this hypothetical module we might do the following: 1 2 3 4 // in A.ts import {B} from 'B'; // a dependency! B.foo(); // using B In many cases, simply importing code is sufficient, but other times we need to provide dependencies in a more sophisticated way. For instance, we may want to: • substitute out the implementation of B for MockB during testing • share a single instance of the B class across our whole app (e.g. the Singleton pattern) • create a new instance of the B class every time it is used (e.g. the Factory pattern) Dependency Injection can solve these problems. Dependency Injection (DI) is a system to make parts of our program accessible to other parts of the program - and we can configure how that happens. One way to think about “the injector” is as a replacement for the new operator. That is, instead of using the language-provided new operator, Dependency Injection let’s us configure how objects are created. The term Dependency Injection is used to describe both a design pattern (used in many different frameworks) and also the specific implementation of DI that is built-in to Angular. The major benefit of using Dependency Injection is that the client component needn’t be aware of how to create the dependencies. All the client component needs to know is how to interact with those dependencies. This is all very abstract, so let’s dive in to some code. Dependency Injection 165 How to use this chapter This chapter is a tour of Angular DI system and concepts. You can find the code for this chapter in code/dependency-injection. While reading this chapter, run the demo project by changing into the project directory and running: 1 2 npm install npm start As a preview, to get Dependency Injection to work involves configuration in your NgModules. It can feel a bit confusing at first to figure out “where” things are coming from. The example code has full, runnable examples with all of the context. So if you feel lost, we’d encourage you to checkout the sample code alongside reading this chapter. Injections Example: PriceService Let’s imagine we’re building a store that has Products and we need to calculate the final price of that product after sales tax. In order to calculate the full price for this product, we use a PriceService that takes as input: • the base price of the Product and • the state we’re selling it to. and then returns the final price of the Product, plus tax: code/dependency-injection/src/app/price-service-demo/price.service.1.ts 1 2 3 4 5 6 7 8 9 10 11 12 export class PriceService { constructor() { } calculateTotalPrice(basePrice: number, state: string) { // e.g. Imgine that in our "real" application we're // accessing a real database of state sales tax amounts const tax = Math.random(); return basePrice + tax; } } Dependency Injection 166 In this service, the calculateTotalPrice function will take the basePrice of a product and the state and return the total price of product. Say we want to use this service on our Product model. Here’s how it could look without dependency injection: code/dependency-injection/src/app/price-service-demo/product.model.1.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { PriceService } from './price.service'; export class Product { service: PriceService; basePrice: number; constructor(basePrice: number) { this.service = new PriceService(); // <-- create directly ("hardcoded") this.basePrice = basePrice; } totalPrice(state: string) { return this.service.calculateTotalPrice(this.basePrice, state); } } Now imagine we need to write a test for this Product class. We could write a test like this: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { Product } from './product'; describe('Product', () => { let product; beforeEach(() => { product = new Product(11); }); describe('price', () => { it('is calculated based on the basePrice and the state', () => { expect(product.totalPrice('FL')).toBe(11.66); // <-- hmmm }); }) }); Dependency Injection 167 The problem with this test is that we don’t actually know what the exact value for tax in Florida ('FL') is going to be. Even if we implemented the PriceService the ‘real’ way by calling an API or calling a database, we have the problem that: • The API needs to be available (or the database needs to be running) and • We need to know the exact Florida tax at the time we write the test. What should we do if we want to test the price method of the Product without relying on this external resource? In this case we often mock the PriceService. For example, if we know the interface of a PriceService, we could write a MockPriceService which will always give us a predictable calculation (and not be reliant on a database or API). Here’s the interface for IPriceService: code/dependency-injection/src/app/price-service-demo/price-service.interface.ts 1 2 3 export interface IPriceService { calculateTotalPrice(basePrice: number, state: string): number; } This interface defines just one function: calculateTotalPrice. Now we can write a MockPriceService that conforms to this interface, which we will use only for our tests: code/dependency-injection/src/app/price-service-demo/price.service.mock.ts 1 2 3 4 5 6 7 8 9 10 11 import { IPriceService } from './price-service.interface'; export class MockPriceService implements IPriceService { calculateTotalPrice(basePrice: number, state: string) { if (state === 'FL') { return basePrice + 0.66; // it's always 66 cents! } return basePrice; } } Now, just because we’ve written a MockPriceService doesn’t mean our Product will use it. In order to use this service, we need to modify our Product class: Dependency Injection 168 code/dependency-injection/src/app/price-service-demo/product.model.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { IPriceService } from './price-service.interface'; export class Product { service: IPriceService; basePrice: number; constructor(service: IPriceService, basePrice: number) { this.service = service; // <-- passed in as an argument! this.basePrice = basePrice; } totalPrice(state: string) { return this.service.calculateTotalPrice(this.basePrice, state); } } Now, when creating a Product the client using the Product class becomes responsible for deciding which concrete implementation of the PriceService is going to be given to the new instance. And with this change, we can tweak our test slightly and get rid of the dependency on the unpredictable PriceService: code/dependency-injection/src/app/price-service-demo/product.spec.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { Product } from './product.model'; import { MockPriceService } from './price.service.mock'; describe('Product', () => { let product; beforeEach(() => { const service = new MockPriceService(); product = new Product(service, 11.00); }); describe('price', () => { it('is calculated based on the basePrice and the state', () => { expect(product.totalPrice('FL')).toBe(11.66); }); }); }); Dependency Injection 169 We also get the bonus of having confidence that we’re testing the Product class in isolation. That is, we’re making sure that our class works with a predictable dependency. While the predictability is nice, it’s a bit laborious to pass a concrete implementation of a service every time we want a new Product. Thankfully, Angular’s DI library helps us deal with that problem, too. More on that below. Within Angular’s DI system, instead of directly importing and creating a new instance of a class, instead we will: • Register the “dependency” with Angular • Describe how the dependency will be injected • Inject the dependency One benefit of this model is that the dependency implementation can be swapped at run-time (as in our mocking example above). But another significant benefit is that we can configure how the dependency is created. That is, often in the case of program-wide services, we may want to have only one instance - that is, a Singleton. With DI we’re able to configure Singletons easily. A third use-case for DI is for configuration or environment-specific variables. For instance, we might define a “constant” API_URL, but then inject a different value in production vs. development. Let’s learn how to create our own services and the different ways of injecting them. Dependency Injection Parts To register a dependency we have to bind it to something that will identify that dependency. This identification is called the dependency token. For instance, if we want to register the URL of an API, we can use the string API_URL as the token. Similarly, if we’re registering a class, we can use the class itself as its token as we’ll see below. Dependency injection in Angular has three pieces: • the Provider (also often referred to as a binding) maps a token (that can be a string or a class) to a list of dependencies. It tells Angular how to create an object, given a token. • the Injector that holds a set of bindings and is responsible for resolving dependencies and injecting them when creating objects • the Dependency that is what’s being injected We can think of the role of each piece as illustrated below: 170 Dependency Injection Dependency Injection A way of thinking about this is that when we configure DI we specify what is being injected and how it will be resolved. Playing with an Injector Above with our Product and PriceService we manually created the PriceService using the new operator. This mimics what Angular itself does. Angular uses an injector to resolve a dependency and create the instance. This is done for us behind the scenes, but as an exercise, it’s useful to explore what’s happening. It can be enlightening to use the injector manually, because we can see what Angular does for us behind the scenes. Let’s manually use the injector in our component to resolve and create a service. (After we’ve resolved a dependency manually, we’ll show the typical, easy way of injecting dependencies. ) One of the common use-cases for services is to have a ‘global’ Singleton object. For instance, we might have a UserService which contains the information for the currently logged in user. Many different components will want to have logic based on the current user, so this is a good case for a service. Here’s a basic UserService that stores the user object as a property: Dependency Injection 171 code/dependency-injection/src/app/services/user.service.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { Injectable } from '@angular/core'; @Injectable() export class UserService { user: any; setUser(newUser) { this.user = newUser; } getUser(): any { return this.user; } } Say we want to create a toy sign-in form: code/dependency-injection/src/app/user-demo/user-demo.component.html 1 2 3 4 5 6 7 8 9 10 11 12

Welcome: {{ userName }}!

Above, we click the “Sign In” button to call the signIn() function (which we’ll define in a moment). If we have a userName, we’ll display a greeting. 172 Dependency Injection Simple Sign In Button Now let’s implement this functionality in our component by using the injector directly. code/dependency-injection/src/app/user-demo/user-demo.injector.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import { Component, ReflectiveInjector } from '@angular/core'; import { UserService } from '../services/user.service'; @Component({ selector: 'app-injector-demo', templateUrl: './user-demo.component.html', styleUrls: ['./user-demo.component.css'] }) export class UserDemoInjectorComponent { userName: string; userService: UserService; constructor() { // Create an _injector_ and ask for it to resolve and create a UserService const injector: any = ReflectiveInjector.resolveAndCreate([UserService]); // use the injector to **get the instance** of the UserService this.userService = injector.get(UserService); } 173 Dependency Injection 25 26 27 28 29 30 31 32 33 34 35 36 signIn(): void { // when we sign in, set the user // this mimics filling out a login form this.userService.setUser({ name: 'Nate Murray' }); // now **read** the user name from the service this.userName = this.userService.getUser().name; console.log('User name is: ', this.userName); } } This starts as a basic component: we have a selector, template, and CSS. Note that we have two properties: userName, which holds the currently logged-in user’s name and userService, which holds a reference to the UserService. In our component’s constructor we are using a static method from ReflectiveInjector called resolveAndCreate. That method is responsible for creating a new injector. The parameter we pass in is an array with all the injectable things we want this new injector to know. In our case, we just wanted it to know about the UserService injectable. The ReflectiveInjector is a concrete implementation of Injector that uses reflection to look up the proper parameter types. While there are other injectors that are possible ReflectiveInjector is the “normal” injector we’ll be using in most apps. Signed In Dependency Injection 174 Providing Dependencies with NgModule While it’s interesting to see how an injector is created directly, that isn’t the typical way we’d use injections. Instead, what we’d normally do is • use NgModule to register what we’ll inject – these are called providers and • use decorators (generally on a constructor) to specify what we’re injecting By doing these two steps Angular will manage creating the injector and resolving the dependencies. Let’s convert our UserService to be injectable as a singleton across our app. First, we’re going to add it to the providers key of our NgModule: code/dependency-injection/src/app/user-demo/user-demo.module.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; // imported here import { UserService } from '../services/user.service'; @NgModule({ imports: [ CommonModule ], providers: [ UserService // <-- added right here ], declarations: [] }) export class UserDemoModule { } Now we can inject UserService into our component like this: Dependency Injection 175 code/dependency-injection/src/app/user-demo/user-demo.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import { Component, OnInit } from '@angular/core'; import { UserService } from '../services/user.service'; @Component({ selector: 'app-user-demo', templateUrl: './user-demo.component.html', styleUrls: ['./user-demo.component.css'] }) export class UserDemoComponent { userName: string; // removed `userService` because of constructor shorthand below // Angular will inject the singleton instance of `UserService` here. // We set it as a property with `private`. constructor(private userService: UserService) { // empty because we don't have to do anything else! } // below is the same... signIn(): void { // when we sign in, set the user // this mimics filling out a login form this.userService.setUser({ name: 'Nate Murray' }); // now **read** the user name from the service this.userName = this.userService.getUser().name; console.log('User name is: ', this.userName); } } Notice in the constructor above that we have made userService: UserService an argument to the UserDemoComponent. When this component is created on our page Angular will resolve and inject the UserService singleton. What’s great about this is that because Angular is managing the instance, we don’t have to worry about doing it ourselves. Every class that injects the UserService will receive the same singleton. Dependency Injection 176 Providers are the Key It’s important to know that when we put the UserService on the constructor of the UserDemoComponent, Angular knows what to inject (and how) **because we listed UserService in the providers key of our NgModule. It does not inject arbitrary classes. You must configure an NgModule for DI to work. We’ve been talking a lot about Singleton services, but we can inject things in lots of other ways. Let’s take a look. Providers There are several ways we can configure resolving injected dependencies in Angular. For instance we can: • Inject a (singleton) instance of a class (as we’ve seen) • Inject a value • Call any function and inject the return value of that function Let’s look into detail at how we create each one: Using a Class As we’ve discussed, injecting a singleton instance of a class is probably the most common type of injection. When we put the class itself into the list of providers like this: 1 providers: [ UserService ] This tells Angular that we want to provide a singleton instance of UserService whenever UserService is injected. Because this pattern is so common, the class by itself is actually shorthand notation for the following, equivalent configuration: 1 2 3 providers: [ { provide: UserService, useClass: UserService } ] Dependency Injection 177 What’s interesting to note is that the object configuration with provide takes two keys. provide is the token that we use to identify the injection and the second useClass is how and what to inject. Here we’re mapping the UserService class to the UserService token. In this case, the name of the class and the token match. This is the common case, but know that the token and the injected thing aren’t required to have the same name. As we’ve seen above, in this case the injector will create a singleton behind the scenes and return the same instance every time we inject it . Of course, the first time it is injected, the singleton hasn’t been instantiated yet, so when creating the UserService instance for the first time, the DI system will trigger the class constructor method. Using a Value Another way we can use DI is to provide a value, much like we might use a global constant. For instance, we might configure an API Endpoint URL depending on the environment. To do this, in our NgModule providers, we use the key useValue: 1 2 3 providers: [ { provide: 'API_URL', useValue: 'http://my.api.com/v1' } ] Above, for the provide token we’re using a string of API_URL. If we use a string for the provide value, Angular can’t infer which dependency we’re resolving by the type. For instance we can’t write: 1 2 3 4 5 6 // doesn't work - anti-example export class AnalyticsDemoComponent { constructor(apiUrl: 'API_URL') { // <--- this isn't a type, just a string // if we put `string` that is ambiguous } } So what can we do? In this case, we’ll use the @Inject() decorator like this: Dependency Injection 1 2 3 4 5 6 7 178 import { Inject } from '@angular/core'; export class AnalyticsDemoComponent { constructor(@Inject('API_URL') apiUrl: string) { // works! do something w/ apiUrl } } Now that we know how to do simple values with useValue and Singleton classes with useClass, we’re ready to talk about the more advanced case: writing configurable services using factories. Configurable Services In the case of the UserService, no arguments are required for the constructor. But what happens if a service’s constructor requires arguments? We can implement this by using a factory which is a function that can return any object when injected. For instance, let’s say we’re writing a library for recording user analytics (that is, keeping a record of events of actions a user took on the page). In this scenario, we want to have an AnalyticsService with a catch: the AnalyticsService should define the interface for recording events, but not the implementation for handling the event. 179 Dependency Injection Tracking Analytics on the events Our user may, for instance, want to record these metrics with Google Analytics or they may want to use Optimizely, or some other in-house solution. Let’s write an injectable AnalyticsService which can take an implementation configuration. First, a couple of definitions. Let’s define a Metric: code/dependency-injection/src/app/analytics-demo/analytics-demo.interface.ts 4 5 6 7 export interface Metric { eventName: string; scope: string; } A Metric will store an eventName and a scope. We could use this for say, when a the user nate logs-in the eventName could be loggedIn and the scope would be nate. Dependency Injection 1 2 3 4 5 180 // just an example let metric: Metric = { eventName: 'loggedIn', scope: 'nate' } This way we could, in theory, count the number of user logins by counting the events with eventName loggedIn and count the number of times the specific user nate logged in by counting the loggedIn events with user nate. We also need to define what an analytics implementation would look like: code/dependency-injection/src/app/analytics-demo/analytics-demo.interface.ts 12 13 14 export interface AnalyticsImplementation { recordEvent(metric: Metric): void; } Here we define an AnalyticsImplementation interface to have one function: recordEvent which takes a Metric as an argument. Now let’s define the AnalyticsService: code/dependency-injection/src/app/services/analytics.service.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { Injectable } from '@angular/core'; import { Metric, AnalyticsImplementation } from '../analytics-demo/analytics-demo.interface'; @Injectable() export class AnalyticsService { constructor(private implementation: AnalyticsImplementation) { } record(metric: Metric): void { this.implementation.recordEvent(metric); } } Above our AnalyticsService defines one method: record which accepts a Metric and then passes it on to the implementation. Dependency Injection 181 Of course, this AnalyticsService is a bit trivial and in this case, we probably wouldn’t need the indirection. But this same pattern could be used in the case where you had a more advanced AnalyticsService. For instance, we could add middleware or broadcast to several implementations. Notice how its constructor method takes a phrase as a parameter? If we try to use the “regular” useClass injection mechanism we would see an error on the browser like: 1 Cannot resolve all parameters for AnalyticsService. This happens because we didn’t provide the injector with the implementation necessary for the constructor. In order to resolve this problem, we need to configure the provider to use a factory. Using a Factory So to use our AnalyticsService, we need to: • create an implementation that conforms to AnalyticsImplementation and • add it to providers with useFactory Here’s how: code/dependency-injection/src/app/analytics-demo/analytics-demo.module.1.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Metric, AnalyticsImplementation } from './analytics-demo.interface'; import { AnalyticsService } from '../services/analytics.service'; @NgModule({ imports: [ CommonModule ], providers: [ { // `AnalyticsService` is the _token_ we use to inject // note, the token is the class, but it's just used as an identifier! provide: AnalyticsService, Dependency Injection 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 182 // useFactory is a function - whatever is returned from this function // will be injected useFactory() { // create an implementation that will log the event const loggingImplementation: AnalyticsImplementation = { recordEvent: (metric: Metric): void => { console.log('The metric is:', metric); } }; // create our new `AnalyticsService` with the implementation return new AnalyticsService(loggingImplementation); } } ], declarations: [ ] }) export class AnalyticsDemoModule { } Here in providers we’re using the syntax: 1 2 3 providers: [ { provide: AnalyticsService, useFactory: () => ... } ] useFactory takes a function and whatever this function returns will be injected. Also note that we provide AnalyticsService. Again, when we use provide this way, we’re using the class AnalyticsService as the identifying token of what we’re going to inject. (If you wanted to be confusing, you could use a completely separate class, or less-confusingly a string.) In useFactory we’re creating an AnalyticsImplementation object that has one function: recordEvent. recordEvent is where we could, in theory, configure what happens when an event is recorded. Again, in a real app this would probably send an event to Google Analytics or a custom event logging software. Lastly, we instantiate our AnalyticsService and return it. Factory Dependencies Using a factory is the most powerful way to create injectables, because we can do whatever we want within the factory function. Sometimes our factory function will have dependencies of it’s own. Say that we wanted to configure our AnalyticsImplementation to make an HTTP request to a particular URL. In order to do this we’d need: Dependency Injection • The Angular Http client and • Our API_URL value Here’s how we could set that up: code/dependency-injection/src/app/analytics-demo/analytics-demo.module.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Metric, AnalyticsImplementation } from './analytics-demo.interface'; import { AnalyticsService } from '../services/analytics.service'; // added this -> import { HttpModule, Http } from '@angular/http'; @NgModule({ imports: [ CommonModule, HttpModule, // <-- added ], providers: [ // add our API_URL provider { provide: 'API_URL', useValue: 'http://devserver.com' }, { provide: AnalyticsService, // add our `deps` to specify the factory depencies deps: [ Http, 'API_URL' ], // notice we've added arguments here // the order matches the deps order useFactory(http: Http, apiUrl: string) { // create an implementation that will log the event const loggingImplementation: AnalyticsImplementation = { recordEvent: (metric: Metric): void => { console.log('The metric is:', metric); 183 Dependency Injection 37 38 39 40 41 42 43 44 45 46 47 48 49 184 console.log('Sending to: ', apiUrl); // ... You'd send the metric using http here ... } }; // create our new `AnalyticsService` with the implementation return new AnalyticsService(loggingImplementation); } }, ], declarations: [ ] }) export class AnalyticsDemoModule { } Here we’re importing the HttpModule, both in the ES6 import (which makes the class constants available) and in our NgModule imports (which makes it available for dependency injection). We’ve added an API_URL provider, as we did above. And then in our AnalyticsService provider, we’ve added a new key: deps. deps is an array of injection tokens and these tokens will be resolved and passed as arguments to the factory function. Dependency Injection in Apps To review, when writing our apps there are three steps we need to take in order to perform an injection: 1. Create the dependency (e.g. the service class) 2. Configure the injection (i.e. register the injection with Angular in our NgModule) 3. Declare the dependencies on the receiving component The first thing we do is create the service class, that is, the class that exposes some behavior we want to use. This will be called the injectable because it is the thing that our components will receive via the injection. Reminder on terminology: a provider provides (creates, instantiates, etc.) the injectable (the thing you want). In Angular when you want to access an injectable you inject a dependency into a function (often a constructor) and Angular’s dependency injection framework will locate it and provide it to you. As we can see, Dependency Injection provides a powerful way to manage dependencies within our app. Dependency Injection More Resources • Official Angular DI Docs⁴⁸ • Victor Savkin Compare DI in Angular 1 vs. Angular 2⁴⁹ ⁴⁸https://angular.io/docs/ts/latest/guide/dependency-injection.html ⁴⁹http://victorsavkin.com/post/126514197956/dependency-injection-in-angular-1-and-angular-2 185 HTTP Introduction Angular comes with its own HTTP library which we can use to call out to external APIs. When we make calls to an external server, we want our user to continue to be able to interact with the page. That is, we don’t want our page to freeze until the HTTP request returns from the external server. To achieve this effect, our HTTP requests are asynchronous. Dealing with asynchronous code is, historically, more tricky than dealing with synchronous code. In JavaScript, there are generally three approaches to dealing with async code: 1. Callbacks 2. Promises 3. Observables In Angular, the preferred method of dealing with async code is using Observables, and so that’s what we’ll cover in this chapter. There’s a whole chapter on RxJS and Observables: In this chapter we’re going to be using Observables and not explaining them much. If you’re just starting to read this book at this chapter, you should know that there’s a whole chapter on Observables that goes into RxJS in more detail. In this chapter we’re going to: 1. show a basic example of Http 2. create a YouTube search-as-you-type component 3. discuss API details about the Http library Sample Code The complete code for the examples in this chapter can be found in the http folder of the sample code. That folder contains a README.md which gives instructions for building and running the project. Try running the code while reading the chapter and feel free play around to get a deeper insight about how it all works. 187 HTTP Using @angular/http HTTP has been split into a separate module in Angular. This means that to use it you need to import constants from @angular/http. For instance, we might import constants from @angular/http like this: 1 2 3 4 5 6 7 8 9 10 import { // The NgModule for using @angular/http HttpModule, // the class constants Http, Response, RequestOptions, Headers } from '@angular/http'; import from @angular/http In our app.module.ts we’re going to import HttpModule which is a convenience collection of modules. code/http/src/app/app.module.ts 1 2 3 4 import import import import { { { { BrowserModule } from '@angular/platform-browser'; NgModule } from '@angular/core'; FormsModule } from '@angular/forms'; HttpModule } from '@angular/http'; In our NgModule we will add HttpModule to the list of imports. The effect is that we will be able to inject Http (and a few other modules) into our components. code/http/src/app/app.module.ts 14 15 16 17 18 19 20 21 @NgModule({ declarations: [ AppComponent, SimpleHttpComponent, MoreHttpRequestsComponent, YouTubeSearchComponent, SearchResultComponent, SearchBoxComponent 188 HTTP 22 23 24 25 26 27 28 29 30 31 ], imports: [ BrowserModule, FormsModule, HttpModule // <-- right here ], providers: [youTubeSearchInjectables], bootstrap: [AppComponent] }) export class AppModule { } Notice that we have custom components in declarations as well as a custom provider. We’ll talk about these later in the chapter! Now we can inject the Http service into our components (or anywhere we use DI). 1 2 3 4 5 6 7 8 class MyFooComponent { constructor(public http: Http) { } makeRequest(): void { // do something with this.http ... } } A Basic Request The first thing we’re going to do is make a simple GET request to the jsonplaceholder API⁵⁰. What we’re going to do is: 1. Have a button that calls makeRequest 2. makeRequest will call the http library to perform a GET request on our API 3. When the request returns, we’ll update this.data with the results of the data, which will be rendered in the view. Here’s a screenshot of our example: ⁵⁰http://jsonplaceholder.typicode.com 189 HTTP Basic Request Building the SimpleHttpComponent Component Definition The first thing we’re going to do is import a few modules and then specify a selector for our @Component: code/http/src/app/simple-http/simple-http.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 import { Component, OnInit } from '@angular/core'; import {Http, Response} from '@angular/http'; @Component({ selector: 'app-simple-http', templateUrl: './simple-http.component.html' }) export class SimpleHttpComponent implements OnInit { data: Object; loading: boolean; constructor(private http: Http) { } Building the SimpleHttpComponent template Next we build our view: HTTP 190 code/http/src/app/simple-http/simple-http.component.html 1 2 3 4

Basic Request

loading...
{{data | json}}
Our template has three interesting parts: 1. The button 2. The loading indicator 3. The data On the button we bind to (click) to call the makeRequest function in our controller, which we’ll define in a minute. We want to indicate to the user that our request is loading, so to do that we will show loading... if the instance variable loading is true, using ngIf. The data is an Object. A great way to debug objects is to use the json pipe as we do here. We’ve put this in a pre tag to give us nice, easy to read formatting. Building the SimpleHttpComponent Controller We start by defining a new class for our SimpleHttpComponent: code/http/src/app/simple-http/simple-http.component.ts 8 9 10 export class SimpleHttpComponent implements OnInit { data: Object; loading: boolean; We have two instance variables: data and loading. This will be used for our API return value and loading indicator respectively. Next we define our constructor: code/http/src/app/simple-http/simple-http.component.ts 12 13 constructor(private http: Http) { } 191 HTTP The constructor body is empty, but we inject one key module: Http. Remember that when we use the public keyword in public http: Http TypeScript will assign http to this.http. It’s a shorthand for: 1 2 3 4 5 6 // other instance variables here http: Http; constructor(http: Http) { this.http = http; } Now let’s make our first HTTP request by implementing the makeRequest function: code/http/src/app/simple-http/simple-http.component.ts 18 19 20 21 22 23 24 25 makeRequest(): void { this.loading = true; this.http.request('http://jsonplaceholder.typicode.com/posts/1') .subscribe((res: Response) => { this.data = res.json(); this.loading = false; }); } When we call makeRequest, the first thing we do is set this.loading = true. This will turn on the loading indicator in our view. To make an HTTP request is straightforward: we call this.http.request and pass the URL to which we want to make a GET request. http.request returns an Observable. We can subscribe to changes (akin to using then from a Promise) using subscribe. code/http/src/app/simple-http/simple-http.component.ts 20 21 this.http.request('http://jsonplaceholder.typicode.com/posts/1') .subscribe((res: Response) => { When our http.request returns (from the server) the stream will emit a Response object. We extract the body of the response as an Object by using json and then we set this.data to that Object. Since we have a response, we’re not loading anymore so we set this.loading = false 192 HTTP .subscribe can also handle failures and stream completion by passing a function to the second and third arguments respectively. In a production app it would be a good idea to handle those cases, too. That is, this.loading should also be set to false if the request fails (i.e. the stream emits an error). Full SimpleHttpComponent Here’s what our SimpleHttpComponent looks like altogether: code/http/src/app/simple-http/simple-http.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import { Component, OnInit } from '@angular/core'; import {Http, Response} from '@angular/http'; @Component({ selector: 'app-simple-http', templateUrl: './simple-http.component.html' }) export class SimpleHttpComponent implements OnInit { data: Object; loading: boolean; constructor(private http: Http) { } ngOnInit() { } makeRequest(): void { this.loading = true; this.http.request('http://jsonplaceholder.typicode.com/posts/1') .subscribe((res: Response) => { this.data = res.json(); this.loading = false; }); } } Writing a YouTubeSearchComponent The last example was a minimal way to get the data from an API server into your code. Now let’s try to build a more involved example. 193 HTTP In this section, we’re going to build a way to search YouTube as you type. When the search returns we’ll show a list of video thumbnail results, along with a description and link to each video. Here’s a screenshot of what happens when I search for “cats playing ipads”: Can I get my cat to write Angular? For this example we’re going to write several things: 1. A SearchResult object that will hold the data we want from each result 2. A YouTubeSearchService which will manage the API request to YouTube and convert the results to a stream of SearchResult[] 3. A SearchBoxComponent which will call out to the YouTube service as the user types 4. A SearchResultComponent which will render a specific SearchResult 194 HTTP 5. A YouTubeSearchComponent which will encapsulate our whole YouTube searching app and render the list of results Let’s handle each part one at a time. Patrick Stapleton has an excellent repository named angular2-webpack-starter⁵¹. This repo has an RxJS example which autocompletes Github repositories. Some of the ideas in this section are inspired from that example. It’s a fantastic project with lots of examples and you should check it out. Writing a SearchResult First let’s start with writing a basic SearchResult class. This class is just a convenient way to store the specific fields we’re interested in from our search results. code/http/src/app/you-tube-search/search-result.model.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /** * SearchResult is a data-structure that holds an individual * record from a YouTube video search */ export class SearchResult { id: string; title: string; description: string; thumbnailUrl: string; videoUrl: string; constructor(obj?: any) this.id this.title this.description this.thumbnailUrl this.videoUrl { = = = = = obj && obj.id || null; obj && obj.title || null; obj && obj.description || null; obj && obj.thumbnailUrl || null; obj && obj.videoUrl || `https://www.youtube.com/watch?v=${this.id}`; } } This pattern of taking an obj?: any lets us simulate keyword arguments. The idea is that we can create a new SearchResult and just pass in an object containing the keys we want to specify. ⁵¹https://github.com/angular-class/angular2-webpack-starter 195 HTTP The only thing to point out here is that we’re constructing the videoUrl using a hard-coded URL format. You’re welcome to change this to a function which takes more arguments, or use the video id directly in your view to build this URL if you need to. Writing the YouTubeSearchService The API For this example we’re going to be using the YouTube v3 search API⁵². In order to use this API you need to have an API key. I’ve included an API key in the sample code which you can use. However, by the time you read this, you may find it’s over the rate limits. If that happens, you’ll need to issue your own key. To issue your own key see this documentation⁵³. For the sake of simplicity, I’ve registered a server key, but you should probably use a browser key if you’re going to put your javascript code online. We’re going to setup two constants for our YouTubeSearchService mapping to our API key and the API URL: 1 2 let YOUTUBE_API_KEY: string = "XXX_YOUR_KEY_HERE_XXX"; let YOUTUBE_API_URL: string = "https://www.googleapis.com/youtube/v3/search"; Eventually we’re going to want to test our app. One of the things we find when testing is that we don’t always want to test against production - we often want to test against staging or a development API. To help with this environment configuration, one of the things we can do is make these constants injectable. Why should we inject these constants instead of just using them in the normal way? Because if we make them injectable we can 1. have code that injects the right constants for a given environment at deploy time and 2. replace the injected value easily at test-time By injecting these values, we have a lot more flexibility about their values down the line. In order to make these values injectable, we use the { provide: ... , useValue: ... } syntax like this: ⁵²https://developers.google.com/youtube/v3/docs/search/list ⁵³https://developers.google.com/youtube/registering_an_application#Create_API_Keys 196 HTTP code/http/src/app/you-tube-search/you-tube-search.injectables.ts 1 2 3 4 5 6 7 8 9 10 11 import { YouTubeSearchService, YOUTUBE_API_KEY, YOUTUBE_API_URL } from './you-tube-search.service'; export const youTubeSearchInjectables: Array = [ {provide: YouTubeSearchService, useClass: YouTubeSearchService}, {provide: YOUTUBE_API_KEY, useValue: YOUTUBE_API_KEY}, {provide: YOUTUBE_API_URL, useValue: YOUTUBE_API_URL} ]; Here we’re specifying that we want to bind YOUTUBE_API_KEY “injectably” to the value of YOUTUBE_API_KEY. (Same for YOUTUBE_API_URL, and we’ll define YouTubeSearchService in a minute.) To get a refresher on the different ways to create ‘injectables’, checkout the chapter on dependency injection If you recall, to make something available to be injected throughout our application, we need to put it in providers for our NgModule. Since we’re exporting youTubeServiceInjectables here we can use it in our app.module.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // http/app.ts import { HttpModule } from '@angular/http'; import { youTubeServiceInjectables } from "components/YouTubeSearchComponent"; // ... // further down // ... @NgModule({ declarations: [ HttpApp, // others .... ], imports: [ BrowserModule, HttpModule ], bootstrap: [ HttpApp ], providers: [ youTubeServiceInjectables // <--- right here 197 HTTP 18 19 20 ] }) class HttpAppModule {} Now we can inject YOUTUBE_API_KEY (from the youTubeServiceInjectables) instead of using the variable directly. YouTubeSearchService constructor We create our YouTubeSearchService by making a service class: code/http/src/app/you-tube-search/you-tube-search.service.ts 22 23 24 25 26 27 28 29 30 31 /** * YouTubeService connects to the YouTube API * See: * https://developers.google.com/youtube/v3/docs/search/list */ @Injectable() export class YouTubeSearchService { constructor(private http: Http, @Inject(YOUTUBE_API_KEY) private apiKey: string, @Inject(YOUTUBE_API_URL) private apiUrl: string) { } The @Injectable annotation allows us to inject things into this classes constructor. In the constructor we inject three things: 1. Http 2. YOUTUBE_API_KEY 3. YOUTUBE_API_URL Notice that we make instance variables from all three arguments, meaning we can access them as this.http, this.apiKey, and this.apiUrl respectively. Notice that we explicitly inject using the @Inject(YOUTUBE_API_KEY) notation. YouTubeSearchService search Next let’s implement the search function. search takes a query string and returns an Observable which will emit a stream of SearchResult[]. That is, each item emitted is an array of SearchResults. HTTP 198 code/http/src/app/you-tube-search/you-tube-search.service.ts 33 34 35 36 37 38 39 40 41 search(query: string): Observable { const params: string = [ `q=${query}`, `key=${this.apiKey}`, `part=snippet`, `type=video`, `maxResults=10` ].join('&'); const queryUrl = `${this.apiUrl}?${params}`; We’re building the queryUrl in a manual way here. We start by simply putting the query params in the params variable. (You can find the meaning of each of those values by reading the search API docs⁵⁴.) Then we build the queryUrl by concatenating the apiUrl and the params. Now that we have a queryUrl we can make our request. In this case we are going to use http.get instead of http.request. While http.request can make any kind of request (POST, DELETE< GET, etc.), http.get is a shorthand for GET requests: code/http/src/app/you-tube-search/you-tube-search.service.ts 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 search(query: string): Observable { const params: string = [ `q=${query}`, `key=${this.apiKey}`, `part=snippet`, `type=video`, `maxResults=10` ].join('&'); const queryUrl = `${this.apiUrl}?${params}`; return this.http.get(queryUrl) .map((response: Response) => { return (response.json()).items.map(item => { // console.log("raw item", item); // uncomment if you want to debug return new SearchResult({ id: item.id.videoId, title: item.snippet.title, description: item.snippet.description, thumbnailUrl: item.snippet.thumbnails.high.url }); ⁵⁴https://developers.google.com/youtube/v3/docs/search/list 199 HTTP 52 53 54 }); }); } Here we take the return value of http.get and use map to get the Response from the request. From that response we extract the body as an object using .json() and then we iterate over each item and convert it to a SearchResult. If you’d like to see what the raw item looks like, just uncomment the console.log and inspect it in your browsers developer console. Notice that we’re calling (response.json()).items. What’s going on here? We’re telling TypeScript that we’re not interested in doing strict type checking. When working with a JSON API, we don’t generally have typing definitions for the API responses, and so TypeScript won’t know that the Object returned even has an items key, so the compiler will complain. We could call response.json()["items"] and then cast that to an Array etc., but in this case (and in creating the SearchResult, it’s just cleaner to use an any type, at the expense of strict type checking YouTubeSearchService Full Listing Here’s the full listing of our YouTubeSearchService. In this chapter we are adding some style using the CSS framework Bootstrap⁵⁵ ⁵⁵http://getbootstrap.com HTTP 200 code/http/src/app/you-tube-search/you-tube-search.service.ts 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 /** * YouTubeService connects to the YouTube API * See: * https://developers.google.com/youtube/v3/docs/search/list */ @Injectable() export class YouTubeSearchService { constructor(private http: Http, @Inject(YOUTUBE_API_KEY) private apiKey: string, @Inject(YOUTUBE_API_URL) private apiUrl: string) { } search(query: string): Observable { const params: string = [ `q=${query}`, `key=${this.apiKey}`, `part=snippet`, `type=video`, `maxResults=10` ].join('&'); const queryUrl = `${this.apiUrl}?${params}`; return this.http.get(queryUrl) .map((response: Response) => { return (response.json()).items.map(item => { // console.log("raw item", item); // uncomment if you want to debug return new SearchResult({ id: item.id.videoId, title: item.snippet.title, description: item.snippet.description, thumbnailUrl: item.snippet.thumbnails.high.url }); }); }); } } Writing the SearchBoxComponent The SearchBoxComponent plays a key role in our app: it is the mediator between our UI and the YouTubeSearchService. The SearchBoxComponent will : HTTP 201 1. Watch for keyup on an input and submit a search to the YouTubeSearchService 2. Emit a loading event when we’re loading (or not) 3. Emit a results event when we have new results SearchBoxComponent @Component Definition Let’s define our SearchBoxComponent @Component: code/http/src/app/you-tube-search/search-box.component.ts 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Component({ selector: 'app-search-box', template: ` ` }) export class SearchBoxComponent implements OnInit { @Output() loading: EventEmitter = new EventEmitter(); @Output() results: EventEmitter = new EventEmitter(); constructor(private youtube: YouTubeSearchService, private el: ElementRef) { } The selector we’ve seen many times before: this allows us to create a tag. The two @Outputs specify that events will be emitted from this component. That is, we can use the (output)="callback()" syntax in our view to listen to events on this component. For example, here’s how we will use the app-search-box tag in our view later on: 1 2 3 4 In this example, when the SearchBoxComponent emits a loading event, we will set the variable loading in the parent context. Likewise, when the SearchBoxComponent emits a results event, we will call the updateResults() function, with the value, in the parent’s context. In the @Component class we’re specifying the properties of the events with the names loading and results. In this example, each event will have a corresponding EventEmitter as an instance variable of the controller class. We’ll implement that in a few minutes. For now, remember that @Component is like the public API for our component, so here we’re just specifying the name of the events, and we’ll worry about implementing the EventEmitters later. HTTP 202 SearchBoxComponent template Definition Our template is straightforward. We have one input tag: code/http/src/app/you-tube-search/search-box.component.ts 24 25 26 template: ` ` SearchBoxComponent Controller Definition Our SearchBoxComponent controller is a new class: code/http/src/app/you-tube-search/search-box.component.ts 28 29 30 31 export class SearchBoxComponent implements OnInit { @Output() loading: EventEmitter = new EventEmitter(); @Output() results: EventEmitter = new EventEmitter(); We say that this class implements OnInit because we want to use the ngOnInit lifecycle callback. If a class implements OnInit then the ngOnInit function will be called after the first change detection check. ngOnInit is a good place to do initialization (vs. the constructor) because inputs set on a component are not available in the constructor. Here we create the EventEmitters for both loading and the results. loading will emit a boolean when this search is loading and results will emit an array of SearchResults when the search is finished. SearchBoxComponent Controller Definition constructor Let’s talk about the SearchBoxComponent constructor: code/http/src/app/you-tube-search/search-box.component.ts 32 33 34 constructor(private youtube: YouTubeSearchService, private el: ElementRef) { } In our constructor we inject : 1. Our YouTubeSearchService and 2. The element el that this component is attached to. el is an object of type ElementRef, which is an Angular wrapper around a native element. We set both injections as instance variables. HTTP 203 SearchBoxComponent Controller Definition ngOnInit On this input box we want to watch for keyup events. The thing is, if we simply did a search after every keyup that wouldn’t work very well. There are three things we can do to improve the user experience: 1. Filter out any empty or short queries 2. “debounce” the input, that is, don’t search on every character but only after the user has stopped typing after a short amount of time 3. discard any old searches, if the user has made a new search We could manually bind to keyup and call a function on each keyup event and then implement filtering and debouncing from there. However, there is a better way: turn the keyup events into an observable stream. RxJS provides a way to listen to events on an element using Rx.Observable.fromEvent. We can use it like so: code/http/src/app/you-tube-search/search-box.component.ts 36 37 38 ngOnInit(): void { // convert the `keyup` event into an observable stream Observable.fromEvent(this.el.nativeElement, 'keyup') Notice that in fromEvent: • the first argument is this.el.nativeElement (the native DOM element this component is attached to) • the second argument is the string 'keyup', which is the name of the event we want to turn into a stream We can now perform some RxJS magic over this stream to turn it into SearchResults. Let’s walk through step by step. Given the stream of keyup events we can chain on more methods. In the next few paragraphs we’re going to chain several functions on to our stream which will transform the stream. Then at the end we’ll show the whole example together. First, let’s extract the value of the input tag: 1 .map((e: any) => e.target.value) // extract the value of the input Above says, map over each keyup event, then find the event target (e.target, that is, our input element) and extract the value of that element. This means our stream is now a stream of strings. Next: 204 HTTP 1 .filter((text: string) => text.length > 1) This filter means the stream will not emit any search strings for which the length is less than one. You could set this to a higher number if you want to ignore short searches. 1 .debounceTime(250) debounceTime means we will throttle requests that come in faster than 250ms. That is, we won’t search on every keystroke, but rather after the user has paused a small amount. 1 .do(() => this.loading.emit(true)) // enable loading Using do on a stream is a way to perform a function mid-stream for each event, but it does not change anything in the stream. The idea here is that we’ve got our search, it has enough characters, and we’ve debounced, so now we’re about to search, so we turn on loading. this.loading is an EventEmitter. We “turn on” loading by emitting true as the next event. We emit something on an EventEmitter by calling next. Writing this.loading.emit(true) means, emit a true event on the loading EventEmitter. When we listen to the loading event on this component, the $event value will now be true (we’ll look more closely at using $event below). 1 2 .map((query: string) => this.youtube.search(query)) .switch() We use .map to call perform a search for each query that is emitted. By using switch we’re, essentially, saying “ignore all search events but the most recent”. That is, if a new search comes in, we want to use the most recent and discard the rest. Reactive experts will note that I’m handwaving here. switch has a more specific technical definition which you can read about in the RxJS docs here⁵⁶. For each query that comes in, we’re going to perform a search on our YouTubeSearchService. Putting the chain together we have this: ⁵⁶https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/switch.md 205 HTTP code/http/src/app/you-tube-search/search-box.component.ts 36 37 38 39 40 41 42 43 44 45 46 47 ngOnInit(): void { // convert the `keyup` event into an observable stream Observable.fromEvent(this.el.nativeElement, 'keyup') .map((e: any) => e.target.value) // extract the value of the input .filter((text: string) => text.length > 1) // filter out if empty .debounceTime(250) // only once every 250ms .do(() => this.loading.emit(true)) // enable loading // search, discarding old events if new input comes in .map((query: string) => this.youtube.search(query)) .switch() // act on the return of the search .subscribe( The API of RxJS can be a little intimidating because the API surface area is large. That said, we’ve implemented a sophisticated event-handling stream in very few lines of code! Because we are calling out to our YouTubeSearchService our stream is now a stream of SearchResult[]. We can subscribe to this stream and perform actions accordingly. subscribe takes three arguments: onSuccess, onError, onCompletion. code/http/src/app/you-tube-search/search-box.component.ts 47 48 49 50 51 52 53 54 55 56 57 58 59 60 .subscribe( (results: SearchResult[]) => { // on sucesss this.loading.emit(false); this.results.emit(results); }, (err: any) => { // on error console.log(err); this.loading.emit(false); }, () => { // on completion this.loading.emit(false); } ); } The first argument specifies what we want to do when the stream emits a regular event. Here we emit an event on both of our EventEmitters: 1. We call this.loading.emit(false), indicating we’ve stopped loading HTTP 206 2. We call this.results.emit(results), which will emit an event containing the list of results The second argument specifies what should happen when the stream has an error event. Here we set this.loading.emit(false) and log out the error. The third argument specifies what should happen when the stream completes. Here we also emit that we’re done loading. SearchBoxComponent: Full Listing All together, here’s the full listing of our SearchBoxComponent Component: code/http/src/app/you-tube-search/search-box.component.ts 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 @Component({ selector: 'app-search-box', template: ` ` }) export class SearchBoxComponent implements OnInit { @Output() loading: EventEmitter = new EventEmitter(); @Output() results: EventEmitter = new EventEmitter(); constructor(private youtube: YouTubeSearchService, private el: ElementRef) { } ngOnInit(): void { // convert the `keyup` event into an observable stream Observable.fromEvent(this.el.nativeElement, 'keyup') .map((e: any) => e.target.value) // extract the value of the input .filter((text: string) => text.length > 1) // filter out if empty .debounceTime(250) // only once every 250ms .do(() => this.loading.emit(true)) // enable loading // search, discarding old events if new input comes in .map((query: string) => this.youtube.search(query)) .switch() // act on the return of the search .subscribe( (results: SearchResult[]) => { // on sucesss this.loading.emit(false); this.results.emit(results); 207 HTTP 52 53 54 55 56 57 58 59 60 61 62 }, (err: any) => { // on error console.log(err); this.loading.emit(false); }, () => { // on completion this.loading.emit(false); } ); } } Writing SearchResultComponent The SearchBoxComponent was fairly complicated . Let’s handle a much easier component now: the SearchResultComponent. The SearchResultComponent’s job is to render a single SearchResult. Given what we’ve already covered there aren’t any new ideas here, so let’s take it all at once: code/http/src/app/you-tube-search/search-result.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import { Component, OnInit, Input } from '@angular/core'; import { SearchResult } from './search-result.model'; @Component({ selector: 'app-search-result', templateUrl: './search-result.component.html' }) export class SearchResultComponent implements OnInit { @Input() result: SearchResult; constructor() { } ngOnInit() { } } Single Search Result Component HTTP A few things: The @Component takes a single input result, on which we will put the SearchResult assigned to this component. The template shows the title, description, and thumbnail of the video and then links to the video via a button. code/http/src/app/you-tube-search/search-result.component.html 1 2 3 4 5 6 7 8 9 10 11 12

{{result.title}}

{{result.description}}

Watch

The SearchResultComponent simply stores the SearchResult in the instance variable result. Writing YouTubeSearchComponent The last component we have to implement is the YouTubeSearchComponent. This is the component that ties everything together. YouTubeSearchComponent @Component code/http/src/app/you-tube-search/you-tube-search.component.ts 4 5 6 7 8 9 10 @Component({ selector: 'app-you-tube-search', templateUrl: './you-tube-search.component.html' }) export class YouTubeSearchComponent implements OnInit { results: SearchResult[]; loading: boolean; Our @Component decorator is straightforward: use the selector app-you-tube-search. 208 HTTP 209 YouTubeSearchComponent Controller Before we look at the template, let’s take a look at the YouTubeSearchComponent controller: code/http/src/app/you-tube-search/you-tube-search.component.ts 8 9 10 11 12 13 14 15 16 17 18 19 export class YouTubeSearchComponent implements OnInit { results: SearchResult[]; loading: boolean; constructor() { } ngOnInit() { } updateResults(results: SearchResult[]): void { this.results = results; // console.log("results:", this.results); // uncomment to take a look } } This component holds one instance variable: results which is an array of SearchResults. We also define one function: updateResults. updateResults simply takes whatever new SearchResult[] it’s given and sets this.results to the new value. We’ll use both results and updateResults in our template. YouTubeSearchComponent template Our view needs to do three things: 1. Show the loading indicator, if we’re loading 2. Listen to events on the search-box 3. Show the search results Next lets look at our template. Let’s build some basic structure and show the loading gif next to the header: HTTP 210 code/http/src/app/you-tube-search/you-tube-search.component.html 1 2 3 4 5 6 7 8 9
We only want to show this loading image if loading is true, so we use ngIf to implement that functionality. Next, let’s look at the markup where we use our search-box: code/http/src/app/you-tube-search/you-tube-search.component.html 10 11 12 13 14 15 16
The interesting part here is how we bind to the loading and results outputs. Notice, that we use the (output)="action()" syntax here. For the loading output, we run the expression loading = $event. $event will be substituted with the value of the event that is emitted from the EventEmitter. That is, in our SearchBoxComponent, when we call this.loading.emit(true) then $event will be true. Similarly, for the results output, we call the updateResults() function whenever a new set of results are emitted. This has the effect of updating our components results instance variable. Lastly, we want to take the list of results in this component and render a search-result for each one: HTTP code/http/src/app/you-tube-search/you-tube-search.component.html 19 20 21 22 23 24 25
YouTubeSearchComponent Full Listing Here’s the full listing for the YouTubeSearchComponent: code/http/src/app/you-tube-search/you-tube-search.component.ts 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Component({ selector: 'app-you-tube-search', templateUrl: './you-tube-search.component.html' }) export class YouTubeSearchComponent implements OnInit { results: SearchResult[]; loading: boolean; constructor() { } ngOnInit() { } updateResults(results: SearchResult[]): void { this.results = results; // console.log("results:", this.results); // uncomment to take a look } } and the template: 211 HTTP 212 code/http/src/app/you-tube-search/you-tube-search.component.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
There we have it! A functional search-as-you-type implemented for YouTube video search! Try running it from the code examples if you haven’t already. @angular/http API Of course, all of the HTTP requests we’ve made so far have simply been GET requests. It’s important that we know how we can make other requests too. Making a POST request Making POST request with @angular/http is very much like making a GET request except that we have one additional parameter: a body. HTTP 213 jsonplaceholder API⁵⁷ also provides a convent URL for testing our POST requests, so let’s use it for a POST: code/http/src/app/more-http-requests/more-http-requests.component.ts 23 24 25 26 27 28 29 30 31 32 33 34 35 36 makePost(): void { this.loading = true; this.http.post( 'http://jsonplaceholder.typicode.com/posts', JSON.stringify({ body: 'bar', title: 'foo', userId: 1 })) .subscribe((res: Response) => { this.data = res.json(); this.loading = false; }); } Notice in the second argument we’re taking an Object and converting it to a JSON string using JSON.stringify. PUT / PATCH / DELETE / HEAD There are a few other fairly common HTTP requests and we call them in much the same way. • http.put and http.patch map to PUT and PATCH respectively and both take a URL and a body • http.delete and http.head map to DELETE and HEAD respectively and both take a URL (no body) Here’s how we might make a DELETE request: ⁵⁷http://jsonplaceholder.typicode.com 214 HTTP code/http/src/app/more-http-requests/more-http-requests.component.ts 38 39 40 41 42 43 44 45 makeDelete(): void { this.loading = true; this.http.delete('http://jsonplaceholder.typicode.com/posts/1') .subscribe((res: Response) => { this.data = res.json(); this.loading = false; }); } RequestOptions All of the http methods we’ve covered so far also take an optional last argument: RequestOptions. The RequestOptions object encapsulates: • • • • • • • • method headers body mode credentials cache url search Let’s say we want to craft a GET request that uses a special X-API-TOKEN header. We can create a request with this header like so: code/http/src/app/more-http-requests/more-http-requests.component.ts 47 48 49 50 51 52 53 54 55 56 57 58 makeHeaders(): void { const headers: Headers = new Headers(); headers.append('X-API-TOKEN', 'ng-book'); const opts: RequestOptions = new RequestOptions(); opts.headers = headers; this.http.get('http://jsonplaceholder.typicode.com/posts/1', opts) .subscribe((res: Response) => { this.data = res.json(); }); } HTTP 215 Summary @angular/http is flexible and suitable for a wide variety of APIs. One of the great things about @angular/http is that it has support for mocking the backend which is very useful in testing. To learn about testing HTTP, flip on over to the testing chapter. Routing In web development, routing means splitting the application into different areas usually based on rules that are derived from the current URL in the browser. For instance, if we visit the / path of a website, we may be visiting the home route of that website. Or if we visit /about we want to render the “about page”, and so on. Why Do We Need Routing? Defining routes in our application is useful because we can: • separate different areas of the app; • maintain the state in the app; • protect areas of the app based on certain rules; For example, imagine we are writing an inventory application similar to the one we described in previous chapters. When we first visit the application, we might see a search form where we can enter a search term and get a list of products that match that term. After that, we might click a given product to visit that product’s details page. Because our app is client-side, it’s not technically required that we change the URL when we change “pages”. But it’s worth thinking about for a minute: what would be the consequences of using the same URL for all pages? • You wouldn’t be able to refresh the page and keep your location within the app • You wouldn’t be able to bookmark a page and come back to it later • You wouldn’t be able to share the URL of that page with others Or put in a positive light, routing lets us define a URL string that specifies where within our app a user should be. In our inventory example we could determine a series of different routes for each activity, for instance: The initial root URL could be represented by http://our-app/. When we visit this page, we could be redirected to our “home” route at http://our-app/home. When accessing the ‘About Us’ area, the URL could become http://our-app/about. This way if we sent the URL http://our-app/about to another user they would see same page. Routing 217 How client-side routing works Perhaps you’ve written server-side routing code before (though, it isn’t necessary to complete this chapter). Generally with server-side routing, the HTTP request comes in and the server will render a different controller depending on the incoming URL. For instance, with Express.js⁵⁸ you might write something like this: 1 2 3 4 5 6 7 var express = require('express'); var router = express.Router(); // define the about route router.get('/about', function(req, res) { res.send('About us'); }); Or with Ruby on Rails⁵⁹ you might have: 1 2 3 4 5 6 7 8 9 # routes.rb get '/about', to: 'pages#about' # PagesController.rb class PagesController < ActionController::Base def about render end end The pattern varies per framework, but in both of these cases you have a server that accepts a request and routes to a controller and the controller runs a specific action, depending on the path and parameters. Client-side routing is very similar in concept but different in implementation. With client-side routing we’re not necessarily making a request to the server on every URL change. With our Angular apps, we refer to them as “Single Page Apps” (SPA) because our server only gives us a single page and it’s our JavaScript that renders the different pages. So how can we have different routes in our JavaScript code? ⁵⁸http://expressjs.com/guide/routing.html ⁵⁹http://rubyonrails.org/ 218 Routing The beginning: using anchor tags Client-side routing started out with a clever hack: Instead of using a normal server-side URL for a page in our SPA, we use the anchor tag as the client-side URL. As you may already know, anchor tags were traditionally used to link directly to a place within the webpage and make the browser scroll all the way to where that anchor was defined. For instance, if we define an anchor tag in an HTML page: 1 2

About

And we visited the URL http://something/#about, the browser would jump straight to that H1 tag that identified by the about anchor. The clever move for client-side frameworks used for SPAs was to take the anchor tags and use them represent the routes within the app by formatting them as paths. For example, the about route for an SPA would be something like http://something/#/about. This is what is known as hash-based routing. What’s neat about this trick is that it looks like a “normal” URL because we’re starting our anchor with a slash (/about). The evolution: HTML5 client-side routing With the introduction of HTML5, browsers acquired the ability to programmatically create new browser history entries that change the displayed URL without the need for a new request. This is achieved using the history.pushState method that exposes the browser’s navigational history to JavaScript. So now, instead of relying on the anchor hack to navigate routes, modern frameworks can rely on pushState to perform history manipulation without reloads. Angular 1 Note: This way of routing already works in Angular 1, but it needs to be explicitly enabled using $locationProvider.html5Mode(true). In Angular, however, the HTML5 is the default mode. Later in this chapter we show how to change from HTML5 mode to the old anchor tag mode. 219 Routing There’s two things you need to be aware of when using HTML5 mode routing, though 1. Not all browsers support HTML5 mode routing, so if you need to support older browsers you might be stuck with hash-based routing for a while. 2. The server has to support HTML5 based routing. It may not be immediately clear why the server has to support HTML5 based-routing, we’ll talk more about why later in this chapter. Writing our first routes The Angular docs recommends using HTML5 mode routing⁶⁰. But due to the challenges mentioned in the previous section we will for simplicity be using hash based routing in our examples. In Angular we configure routes by mapping paths to the component that will handle them. Let’s create a small app that has multiple routes. On this sample application we will have 3 routes: • A main page route, using the /#/home path; • An about page, using the /#/about path; • A contact us page, using the /#/contact path; And when the user visits the root path (/#/), it will redirect to the home path. Components of Angular routing There are three main components that we use to configure routing in Angular: • Routes describes the routes our application supports • RouterOutlet is a “placeholder” component that shows Angular where to put the content of each route • RouterLink directive is used to link to routes Let’s look at each one more closely. Imports In order to use the router in Angular, we import constants from the @angular/router package: ⁶⁰https://angular.io/docs/ts/latest/guide/router.html#!#browser-url-styles Routing 220 code/routes/routing/src/app/app.module.ts 5 6 7 8 import { RouterModule, Routes } from '@angular/router'; Now we can define our router configuration. Routes To define routes for our application, create a Routes configuration and then use RouterModule.forRoot(routes) to provide our application with the dependencies necessary to use the router. First, let’s look at the routes definitions: code/routes/routing/src/app/app.module.ts 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 const routes: Routes = [ // basic routes { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'home', component: HomeComponent }, { path: 'about', component: AboutComponent }, { path: 'contact', component: ContactComponent }, { path: 'contactus', redirectTo: 'contact' }, // authentication demo { path: 'login', component: LoginComponent }, { path: 'protected', component: ProtectedComponent, canActivate: [ LoggedInGuard ] }, // nested { path: 'products', component: ProductsComponent, children: childRoutes } ]; Notice a few things about the routes: 221 Routing • path specifies the URL this route will handle • component is what ties a given route path to a component that will handle the route • the optional redirectTo is used to redirect a given path to an existing route We’ll dive into the details of each route in this chapter, but at a high-level, the goal of routes is to specify which component will handle a given path. Redirections When we use redirectTo on a route definition, it will tell the router that when we visit the path of the route, we want the browser to be redirected to another route. In our sample code above, if we visit the root path at http://localhost:4200/#/⁶¹, we’ll be redirected to the route home. Another example is the contactus route: code/routes/routing/src/app/app.module.ts 32 { path: 'contactus', redirectTo: 'contact' }, In this case, if we visit the URL http://localhost:4200/#/contactus⁶², we’ll see that the browser redirects to /contact. Sample Code The complete code for the examples in this section can be found in the routes/routing folder of the sample code. That folder contains a README.md, which gives instructions for building and running the project. There are many different imports required for routing and we don’t list every single one in every code example below. However we do list the filename and line number from which almost every example is taken from. If you’re having trouble figuring out how to import a particular class, open up the code using your editor to see the entire code listing. Try running the code while reading this section and feel free play around to get a deeper insight about how it all works. Installing our Routes Now that we have our Routes routes, we need to install it. To use the routes in our app we do two things to our NgModule: 1. Import the RouterModule 2. Install the routes using RouterModule.forRoot(routes) in the imports of our NgModule Here’s our routes configured into our NgModule for this app: ⁶¹http://localhost:4200/#/ ⁶²http://localhost:4200/#/contactus 222 Routing code/routes/routing/src/app/app.module.ts 26 27 28 29 30 31 32 const routes: Routes = [ // basic routes { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'home', component: HomeComponent }, { path: 'about', component: AboutComponent }, { path: 'contact', component: ContactComponent }, { path: 'contactus', redirectTo: 'contact' }, code/routes/routing/src/app/app.module.ts 59 60 61 62 63 64 65 66 67 imports: [ BrowserModule, FormsModule, HttpModule, RouterModule.forRoot(routes), // <-- routes // added this for our child module ProductsModule ], RouterOutlet using When we change routes, we want to keep our outer “layout” template and only substitute the “inner section” of the page with the route’s component. In order to describe to Angular where in our page we want to render the contents for each route, we use the RouterOutlet directive. Our component @Component has a template which specifies some div structure, a section for Navigation, and a directive called router-outlet. The router-outlet element indicates where the contents of each route component will be rendered. We are are able to use the router-outlet directive in our template because we imported the RouterModule in our NgModule. Here’s the component and template for the navigation wrapper of our app: Routing 223 code/routes/routing/src/app/app.component.ts 6 7 8 9 10 11 12 13 14 @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(private router: Router) { }; } and the template: code/routes/routing/src/app/app.component.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
If we look at the template above, you will note the router-outlet element right below the navigation menu. When we visit /home, that’s where HomeComponent template will be rendered. The same happens for the other components. Routing 224 RouterLink using [routerLink] Now that we know where route templates will be rendered, how do we tell Angular to navigate to a given route? We might try linking to the routes directly using pure HTML: 1 Home But if we do this, we’ll notice that clicking the link triggers a page reload and that’s definitely not what we want when programming single page apps. To solve this problem, Angular provides a solution that can be used to link to routes with no page reload: the RouterLink directive. This directive allows you to write links using a special syntax: code/routes/routing/src/app/app.component.html 3 4 5 6 7 8

Router Sample