Angular From Scratch
Angular From Scratch
Ajdin Imsirovic
This book is for sale at http://leanpub.com/angular-from-scratch
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
Chapter 0: Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Software we’ll be using . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
What is Angular? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Let’s Build the Simplest Possible Angular App . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Viewing our Angular app in the browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Re-visiting Angular versioning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Updating our Angular version automatically . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Chapter 5: Binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
5.1 Binding Template Files to Class Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
5.2 Binding events in Angular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
5.3 Binding a (click) event to AppComponent template file . . . . . . . . . . . . . . . . . . . . . 54
5.4 Defining the randomColor function inside app.component.ts . . . . . . . . . . . . . . . . 55
5.5 Property binding in Angular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
CONTENTS
5.5.1 Dynamically update HTML attributes using property binding and method calls . . . 59
5.6 Binding a property to a button click . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
5.7 Comparing one-way and two-way data binding in Angular . . . . . . . . . . . . . . . . . 65
5.8 Examining Property Bindings in Angular in More Detail . . . . . . . . . . . . . . . . . . 66
Chapter 6: Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
6.1 Adding a service to our app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
6.2 Working with HttpClient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Chapter 7: Revision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
A revision of things we’ve learned . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
A revision of things we glossed over . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Explanation of the reason why this book was written the way it was . . . . . . . . . . . . . 83
Additional explanation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
What’s next? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Chapter 0: Introduction
Software we’ll be using
The software (the Operating System) that is being used in this book is the current, newest stable
version of Ubuntu Linux.
The reason for this choice:
• I have a Windows machine too. For many years, Windows didn’t support Ruby and Ruby on
Rails. It was a frustrating experience to “fight the OS” using varios workaround. Finally, I just
gave it up.
• A vast majority of servers are run on Linux. We, as web developers, will inevitably be faced
with having to do some work on the server. Getting used to working with Linux is very useful
and prepares us for our future work.
• Linux command line and Mac OS command line are quite similar.
• I’m more comfortable using Linux than Windows.
With all of this being said, it’s likely that you’ll be able to follow along using Windows just fine.
Still, I don’t test my code and my development setup on Windows, so I’ll leave it at that.
What is Angular?
Angular is a grouping of organized, pre-built code, that helps us build websites in a specific,
streamlined manner.
It’s a way to control the complexity of building a website - a tool we can wield to tame our web
development processes.
Of course, it’s not the only tool that we can use. There are many others, like: Vue, React, jQuery,
Ember, Alpine.js, Svelte, etc.
There are actually two separate versions of Angular:
This book (and the other Angular books in this series) deals with this “new” version of Angular.
Every six months, a new version of Angular comes out - currently, it’s at version 12.
Interesting note: to match the versions of the Angular router and Angular core, the version 3 was
completely skip. So the Angular 2+ history goes like this (version-wise):
• Angular 2
• Angular 4
• Angular 5
• …
As far as support goes, there are 6 months of full support for the currently-issued version of Angular,
then after a new version comes out, the previous version is supported for another 12 months (it’s
called LTS - Long Term Support).
TL; DR: Keep your Angular up-to-date, if at all possible.
Sometimes, updating is as easy as running ng update command - which auto-updates the code
inside your Angular app. The idea is to make this process as painless as possible, hopefully without
the need for manual fixes by us, the users of the framework.
Also, since the release cycle is so quick, there are usually no major changes from one version to the
next - it’s simply not possible to make too many major updates to a framework in such a short time
- even though the Angular team at Google is relatively large.
If Node.js and NPM are installed, we’d get back something like the following:
1 v14.16.0
2 7.11.1
¹https://www.codingexercises.com/guides/understanding-node-npm-and-javascript-modules
Chapter 0: Introduction 3
Of course, versions can be different - it’s best to be up to date, which, currently, is version 14 of
Node.js.
If you need to either install Node.js on your machine, or update to a newer version, please refer to
my article on codingexercises.com, titled: The Proper Way to Install Node.js on Ubuntu².
Now that we’ve got everything we need to get started, installing Angular is easy:
Once Angular is installed, you can generate a new Angular project like this:
1 code .
Note: if you don’t have VS Code on your computer you can download it here³.
Alternatively, you can use any other code editor, but in this book I’ll be using VS Code.
You might notice that the opened VS Code is showing a single folder in the sidebar panel, and the
folder reads: angulartestapp.
This means that when we ran this command:
Knowing what we know now, we could have ran this command instead:
³https://code.visualstudio.com/
Chapter 0: Introduction 5
Then we’d have the folder angularplayground holding our angulartestapp and down the line, if we
wanted to have another angular app in a separate sub-folder inside angularplayground, we’d just
run the command:
1 ng new someothername
Obviously, someothername would need to be a bit more meaningful name for our different Angular
app.
Still, I chose to do it like this so that you remember how this kind of setup works.
In this introduction, we won’t go into any other details. For now, let’s just serve our Angular app
(meaning: let’s make it appear in the browser).
We’ll do it by running:
1 ng s
We could have ran the full ng serve command, but honestly, why bother when we have this handy
shortcut command.
Here’s the output of running ng s in the console:
1 localhost:4200
Here’s what our app looks like in the browser at the moment.
1 ng --version
1 ng --version
We get the same output as before, which means we’re at the most-recent version.
In the next chapter, we’ll learn about the basic building blocks of Angular.
Chapter 1: Inspecting the Boilerplate
Angular App
In this chapter, we’ll discuss the following topics:
1 ng --version
1 ng new my-app
Next, Angular CLI will ask us which stylesheet format we’d like to use, and we can just press the
ENTER key to accept the default CSS option.
• We get an entire copy of the Angular framework and all its dependencies installed inside our
<app name> directory. These dependencies get added to the node_modules sub-directory of our
project. In our example app, we’ve named it my-app so the Angular framework is saved inside
my-app/node_modules.
• A barebones Angular app gets installed inside the ./src/app folder.
Next, we’ll inspect this boilerplate, skeleton app that just got generated.
Inside the src/app folder, Angular CLI generated the following files for us:
Chapter 1: Inspecting the Boilerplate Angular App 10
Some other files and folders get generated too, but they are not important for our discussion.
We need to understand how the files that got generated in ./src/app folder work together.
To be able to have a better understanding let’s rearrange these generated files by order of importance.
We’ll also give all these files some “nicknames”:
Now we can turn the entire list of files above, into a simple diagram:
Chapter 1: Inspecting the Boilerplate Angular App 11
Feel free to print out the above diagram and keep it above your monitor. It’s going to to wonders for
understanding how things fit together while we’re at the very beginning of working with Angular.
We’ll be referring back to this diagram again and again throughout this chapter.
Next, we’ll discuss how each of these files work together.
Here is the contents of the Class file (i.e the app.component.ts file). We’ve added some additional
visual cues to make it easier to see how everything fits together.
Chapter 1: Inspecting the Boilerplate Angular App 12
• It defines some meta information for the AppComponent inside the @Component decorator.
• It exports the AppComponent class to make it available to other parts of our app.
Chapter 1: Inspecting the Boilerplate Angular App 13
Additionally, the Class file defines all the logic for our component. Currently, there is no logic.
Instead, only the title property is set to my-app, but there are no methods inside our class
whatsoever.
Notice also that this file does several things in order:
• import
• decorator
• class export
This import-decorator-export file structure is common throughout Angular, as we’ll see later on in
this book.
1 <!-- The content below is only a placeholder and can be replaced -->
2 <div style="text-align: center">
3 <h1>
4 Welcome to {{ title }}!
5 </h1>
6 </div>
7
8 <router-outlet></router-outlet>
⁴https://gist.github.com/ImsirovicAjdin/bd2b65218115c7f54458e411521f76a3
Chapter 1: Inspecting the Boilerplate Angular App 14
Now that we’ve simplified our code, we can see more obviously that it’s just mostly regular HTML.
However, there are a couple additional things that are not the usual HTML:
The <router-outlet> is simply a placeholder that will mount into the DOM whatever component
is set to be placed on a certain route. More about that later. For now, let’s discuss the title.
In a nutshell, the title variable is displayed on the screen using what’s commonly known as
variable interpolation. Many languages and frameworks other than Angular also employ variable
interpolation extensively.
In this specific case, the title is the variable that’s made available by our component’s Class file
(i.e app.component.ts), and we can use it inside our template file (i.e app.component.html).
Whatever we set as the title inside our Class, will be displayed between these double curly brackets.
The {{ }} syntax is commonly known as “mustache” syntax. Some refer to it as “a template
expression”. It’s one of the simplest ways to get some data from the Class to the template in Angular.
Before we continue, we’ll take a look at our updated app being served in the browser.
Chapter 1: Inspecting the Boilerplate Angular App 15
To view the default app that was created by ng new my-app command, we need to go back to our
command line and first change into the app directory with this command:
1 cd my-app
1 ng serve
To view the barebones Angular app served in the browser, we’ll open the following URL:
localhost:4200.
The CSS file of App Component is currently empty. At this point of our examination of the default
barebones app, we will not add any changes to it. The only important thing to note about the CSS
file for our App Component is that whatever CSS properties we specify in this CSS file, those CSS
properties will be applied only to the App Component.
Next, we’ll discuss the app.module.ts file (the file that we dubbed “MDL”).
The app.module.ts file, the one that we called MDL in our diagram, has three distinct sections:
• the import
Chapter 1: Inspecting the Boilerplate Angular App 16
Here is the contents of the app.module.ts file, with the above three sections marked in magenta
squares:
The first section, the one with the import statements, is grouped into two sub-sections with two
imports each.
Chapter 1: Inspecting the Boilerplate Angular App 17
The first sub-section is NgModule. The official documentation provides an in-depth explanation of it.
A simplified explanation is that an @NgModule is a decorator.
The second sub-section imports the BrowserModule.
As can be read in the official docs, the BrowserModule exports all the required infrastructure for
all Angular apps. To be more specific, the BrowserModule gives us some browser-specific services
including DOM rendering and sanitization.
We’ve seen a decorator earlier, when we discussed the app.component.ts file. The decorator we
discussed there was the @Component decorator.
Similar to the @Component decorator, any other decorator - including the @NgModule decorator, takes
a metadata object.
In the case of the @NgModule decorator inside the app.module.ts file, this @NgModule decorator
describes how to compile the App Component. It basically serves to describe how the entire app’s
“plumbing” works, as it lists out all the necessary parts: declarations, imports, providers, and
bootstrap (the entry or root) component.
After we’ve imported the BrowserModule and the @NgModule, we are also importing the following
two modules:
With that, we now understand what the first section of the app.module.ts file does: it simply imports
all the things we will need to pass to the metadata object (the object literal) inside the @NgModule in
the second section of the file.
Here’s the code of the second section of the app.component.ts file:
1 @NgModule({
2 declarations: [
3 AppComponent
4 ],
5 imports: [
6 BrowserModule,
7 AppRoutingModule
8 ],
9 providers: [],
10 bootstrap: [AppComponent]
11 })
Chapter 1: Inspecting the Boilerplate Angular App 18
As we can see in the code above, we specified four keys inside the metadata object. They all describe
all the components, modules, data providers, adn the entry point for the app.module. Specifically:
The third section of app.module.ts is the export statement, where we’re exporting the AppModule
class.
Next, we’ll look into our app routing module.
After the imports, we specify the routes constant, which is currently an empty array, but we’ll soon
add to it.
The @NgModule decorator’s metadata object literal comes with two entries: imports and exports.
Finally, we just export the AppRoutingModule class.
Next, we’ll look at where and how our Angular app gets injected inside the index.html file.
The index html file is the entry point for our Angular app
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="utf-8">
5 <title>MyApp</title>
6 <base href="/">
7
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 <link rel="icon" type="image/x-icon" href="favicon.ico">
Chapter 1: Inspecting the Boilerplate Angular App 21
10 </head>
11 <body>
12 <app-root></app-root>
13 </body>
14 </html>
Inside the above code we can see a custom <app-root> tag. This is the tag into which the entire
Angular app will get mounted.
While our Angular app is open in the browser, we can press the F12 key to open the developer tools.
On the Elements tab, we can see the updated DOM structure of our web page. Notice that our entire
Angular app is inserted into the <app-root> tag.
We can see the effects of the mounted app using the developer tools
We’ve already covered all of this, but now is a good time to reiterate how our app gets mounted:
1. Inside the ./src/app folder, the app.module.ts file combines all the plumbing of the Angular
framework. It lists the metadata for our AppModule inside the @NgModule decorator, and specifies:
* the declarations (i.e components),
* the imports (i.e other modules it needs),
* the providers (i.e the sources of data), and
* the bootstrap file (which is the AppComponent file)
2. The bootstraped AppComponent is the name of the class we’ve exported at the very bottom of the
app.component.ts file. The app.component.ts file has its own metadata object literal passed inside
the @Component decorator. The first entry inside the @Component decorator is the custom HTML tag
for our AppComponent. This is the selector entry, and its value is set to app-root.
In a nutshell, this is how our Angular app gets loaded into the index.html file. In turn, this file gets
loaded into the browser at localhost:4200 when we run the ng serve command.
Chapter 1: Inspecting the Boilerplate Angular App 22
We’ve covered the contents of the generated app folder, and the index.html file in the very root of
our project.
• first, every ng new ... app comes with Git installed and, once the boilerplate app is ready, the
first commit is made, with the commit message of “Initial commit”
• second, I’ve tried my best to keep all the progress in our book in nicely-organized commits. This
project’s history is available at the following URL: https://github.com/ImsirovicAjdin/angular-
book-1⁵
1 ng g c navigation
Obviously, the g is short for generate, and the c is short for component.
This is the output of the above command in the console:
Here is the updated file structure of the ./src/app folder after Angular CLI generates our navigation
component:
Chapter 2: Generating components 24
If you open the navigation folder, you’ll see the following files:
Note that our navigation component’s structure is exactly the same as what we had in the
AppComponent. The only difference is that the AppComponent had two additional files: the
app.module.ts and app-routing.module.ts. This makes perfect sense, because Angular modules
are used to combine several components together, which is what we will do next.
If we were to describe our new navigation component’s files in a diagram, it would look almost the
same as the diagram for the AppComponent, minus the two module files:
Chapter 2: Generating components 25
Notice that in the above diagram, we are ignoring the testing file (app.component.spec.ts), just like
we did in the diagram of the App Component earlier in this book, in chapter 1.
Chapter 2: Generating components 26
Looking at the contents of the Class file of our navigation component, we can see that at the very
top, there is an import of Component and OnInit from the framework’s core.
Both the Component and the OnInit are examples of an interface.
In TypeScript (and some other languages), an interface is used when we want to make sure that a
class sticks to a contract of sorts. What this means is that a class will implement an interface. So
if we define an interface to have the property of name set to string, that means that a class which
will implement that interface will have to have a property name set to a string. So when we say that
an interface is sort of like a contract, what we mean is that if something is defined in the interface,
than that same thing should be used in a class implementing the interface.
On lines 3-7 of our code, the @Component decorator defines the selector, the templateUrl, and the
styleUrls - just like we saw in the App Component’s class.
Next, we export the NavigationComponent class. It implements the OnInit interface. The OnInit
interface is a part of the component lifecycle mechanism. A component, during its life, gets
initialized, added to the virtual DOM, etc. The ngOnInit() allows us to tap into this lifecycle and do
Chapter 2: Generating components 27
something at a certain point of a component’s life: in our case, when the component gets initialized.
Of course, the ngOnInit() is just one method inside the lifecycle sequence. There are others too.
Here’s the full list of lifecycle hooks:
• ngOnChanges()
• ngOnInit()
• ngDoCheck()
• ngAfterContentInit()
• ngAfterContentChecked()
• ngAfterViewInit()
• ngAfterViewChecked()
• ngOnDestroy()
Back to our NavigationComponent, on line 10 of its class file, we can see the call to a constructor:
1 constructor() {}
We inject dependencies of our class inside this constructor() method. On the other hand, the
ngOnInit is ran after the component’s DOM is built and injected, and all the dependencies that
came into the constructor are processed.
The constructor simply constructs our component.
Once a component is “alive”, we can use various lifecycle hooks. Since we want to add some logic
as soon as the component is mounted into the DOM, we tap into the ngOnInit moment of our
component’s lifecycle.
Of course, this was all generated for us, so at this moment we are not adding any updates to the
ngOnInit lifecycle hook.
So, there it is, we’ve just covered the code inside the class file for our newly generated
NavigationComponent.
Next, we’ll look at the template file and the CSS file of our NavigationComponent.
The Template file for our NavigationComponent is really simple, just a <p> tag with the text
“navigation works!” inside of it.
The CSS file for the NavigationComponent is currently empty.
Thus far, this is all the code for our navigation component.
Let’s now improve it, by first adding the actual navigation code to the Template file of our
NavigationComponent.
1 <nav>
2 <li><a href="#">One</a></li>
3 <li><a href="#">Two</a></li>
4 <li><a href="#">Three</a></li>
5 </nav>
Ok, so that’s easy enough. Next, we need to actually display this code on our webpage.
1. When we open our localhost:4200, a web page displays the text “Welcome to my-app!”.
2. We know that these words come from app.component.html file.
1 <app-navigation></app-navigation>
2
3 <div>
4 <h1>
5 Welcome to {{ title }}!
6 </h1>
7 </div>
8
9 <router-outlet></router-outlet>
Now we can save and refresh, and this is what we’ll see in the browser:
Chapter 2: Generating components 29
Now we can refer back to the schematic display of our Angular app, and see how these two
components fit together right now:
Chapter 2: Generating components 30
A diagram of how the navigation component and the app component fit together
We can conclude from the image above that the NavigationComponent is simply a child of the
AppComponent. However, there is more to it than just adding the <app-navigation> custom tag to
the AppComponent template file, and it has to do with Angular automatically updating files when a
component gets created.
As discussed earlier, there were four import statements initally. Now there are five. The fifth import
statement reads:
Of course, as it was mentioned earlier when we were looking at how @NgModule works, we need
to declare all the components that the app module will use. Initially, when we generated our
app, the declarations only had a single file, the AppComponent. Now, it’s updated to include the
NavigationComponent too:
1 declarations: [
2 AppComponent,
3 NavigationComponent
4 ]
A screenshot of our app module file’s changes highlighted by Git after running the ng generate command to generate
the navigation component
This is all that it takes for a new component to be added to a module in Angular. Even better, Angular
does this automatically whenever a new component is generated. Now that we know how this works,
we will have no problems to add other components and understand what behavior to expect and
how everything fits together.
1 /* navigation.component.css */
2 nav {
3 height: 100vh;
4 float: left;
5 width: 150px;
6 background: tomato;
7 list-style-type: none;
8 }
9
10 nav li {
11 font-family: Arial;
12 font-size: 16px;
13 padding: 12px 20px 5px;
14 border-bottom: 1px solid white;
15 }
16
17 nav li a {
18 color: white;
19 text-decoration: none;
20 }
A screenshot of our app module file’s changes highlighted by Git after running the ng generate command to generate
the navigation component
Ok, so the design is nothing to write home about, but at least we now have a sidebar navigation.
Chapter 2: Generating components 35
1 .p20 {
2 padding: 20px;
3 }
The reason for this is the fact that we need to update the styles of the DOM-generated app-root
component.
There are several ways to do it, but an interesting (and easy) approach is to use the :host selector
inside the app.component.css file.
1 :host {
2 display: flex;
3 }
4 .p20 {
5 padding: 20px;
6 }
A screenshot of the result of using the :host selector in app component’s css
Next, we’ll add another component, and we’ll call it ProfileComponent. This time, however, we
won’t generate it, but rather write it from scratch.
Chapter 2: Generating components 38
Note: If you’re curious to see the changes made in the second chapter in our Git repo,
navigate to the commit message titled https://github.com/ImsirovicAjdin/angular-book-
1/commit/30c000ad5e652c8251964683dfe09dcd1d957466⁶.
⁶2.Generatethenavigationcomponentandupdatestyles
Chapter 3: Writing an Angular
component from scratch
In this chapter, we will:
1 ____________________________________
2 | | | | |
3 | Any Component: | C | T | S |
4 |________________|_____|_____|_____|
Looking at the above “diagram”, we can say that any Angular component must have at least three
files:
Next, still inside the app.module.ts file, let’s add the newly imported component to declarations
inside @NgModule decorator:
1 @NgModule({
2 declarations: [
3 AppComponent,
4 NavigationComponent,
5 ProfileComponent
6 ],
Now we’re done with the app.module.ts file, and we can go on by adding a new folder inside
./src/app. We’ll call it profile. Inside the profile folder there will be three new files:
• C, profile.component.ts
• T, profile.component.html
• S, profile.component.css
1 // ./src/app/profile/profile.component.ts
2 import { Component, OnInit } from '@angular/core';
3
4 @Component({
5 selector: 'app-profile',
6 templateUrl: './profile.component.html',
7 styleUrls: ['./profile.component.css']
8 })
9 export class ProfileComponent implements OnInit {
10
11 constructor() { }
12
13 ngOnInit() {
14 }
15
16 }
Before we continue, we can see that this component’s class is almost identical to the
navigation.component.ts class.
This leads us to an interesting conclusion: let’s say that we could replace any component’s name
with this string: <component-name>. In that case, we could say that any component, when it gets
generated, has the following structure:
Chapter 3: Writing an Angular component from scratch 41
Next up, we’ll add the template file (the “T” in our diagram) for the ProfileComponent:
Finally, the styles file (the “S” in our diagram), as we’ve already come to expect, is initially just an
empty file called profile.component.css. However, in order for our profile component not to be
glued to the sidebar, we need to add it the same styles that we added to the app component. So let’s
just copy the styles from app.component.css and paste them into profile.component.css:
1 .p20 {
2 padding: 20px;
3 }
At this point, your app should refresh, and be working. If it is not, you might have to stop the
ng serve command by pressing the CTRL c keyboard shortcut. After stopping the Angular server,
simply restart it. Your app should be served fine.
Our app at this point is saved in the commit message titled: 3.1. Add the profile component without
using the ng generate component command⁷
⁷https://github.com/ImsirovicAjdin/angular-book-1/commit/5c7efba1d6967ea8de5108baa9c0b4e17859ef94
Chapter 3: Writing an Angular component from scratch 42
1 /* ./src/styles.css */
2 .p20 {
3 padding: 20px;
4 }
Now we can delete the .p20 class from both the app.component.css and the profile.component.css
files.
This updated commit is saved under git commit titled: 3.2 Add a global style and clean up the
component styles⁸
Next, we’ll add a new view to our router, and that new view will show our profile page.
⁸https://github.com/ImsirovicAjdin/angular-book-1/commit/6e0d14ddcf0017c704d5ad2f1fae39d58be3ffe9
Chapter 4: Using Routing and Adding
a Login Component
In this chapter, we’ll show the profile page using Angular routing.
Specifically:
Next, we’ll need to go to the navigation component’s template, i.e the navigation.component.HTML
file, and change the contents so that it looks like this:
1 <nav>
2 <li><a routerLink="/">Home</a></li>
3 <li><a routerLink="/profile">Profile</a></li>
4 </nav>
Chapter 4: Using Routing and Adding a Login Component 44
What have we just done here? We have used a custom Angular attribute, routerLink, and we have
set the root route to Home, and the /profile route to Profile.
Let’s inspect our updated site in the browser.
However, this is just an HTML template. There is no logic behind these routes. In other words,
although the links are showing in the website’s navigation, we have not specified how these routes
should work whatsoever.
The Angular way of specifying routes is to add them to the app-routing module, which we’ll do
next.
1 {
2 path: 'profile',
3 component: ProfileComponent
4 },
The path property sets the actual URL address that will be appended to the base URL of our app.
So, if the base URL of our app is localhost:4200, then the ProfileComponent will be served on this
path: localhost:4200. In other words, this will be our root route.
Likewise, if the base URL of our app is localhost:4200, the ProfileComponent will be served on this
path: localhost:4200/profile.
Before we can save our updated app-routing.module.ts file, we need to make sure that the
components we’re referencing inside the Routes array are imported. Thus, the fully updated
app-routing.module.ts file will look like this:
However, when we click the Profile link in the navbar, we get the following:
… to this:
However, if we just left it like this, it means that our site visitors would not see the greeting Welcome
to {{ title }}!, ever.
Before we fix this “problem”, let’s step back outside the Angular world, and think about it logically.
What’s the one thing that we always need to have on a website, regardless of what page we’re on?
Well, beside the obvious navigation, the answer might be: a logo, or a footer.
Let’s go with the logo.
We’ll just change the HTML from this:
1 <div class="p20">
2 <h1>
3 Welcome to {{ title }}!
4 </h1>
5 </div>
…to this:
1 <div>
2 <h1><a routerLink="/">{{ title }}</a></h1>
3 </div>
Let’s also update our styles so that our app looks nicer.
We’ll update several files:
Chapter 4: Using Routing and Adding a Login Component 48
1. In src/app/app.component.html, we’ll update the h1, and give it an anchor using routerLink
2. In src/app/app.component.css, we’ll update the :host styles
3. In src/app/navigation/navigation.component.css, we'll update the nav and nav li‘
selectors
1 :host {
2 display: flex;
3 flex-wrap: wrap;
4 flex-direction: column;
5 }
1 /* navigation.component.css */
2 nav {
3 height: 37px;
4 width: 100%;
5 display: block;
6 background: tomato;
7 list-style-type: none;
8 }
9
10 nav li {
11 font-family: Arial;
12 font-size: 16px;
13 padding: 12px 20px 5px;
14 border-right: 1px solid white;
15 display: inline-block;
16 }
Chapter 4: Using Routing and Adding a Login Component 49
17
18 nav li a {
19 color: white;
20 text-decoration: none;
21 }
Viewing our site in the browser, after the above changes, we get this:
This slight detour in the development of our app was taken so as to highlight a couple of points:
1. We need a more robust way of dealing with the styles (which we’ll mark as an item on our
app’s “todo list”, and deal with it later in the book)
2. We needed a reminder of how to add some code that will show up in all pages of our app.
While the first point is something to take care of later, the second point leads to an interesting
conclusion:
To repeat a piece of code in our App module, we can either add it right inside the App component’s
template file, or we can import it as a separate component.
It might be an obvious conclusion, but it’s worth repeating as it’s one of the cornerstones of how we
build apps in Angular.
We’ve made enough incremental updates to our app to warrant another Git commit, so let’s name
this one as follows: 4.1 Turn the sidebar nav into a horizontal nav¹⁰.
Let’s move on now, and practice adding another component to our App module.
We’ll begin with steps number 1 and 2 from the above list. We’ll add the ./src/app/login com-
ponent, and we’ll add the login.component.ts, login.component.html, and login.component.css
files.
Here is the code for the class file:
1 // login.component.ts
2
3 import { Component, OnInit } from '@angular/core';
4
5 @Component({
6 selector: 'app-login',
7 templateUrl: './login.component.html',
8 styleUrls: ['login.component.css']
9 })
10 export class LoginComponent implements OnInit {
11
12 constructor() { }
13
14 ngOnInit(): void {
15 }
16
17 }
The style file (login.component.css) will remain empty, but we’ll still add it in case we might need
to add some styles later.
Next up, after we’ve imported the LoginComponent to app.module.ts, we’ll add it to the list of
declarations inside the @NgModule decorator.
Next, we’ll import the LoginComponent so that we can update the Routes array inside the
app-routing.module.ts, with this new path:
Finally, we’ll add a new routerLink inside navigation.component.html, pointing to the /login path:
1 <nav>
2 <li><a routerLink="/">Home</a></li>
3 <li><a routerLink="/profile">Profile</a></li>
4 <li><a routerLink="/login">Login</a></li>
5 </nav>
In the next chapter, we’ll look at how to bind HTML in our templates to events.
Chapter 5: Binding
5.1 Binding Template Files to Class Files
There are many ways to connect template files with class files in Angular components. Any kind of
binding between these two files is what’s known as data binding.
There are 3 kinds of data binding in Angular:
• Interpolation (we’ve already seen that; it’s simple variable interpolation using the mustache
syntax)
• Event binding (handling events): an example is a (click) event binding; all event bindings are
surrounded with a pair of parentheses, i.e ()
• Property binding: we bind any HTML element’s property using the square brackets, i.e []
We’ll be adding a button with a (click) event. The updated template file will look like this:
Chapter 5: Binding 55
As we can see above, a (click) event will run a randomColor function. We’ll define it next.
We’ll add our randomColor function definition inside the app.component.ts file. This is the code:
Let’s now try clicking the button inside the AppComponent (on the root route). We’ll get an alert with
a custom HEX color.
What we just did here? We’ve setup a click event handler inside a component’s template file. This
handler is simply a call to a function, which we’ve defined in the component’s class file.
What this means is that all the component’s logic lives inside a component’s class file.
Now’s a good time for a git commit. Let’s name this commit 5. Bind events in the component template
file to the logic in the component class file¹².
1 <table [border]>...</table>
Above, using square brackets, we’ve bound the border html attribute on the table tag inside the
template file of a component, to its class file.
Let’s say that again.
We’ve bound:
• the border html attribute (the one on the tabletag inside the template file of our component)
To:
Now we can set the border inside the class file, like this:
¹²https://github.com/ImsirovicAjdin/angular-book-1/commit/2b6ad3a194210e8fdcd18fade96255a2be12cd85
Chapter 5: Binding 57
We’ve just set our table’s border to the width of 10 pixels. Let’s now write the actual code and really
add this to our app.component.html. This is the updated file:
1 <app-navigation></app-navigation>
2
3 <div>
4 <h1>
5 <a routerLink="/">{{ title }}</a>
6 </h1>
7 <button (click)="randomColor()">Give me a random color!</button>
8
9 <table [border]="this.border">
10 <tr>
11 <th>Year</th>
12 </tr>
13 <tr>
14 <td>2021</td>
15 </tr>
16 <tr>
17 <td>2020</td>
18 </tr>
19 </table>
20 </div>
21
22 <router-outlet></router-outlet>
Let’s save these changes in Git, with this commit message: 5.1 Bind an HTML attribute in the
component template file to a property in the component class file¹³.
Let’s also review the updates in the browser.
¹³https://github.com/ImsirovicAjdin/angular-book-1/commit/01a8520d3fe05455fbc98b535cefd9a6b731c7fb
Chapter 5: Binding 59
Our Angular app after adding the table and binding its border to a property in the component’s class file
If you ran the above code, you’d get the following error:
Now our error is gone, and the easiest way to verify it using VS Code:
No errors in VS Code
Now our app works again, and every time we refresh our app on the root route, we get a border
with a dynamically set width. This time we’ve also logged out the function definition to the console,
using the constructor method.
Logging the value of table border width to the console using the constructor method
These updates are ripe for another git commit: 5.2 Dynamically update html attributes using property
Chapter 5: Binding 63
This approach will not work, but let’s look at how we might write this in code. Note the question
marks (i.e “????”) in places where we simply do not have an easy way to actually assign values we
need without the compiler throwing errors:
Since the above approach will not work, let’s look at an actual solution:
¹⁴https://github.com/ImsirovicAjdin/angular-book-1/commit/eea75adaafc9c8a32914f1faa6cfc2be1fe0c9f7
Chapter 5: Binding 64
Looking at the above code, we can see that it’s actually very elegant. Inside the class, we first have
our variables: title, border, and color.
We can skip the constructor (since we already explained that it’s just an easy way to console.log
values from a class member).
The interesting bit, and the solution to our little wrinkle, is in the updated code of the
randomWidthAndCustomColor(), where we assign the Math.random() calculations to this.border
and this.color, respectively.
Now we can update the app component’s template file, like this:
This time, every time we refresh it, it will show a different table background and a different width
on the table border.
The Git commit for this improvement will also hold the previous code, commented out - that way
we’ll be able to more easily compare the updates, in a single commit, rather than comparing them
across two or more commits.
Note that committing commented code is not a good practice, and we won’t keep doing it in this
book. This is more of a one-of exception.
This Git commit message reads: 5.3 Binding properties to button click events¹⁵.
Next, we’ll discuss one-way versus two-way data binding.
In the first two cases (variable interpolation and property binding), the data goes in one-way, from
the data source (in our example, the class file), to our view template.
In the case of event binding, the data flows from the view to the class (i.e to the data source).
Two-way binding is simply a combination of these two data flows: the data can flow from the view
to the class, and it can also flow from the class to the view.
This is the syntax of two-way binding in angular:
1 [(target)]="expression"
This syntax of square brackets surrounding round brackets - the [()] syntax - is known as the
‘banana in a box’ syntax.
This two-way binding syntax is simply a combination of both versions of one-way data binding, so
we could rewrite banana in a box to this:
1 <p [innerText]=""></p>
2
3 <p [style.color]="styleColor">
4 Lorem ipsum dolor sit amet
5 </p>
The official Angular documentation refers to Angular’s HTML templates as “HTML plus”, meaning
it’s more than just plain old HTML. It also mentions that instead of looking at property bindings as
simply a way to interact with HTML attributes, it is rather a way to interact with DOM properties.
It even lists the differences between regular HTML attributes and DOM properties, which is an
important and insightfull read.
So, to aid in our understanding of the property binding syntax, we’ve made an intentional error
earlier in the text, and now’s the time to fix it: property binding in Angular does not work on
HTML attributes, but rather on DOM properties!
To quote the official docs: “The HTML attribute and the DOM property are not the same thing, even
when they have the same name.”
Let’s now extend our Angular app with some additional property binding.
Open the app.component.html file and update the <table> element to this:
We’ve updated the first <th> element to [innerText]="this.yearTitle", so now we need to specify
the value for yearTitle in our component’s class file, namely in app.component.ts:
Chapter 5: Binding 67
We’ve just dynamically set the innerHTML property on an HTML element using property binding
in Angular! This update now adds the text “The year” to the first cell of the HTML table under
the “Click me!” button. Note how the innerText property binding overrides the static “Year” text
between the p opening and closing tags.
We’ll save this in another git commit message: 5.4 Binding innerHTML property¹⁶.
Next, we’ll add some services to our app, so that we can work with data.
¹⁶https://github.com/ImsirovicAjdin/angular-book-1/commit/06906fd03e7c29eb44721d0be462253637924f59
Chapter 6: Services
Services in Angular are used as data providers.
For example, an Angular service can get some data from the server, and inject it into a component.
A service has some methods defined to call a server and get data.
We can then call these methods from our components’ classes, and then pass this data on to our
components’ template files.
Thanks to the two-way data binding, we can easily update the data model from our template files
too.
Let’s add a service to our example app, so that we see how it works.
Similar to what we’ve seen for generating components, we can use shorthand syntax here as well,
which would simply be:
1 ng g s somedata
Once we run this command, our app will be updated with the following:
Let’s commit this generated code: 6. Run ng g s somedata to generate the SomedataService¹⁷.
Similar to @Component and @NgModule, the @Injectable is a decorator. This decorator only shows
the providedIn property inside its object literal. It also exports the SomedataService class, with an
empty constructor function.
As we can see, the service is as barebones as it can get.
19 this.border = Math.round(Math.random()*100);
20 this.color = '#'+Math.random().toString(16).substr(-6);
21 }
22 }
We’ll save this update in another commit, with the message of: 6.1 Remove redundant comments¹⁸.
Now we can go back to our generated service. We’ll use a method from the service somewhere else
in our code.
1 someMethodFromService() {
2 return 42;
3 }
Now let’s go back to the App Component’s class file (app.component.ts), and import the
SomedataService class (on line 2):
Now we need to access our service data. This is done by simply passing a private argument to the
constructor function inside the exported AppComponent class:
Now that we have the data from SomedataService available, we can use it anywhere inside our
AppComponent class. For example, inside the randomWidthAndCustomColor() method, we can pass it
to the this.border value:
¹⁸https://github.com/ImsirovicAjdin/angular-book-1/commit/1a52db29c9bff10008b30191d697b48cce6b2a1b
Chapter 6: Services 71
1 randomWidthAndCustomColor() {
2 this.border = this.data.someMethodFromService();
3 this.color = '#'+Math.random().toString(16).substr(-6);
4 }
What effect will this have on our app? Of course, the this.border data now comes from the
someMethodFromService() - a function that lives inside our SomedataService class. And since this
value is set to 42, the border of the <table> element will no longer change.
However, not all is smooth sailing here. A problem that we’re facing at this point is that, instead of
getting the actual app in the browser, we get the “Failed to compile error message”.
Let’s inspect the console and see if we get more info there:
Hm, that’s quite a weird message. After all, I’m pretty sure I’ve just added the someMethodFromSer-
vice() function to my SomedataService.
So what’s happening here?
TypeScript is touted as a “superset of JS” - and it comes with types. Meaning, we need to add the
return type to our someMethodFromService() method definition for it to work correctly.
The updated code looks like this:
1 someMethodFromService(): number {
2 return 42;
3 }
The first line in the above code snippet determines that this function’s return value must be a number.
Now our app works again - everything compiles in the console successfully and the app displays in
the browser.
It is a bit unfortunate that the specific error we were faced with was too vague. This requires some
explaining.
Of all the frontend technologies I’ve worked with, the Elm language is the only one that, for me, has
the satisfactory level and utility of error messages. It’s a pleasure to work with. The Elm language
inspired many concepts that are today widely used in the frontend world:
Chapter 6: Services 72
6.1.4
Services are used to communicate with some data source. In the above example, we just hard-coded
some values inside the SomedataService class itself.
However, a real-world scenario includes getting the actual data from an api. Angular comes with
its own http library, called HttpClient. We can use HttpClient to have some data provided from a
server.
In this book series, that backend will be powered by Node.js.
However, starting to introduce Node.js at this point would simply be a bit too much, so we’ll have
to wait just a bit more for the Node.js implementation and instead we’ll first see how to work with
HttpClient.
Also, let’s not forget to add another Git commit: 6.2 Fix the error: property someMethodFromService
does not exist on type SomedataService¹⁹.
¹⁹https://github.com/ImsirovicAjdin/angular-book-1/commit/9e50c867f94865195a6875b2fefd088dedece0f2
Chapter 6: Services 73
Now we need to import the HttpClient to the somedata.service.ts file, and inject this dependency
inside our class’ constructor with a private property, like this:
1 someMethodFromService(): number {
2 return this.http.get('https://api.mathjs.org/v4/?expr=42');
3 }
1 someMethodFromService(): Observable {
2 return this.http.get('https://api.mathjs.org/v4/?expr=42');
3 }
Now we get a different error, with the Observable keyword underlined with a squiggly red line -
signifying the offending code. When we hover over it, the error now reads:
Ah, so that’s what we need to do: we need to specify the actual returned type instead of a <T>. In
other words, we need to do this:
1 someMethodFromService(): Observable<Object> {
2 return this.http.get('https://api.mathjs.org/v4/?expr=42');
3 }
Now all our errors are gone, as far as our someMethodFromService() is concerned.
However, we still need to fix another error, which we can see in the console.
Namely, this:
Note: It might happen that you have many files on your machine and it’s kind of unclear exactly
where you’ve saved your Angular project. In that case, there’s a roundabout way of getting
Chapter 6: Services 75
where you need to be. Open VS Code, then click File > Open Recent > …, and you’ll see a list
of recently open folders and files. In my case, the first entry (the most-recently opened one), is:
∼/angulartestapp/my-app. Once you’ve reopened the files, right-click on any file in the sidebar of
VS Code, and click on “Open Containing Folder”. Once inside this folder, if needed, navigate to your
project’s root folder, i.e to the my-app folder. Now you can right-click anywhere in the blank space
of this root folder in your file explorer, and click on the “Open in Terminal” command. This will
open your terminal in the root of your Angular app, and now you can simply run ng serve.
Your Angular app will not serve since we had the error the last time we ran it. This time, instead of
seeing the app in the browser, we’ll have the following screen:
Now that we see this error on re-start, it time to update our code so that we’re error free and
everything works again.
1 someMethodFromService(): number {
2 return 42
3 }
1 https://api.mathjs.org/v4/?expr=40
One of the reasons why our code does not work is due to the fact that:
To consume a service in our app.component.ts, we must subscribe to it.
This is a broad subject, but let’s not get too technical here - instead, we’ll just make it work.
Let’s open app.component.ts and locate the randomWidthAndCustomColor() method definition. We’ll
update it as follows:
1 randomWidthAndCustomColor() {
2 this.border = this.data.someMethodFromService().subscribe(res => {
3 console.log(res);
4 });
5 // ... code truncated for clarity ...
This update will now cause our border property to highlight as broken (erroneous code), so let’s
update that as well:
1 border: any;
What we did above is: we’ve set the border property to accept any type, and we’re also not assigning
it any value. We’re just declaring a variable, without a value, but whose type definition accepts any
type. This makes it less error-prone, but also less precise. Another big topic that we’ll skip for now,
just so that we can debug our app.
Next, we’ll define our data model, by adding a new folder to our app. We’ll call that folder models,
and this will be its path: src/app/models.
Inside the models folder, we’ll add a new file, and we’ll call it numberFromService.model.ts.
Here’s our new file’s contents:
Chapter 6: Services 77
Now let’s save all the changes, and we’ll get the following result in the browser:
Great, our app works again! We’re error-free, although it’s not doing exactly what we want. Let’s
fix that next.
6.2.2 Binding the value returned from the service to the value of
this.border in the randomWidthAndCustomColor() method in App
Component’s Class file
We’re getting closer to our goal. The next step in our solution is to update the definition of
randomWidthAndCustomColor() to this:
Chapter 6: Services 78
1 randomWidthAndCustomColor() {
2 this.data.someMethodFromService().subscribe(res => {
3 this.border = res;
4 });
5 this.color = '#'+Math.random().toString(16).substr(-6);
6 }
Now we can finally get a random number from the API call.
1 someMethodFromService(): Observable<Object> {
2 const params = Math.round(Math.random()*100);
3
4 return this.http.get(`https://api.mathjs.org/v4/?expr=${params}`);
5 }
Every click on the button results in a new border width on our table
Chapter 6: Services 79
It’s high time for another commit, so let’s add all our changes to git and save it with the following
commit message: 6.3 Make the service dynamically provide a random number from a 3rd-party API
and consume it in the frontend of our app²⁰.
This wraps up chapter 6: Services.
In the next chapter, we’ll revise what we’ve learned in this book and we’ll discuss some of the things
that we’ve skipped.
²⁰https://github.com/ImsirovicAjdin/angular-book-1/commit/2890d61e990d629365c6bad1a00df28cb883944e
Chapter 7: Revision
Angular is a mature and stable framework that offers a lot out-of-the-box.
While the learning curve might be a bit steep, it offers great DX (developer experience) and a way
to streamline your team’s work.
A funny anecdote I’ve read online has to do with a small company of about 50 developers where
half of the employees were working with Angular, and the other half was working with React.
One of the Angular developers wrote that while they were constantly delivering their projects on
time, and had enough energy left to go grab some beers from time to time after office hours, the
React team was having to do overtime work most of the time - and of course, had to debug weird
issues instead of relax a bit.
Obviously, the above story is anecdotal, but I have a hunch it might be true.
Out of all the frontend frameworks I’ve worked with, Angular takes the high runner-up position -
i.e the second place.
The first place, as far as DX is concerned, still goes to the Elm language, but that’s a topic for another
book.
Due to its popularity and relative ease of use and its streamlined approaches, I’d even say that
Angular might share place number one with Elm - at least in my experience.
Anyway, enough with singing praises to Angular.
Let’s review what we’ve learned in this book.
37. Each such object has two properties: path and component
38. There are several ways to bind template files to class files - this is data binding.
39. We can data-bind in tree ways, through: 1. interpolation, 2. event binding, and 3. property
binding
40. We bind events using round brackets: ()
41. We bind properties using square brackets: []
42. We set the values of data in our component’s Class file, e.g border = 10
43. We can dynamically update HTML attributes in our templates using property binding and
method calls in our class files
44. We can bind properties to button clicks by updating properties inside the methods called by
click events
45. Variable interpolation, property binding, and event binding are all examples of one-way data
flows
46. We can use the “banana in a box” syntax ([()]) to achieve two-way binding
47. We provide data to our angular apps using services
48. A service is generated using this code: ng generate service someservice, where the
someservice part is editable
49. The above command can be shortened to ng g s someservice
50. Data is provided through methods in our services
51. When we define a method in a service, we need to provide the expected return value of that
method
52. We use HttpClient to access third-party APIs (online data sources)
53. Angular errors can sometimes nudge us to successful debugging of our Angular apps
54. To consume a service in our component’s class file, we must subscribe to a service using the
syntax: this.data.someMethodFromService().subscribe(res => { // do something with
res here })
55. We use data model files to define the expected structure of our data, in an interface
56. To bind the value returned from a service, we assign whatever was the result of the subscription
to the property that we want to update. For example: this.border = res, where this.border
is the border property declared on the Class file and bound to in the Template file, and the res
variable is the result of the subscribe() call.
Unfortunately, we couldn’t fit all of these concepts in such a short book like this one, without making
it a lot more theoretical (and a lot longer).
1. This book comes with its own repo. I’ve referenced this repo throughout the book, and it should
serve as a quick way to check against your own errors as you read it.
2. The goal was to make this book into a tool that would give a complete Angular newbie a chance
to get to some level of comfort with Angular, in a neat and practical package. I dare say that
this entire book can be read, cover from cover - while following examples - in about 5-6 hours.
The above two considerations make this book very useful if you’re faced with the following
situations:
• you are a new developer in an Anglar team and need to get up to speed quickly
• you need to cram for an exam involving Angular
• you need to quickly get to some level of competence with Angular for a job interview
Additional explanation
In early 2018, I set out to write an in-depth book on JavaScript basics.
This single book soon turned out to be over a thousand pages long, so I’ve split it into three books.
Again, these three books started getting a bit on the heavy side, so I’ve again split them into the total
of five books.
It’s 2021, and those books are still in progress.
Chapter 7: Revision 84
What’s next?
This brings me to the topic of new books in this book series.
Currently, they are only a plan. I try not to plan too far ahead with these books - it’s not a productive
approach.
Thus, I’ve only planned to write the second book in this series, in the coming weeks/months -
depending on my workload.
Currently, I envision the Book two to cover in more depth the topics of:
In a few words, I’m planning to continue guiding the reader through the interesting world of Angular
web development.
Till then, I wish you all the best in your life and career.