1. News Flash
-
Released version v2.14.2
2. Release notes
-
Fix an error in the link to the site of Angular.io (modify http:/[]angular.io to http:/[/]angular.io)
-
Mention that installation of TypeScript is done through the angular cli installation process so no manual install of TypeScript anymore
-
Fix the link to the Angular CLI. Changed to http://cli.angular.io
-
Remove the Topic: in the Topics since it is double mentionning (like white snow?)
-
Rename title Modifying to Create a method
-
Add setting to pom to have selectable source code WITHOUT that annoying linenums
-
Change [source, javascript] to [source, javascript, linenums, options="nowrap"]
-
Rename some get methods which return a list to list() since the name should be more clear on this method
-
Add topic: Observable ⇒ Pipe
-
Add how to upgrade TypeScript
-
Add more explaination regarding what the --routing option does when creating angular app with $ ng new …
-
Explain the selector attribute of the @Component annotation more thoroughly after feedback of group
3. Structure of this training
For the summary of Angular see this page Link to Summary
-
Introduce topic
-
Why
-
When
-
How
-
This module explains the theory of installing typescript
-
-
Assignment
-
This modules lets you practise with the above learned theory
-
Target
-
Roadmap
-
Steps (although not always target, roadmap and steps are applicable)
-
Validation: this module validates that you understood this topic well enough and validates that your assignment is OK
-
-
Solution: this module shows a possible solution if applicable
-
Conclusion
Sometimes there is some more or less to mention since now every topic might have the question: when to do it … And sometimes there is no assignment applicable. In the end only wisdom counts :-)
4. Conventions of this document
When showing code which is to be executed in your Windows CMD or Linux Terminal, the "code" will be prefixed with a $ sign like this
$ ng generate component User
When showing code Angular (sometimes Java or C#) than the code will not be prefixed with a $ sign like this
1
2
3
4
export class User {
id: Number;
name: string;
}
When expressing the smaller than and the greater than sign around some value, the meaning is that you should replace the value INCLUDING the smaller and larger sign to a value of your choice, or a value which is applicable at that moment. e.g
$ ng new <appName>
or to enable routing directly for later usage (which is better now)
$ ng new <appName> --routing
can be changed by as example to e.g.
$ ng new myFirstBikeApplication
When there are three periods / ellipsis in some sourcecode in this document it means that there might be some more code in sourceblock on your machine but we are not focussing on that for now
5. Working through the courseware
During this training we will introduce the next part of the Angular framework and start working on it
We will use the same structure as the Tour of Heroes but we will use the link to the Fake REST api of JSON placeholder for convenience and to prevent to get too much in the details of creating a REST api. (which is NOT part of this training)
6. Using the REST service embedded in this course
There is (since 10-10-2018) an embedded REST api using NodeJS in this course which can be downloaded here
-
The Service has a REST service for a user with properties
-
id, name, username and email
-
GET http://localhost:8081/users (list users)
-
GET http://localhost:8081/users/:id (find user by id)
-
POST http://localhost:8081/users (create a user)
-
PUT http://localhost:8081/users/:id (update a user)
-
DELETE http://localhost:8081/users/:id (delete a user)
-
Hence, in fact a standard REST api with url http://localhost:8081 and endpoint users
-
Locate a directory where you want to make the REST service
-
cd to it and invoke
-
$ npm init (enter) (and enter the values as is for your convenience or just enter)
-
unzip the files of the zip above to that location (overwriting your package.json)
$ npm install
$ node app
-
You should now be able to run the (Angular) application (later) and/but use Postman on the http://localhost:8081/users URL
7. Abbreviations
-
CLI: Command Line Interface
-
The way of working in a DOSBox or Shell (Linux) to express statements
-
8. Introduction to Angular
8.1. Introduction: What is Angular
Angular is a opinionated framework which allows us to create web-applications using TypeScript
8.2. Why and What is Angular
There are many front-end JavaScript frameworks to choose from today, each with its own set of trade-offs
Many people were happy with the functionality that Angular 1.x afforded them. Angular 2 improved on that functionality and made it faster, more scalable and more modern. Organizations that found value in Angular 1.x will find more value in Angular 2.
-
more modern
-
more capable and easier for new programmers to learn than Angular 1.x
-
easier for project veterans to work with
8.3. Why: Features and Benefits
Some features and benefits from http://angular.io
-
Use modern web platform capabilities to deliver app-like experiences. High performance, offline, and zero-step installation
-
Quickly create UI views with simple and powerful template syntax
-
Command line tools: start building fast, add components and tests, then instantly deploy
The first release of Angular provided programmers with the tools to develop and architect large scale JavaScript applications, but its age has revealed a number of flaws and sharp edges
Angular 2 was built on five years of community feedback
9. TypeScript
To add code to (y)our project we will use the called language TypeScript
9.1. Why TypeScript
-
TypeScript is called TypeScript because it is a statically typed language
-
this prevents us from making (too much) runtime errors
-
this helps us during development
9.2. What is TypeScript
TypeScript is the native language of Angular which is even build / made by Microsoft
Hence we are using a Google / Microsoft product when we are using Angular
Angular 2 was written in TypeScript, a superset of JavaScript that implements many new ES2016+ features
By focusing on making the framework easier for computers to process, Angular 2 allows for a much richer development ecosystem
Programmers using sophisticated text editors (or IDEs) will notice dramatic improvements with auto-completion and type suggestions. These improvements help to reduce the cognitive burden of learning Angular 2. Fortunately for traditional ES5 JavaScript programmers this does not mean that development must be done in TypeScript or ES2015: programmers can still write vanilla JavaScript that runs without transpilation.
Installation of TypeScript is done through the installation of Angular using the Angular CLI so we do not have to install TypeScript manually! |
9.3. Optional: How to use TypeScript
This module explains the theory of installing typescript
Installation of TypeScript happens by issueing the following command in your terminal
$ (sudo) npm install -g typescript
Use the same command later on during your development to upgrade the TypeScript compiler (e.g. per feb 2019 the Angular 7 install fails if you do not upgrade TypeScript) |
After this npm-command we should be able to invoke the tsc command from the terminal
After installing TypeScript, we can add TypeScript code immediately
function greeter(person) {
return "Hello, " + person;
}
let user:string = "Jane User"; // this is TypeScript
// document.body.innerHTML = greeter(user);
console.log(greeter(user));
After adding code we can compile the greeter.ts file using the tsc compiler
$ tsc greeter.ts
The result of this should be greeter.js
We could run this greeter.js file using our node command or just watch the resulting greeter.js file.
9.4. Optional: Assignment: Install and use TypeScript
$ npm install -g typescript
$ tsc -v
Should print the version of typescript. Now (18-09-2018) the version should be around 3.0.3
function greeter(person) {
return "Hello, " + person;
}
let user:string = "Jane User"; // this is TypeScript
// document.body.innerHTML = greeter(user);
console.log(greeter(user));
$ tsc greeter.ts
A file greeter.js should be generated
$ node greeter.js
$ node greeter
You can ignore the extension .js since node expect the file to be a .js file |
-
You should have seen the message "Hello Jane User" on your terminal and NOT something else and NOT Hello undefined!!!
9.5. Relevant parts of an Angular app
-
Install Angular
-
Install Node(JS) and NPM
-
Install Angular CLI (Command Line Interface)
-
-
Domain model
-
Component
-
Templates
-
Services (REST clients)
-
Dependency injection
-
Directives
-
Data binding
-
Routing
-
Modules
-
The other cement of the Angular app like
-
package.json
-
…
-
10. Install Node and NPM
This module explains how to install Node and NPM
10.1. Assignment: Install NodeJS and NPM
10.1.1. Optional!!!: Update NodeJS to the latest release
If you installed Node and NPM a while ago you might/must update. See the link below
11. Install Angular CLI
Since Angular(2+) the framework is switched to using the Command Line Interface to generate the (upcoming) parts of the Angular Framework
So … that is why we have to install the CLI
11.1. How: Install Angular CLI
11.2. Assignment: Install Angular CLI
Installing the Angular CLI is pretty simple. After installing Node and NPM we can invoke the the npm command to install it
$ (sudo) npm install -g @angular/cli
You run the same commmand (sudo) npm install -g @angular/cli to upgrade to the next version |
You run ng update on a project to update that project to the newly version after upgrading |
12. Create Angular application
This module describes how we can create an Angular application from scratch
-
Windows
-
C:
-
cd \Users\raymond\angular\projects
-
-
Linux/Apple
-
cd /home/rloman/angular/projects
-
Now we can invoke commands to create a fresly green angular app using the command: ng new <appName>
or to enable routing which we should do since mostly we will use routing
ng new <appName> --routing
For instance to generate an app for users we might do something like this
$ ng new user-app --routing (enter)
Afterwards, Angular will now have created a directory user-app in the above mentioned location which is a full blown fresh Angular app
You might wonder what that routing option does?, well ⇒ |
-
Add an app-routing.module.ts file in your src/app folder where you can tweak the routing in (later)
-
Add AppRoutingModule to imports in src/app/app.module.ts
-
Add <router-outlet> tag to app.component.html
Since Angular CLI 7 the question "Do you want to add routing?" is asked during project creation so you might forget to enter it. |
12.1. Explain the result of a generated Angular app
After generating the application structure a lot of files are created. The most important onces will be described below but be aware that some parts are not used perse yet so the learning of this files is an organic process
12.1.1. Most used for starters
-
The NodeJS file which describes all the Node dependencies for this project. Comparable with Java’s Maven’s pom.xml
-
File for administering the changes in the package.json (should be commited in git)
-
The file which is opened by you, the customer when starting the app. The most used and important tag in the index.html is (or should be) the router-outlet (with routing enabled) or app-root (without routing) since that renders all the output of the app
-
contains the first application module and describes what is available in this appliation
-
this component is the first component of this app and mostly this is the starting component with it’s HTML when the app starts
-
the HTML which is to be rendered when the app.component will be active
-
a folder where we might be adding content later for deployment
-
a folder which contains files per environment e.g. (develop, test, acc, prod) to specify a specific URL for example per environment
-
e.g. the URL for development might be http://localhost:8080/api/users but in test it might be http://api.test.example.com:8080/api/users and in production it might be: http://api.example.com/users
-
-
a file used to add styles for the ENTIRE application, not to be confused with the .css files PER COMPONENT
12.1.2. Less used for starters
-
Configuring the TypeScript Lint source code checker
-
Configuration the TypeScript compiler
-
THE file which is used for administering the project. Especially when you create a new environment (later called Configuration). Not to be used a lot by the (new) programmer
-
main.ts is the entry point of your application , compiles the application with just-in-time and bootstraps the application
-
The file for configuring the Karma Framework. A framework for testing the Angular application
-
The filder which contains the End To End Testing apps
12.2. Assignment: Create angular application structure
$ ng new user-app --routing (enter)
13. Running the created application
13.1. Go to the application
Now we have created a directory user-app, jump into the directuring using the cd command
$ cd user-app
13.2. Run the application
Although the application is now pretty blank, it is possible to run the Angular application now using the Angular CLI command ng serve e.g.
$ ng serve
or to open your default browser directory in the app
$ ng serve --open
At the moment of writing the default HTTP port of an Angular app is 4200 hence we can open an other browser and goto http://localhost:4200
13.3. Stop the application
Although it is not stated when running you can stop the application using Ctrl-C commands.
So when you press Ctrl and keep Ctrl key down and then press C the application will stop runnning
13.4. Running the app explained
When you invoke the command ng serve angular starts to compile your source files to (native) JavaScript and places those files in the _dist/<appName>
If you look at that directory you will see several files
-
index.html: the home page of the app
-
main.js: the main JavaScript file
-
map.js files: files which are unused for now. They contain the exported Symbols of the app. Not needed (yet)
You might even copy that files to a public webroot and than you app will run there. e.g.
if you copy that files to c:\users\<username>\apps\bikeshop than you can run the app from that directory
Allthough stated above it is very strange and will not work for remote calls (using REST) since browsers will prevent invoking remote call outside a C: disk website |
14. Domain class / model
14.1. Assumptions: Domain class / model
For now the assumption is made that you (yes you, the reader) has learned some regarding Object Orientation so terms like the following below are familiar
-
class
-
property, field, instance var
-
methods
-
getter
-
setter
14.2. Why: Domain class / model
In several languages so als for Angular it is common best practise and used a lot to express the classes which we are working on during development which are part of the customer domain / problem are created.
This helps us a lot (proven enough) e.g. to speak the same languare in the team and prevents us from making errors
14.3. How: Domain class / model
This module explains how to generate a class in Angular using the Angular CLI
You can generate a domain class with the CLI using the following commands
$ ng generate class <className>
e.g. to create a User class we express it like this
$ ng generate class User
The domain class (here 'User') might be expressed using lower case or uppercase
The result of this 'ng generate class' is that Angular will generate a class in 'src/app' folder like this
1
2
3
export class User {
}
We can (later) hack on this class to give it all the properties the User has but for now we will add id:Number and name:string to it (Number and string are bot TypeScript types)
14.3.1. Assignment: Creating domain class
During this assignment we will generate our first domain class
-
To learn how to generate a domain class
-
Since we are using the User domain class later on we will create it now
-
Perform the following commands on your command line
$ ng generate class User
1
2
3
export class User {
}
14.3.2. Assignment: Add id and name to domain class User
1
2
3
export class User {
}
1
2
3
4
export class User {
id: Number;
name: string;
}
14.4. Follow-up
After adding the domain class we are ready to continue developing the next step of the application, the Component
15. Component
15.1. Introduction
During this module we will learn what an Angular component is, why we are using it, when to use it and how to use it and finally what the starting content of a Component is
15.2. What: Component
In Angular terms a Component is the part of the application which is responsible for rendering the HTML to the browser. So it is the view part of the MVC model
15.3. Why: Component
The answer to the question why are we using a component? is pretty trivial
-
High cohesion: the component is ONLY responsible for the view part
-
Loose coupling: we can change the component afterwards without affecting the rest of the applicatoin
-
A frontend developer can focus on component whilst a backend developer on the REST service etc
15.4. Parts of component
When we are generating a Component we will generate three files during generating of the component part
-
<name>-component.ts contains the view logic
-
<name>-component.html contains the view HTML
-
<name>-component.css contains the CSS
All those parts will become clearer later on
15.5. Generating component
We can generate a component using the Angular CLI using the following syntax: $ ng generate component <componentName>
An example, to create a component which will (later) render a list of all customers might be generated like this
$ ng generate component users
After invoking the above command there will be a file created with the name users.component.ts, users.component.html and users.component.css in the subdirectory src/app_users_
15.6. Assignment
-
to learn how to work with the component generator of Angular CLI
-
During this assignment we will generate our first component
-
generate component UsersComponent using the following example
$ ng generate component users
15.6.1. Validation
-
Validate that the following files are generated in src/app/users
-
users.component.ts
-
users.component.spec.ts
-
users.component.html
-
users.component.css
-
-
Validate that the following file is modified during generating
-
app.module.ts
-
15.6.2. Explaination
users.component.ts explained
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
-
3: @Component: says that this class is a real Angular component. Hence an annotation, called a decorator in Angular
-
4: selector: says that we can render this component everywhere in this app when we use the <users>-tag
-
The selector is since the Angular 6 CLI generated by prepending it with 'app-' so when you create a component and that component has a selector 'app-users' then you can use <app-users> everywhere where you want to have the userscomponent rendered
-
-
5: templateUrl: directory where the to be render HTML template is located
-
6: styleUrl: directory where the to be used css file is located
-
for now remember that this file is (only) applicable per component which is handy mostly
-
-
10: default constructor with no dependencies yet
-
12: ngOnInit implementation: the method which is invoked just after the constructor has run
-
still empty for now but later it will fetch the list of users to be shown
-
users.component.html explained
1
2
3
<p>
users works!
</p>
-
In fact a still empty component which we will modify later
users.component.spec.ts explained
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
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UsersComponent } from './users.component';
describe('UsersComponent', () => {
let component: UsersComponent;
let fixture: ComponentFixture<UsersComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ UsersComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(UsersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
-
For now, all we have and should know is that this file is able to test the behaviour of the Users Component
The users.component.spec.ts file is a unit-test for the Users component. The convention for Angular applications is to have a .spec.ts file for each .ts file. They are run using the Jasmine javascript test framework through the Karma task runner when you use the ng test command
-
for now the css file is still empty
Explaining the change in app.module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { UsersComponent } from './users/users.component';
@NgModule({
declarations: [
AppComponent,
UsersComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
-
First, we will review this file later on more detailistic
-
For now, this file is the AppModule and describes the AppModule using some decorators
-
6: Import UsersComponent
-
11: Declare the availabity of UsersComponent for this (basic) Appmodule
-
The rest is really for later …
Two way data binding in Angular |
link:https://blog.thoughtram.io/angular/2016/10/13/two-way-data-binding-in-angular-2.html [, window=_blank] |
16. Service
16.1. Why: Service
Components shouldn’t fetch or save data directly. They should focus on presenting data and delegate data access to a Service
-
to separate te concerns of developing components and service(s)
-
to abstract the layer between component and service
-
to reuse a service in several other components
16.2. What: Service
A service is the abstraction in Angular which you might see as the waiter in a restaurant. When eating or drinking, the waiter asks what you want and he will deliver it to you.
In Angular a service is responsible for fetching data from a RESTful webservice and giving that data to a Component Or better … a Component will ask the Service to get data and when done the component will render the content of his HTML template
In the MVC - pattern you might see a service as the *C*ontroller part of the pattern. But since we are now in the frontend (so everything here is on the View side of the application landscape) the term controller is very confusing, hence they call it the service, since in fact it is the service who is servicing the data from V (Component) to and from M(MOdel)
16.3. How: Service
16.3.1. Caution: Service overhauled
The http / httpClient part of Angular is completely overhauled in Angular5 |
We will discuss the new way of generating services and make note of the legacy part of Angular service (pre 5) in the last part of this document
16.3.2. Adding Service support
Before we can use the service using the Angular5 way we have to do the following
-
Add an import for HttpClientModule to app.module.ts
-
Add the class HttpClientModule to the imports array in app.module.ts
Some videos regarding this are below in the Resources module |
16.3.3. Create a Service
In Angular using the CLI we can generate a Service using the following command
$ ng generate service <serviceName>
hence, to generate a Service for our User domain model we can instruct the CLI like this
$ ng generate service user
NB: Be aware that we are generating a service for the user domain class, hence it’s name UserService. This service will be responsible for every action (Create, List, Fetch one, Update and Delete) for the User model
Assignment: Create service
-
To learn how to generate an Angular service
-
We will generate a service and (later) we will instruct the service to fetch data from the http://jsonplaceholder.typicode.com
$ ng generate service user
Validation
After instructing the command above you should find
-
src/app/user.service.ts
-
src/app/user.service.spec.ts
Explaination
We will explain the user.service.ts now
1
2
3
4
5
6
7
8
9
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor() { }
}
-
1: import Injectable ⇒ to be able to use the Injectable decorator (see below)
-
3: decorate the class with @Injectable ⇒ So that we can inject the service later as collaborator in an other class e.g. component
-
4: providedIn: 'root' (explained below)
-
6: export class UserService which is empty for now
When you add a service provider to the root application injector, it’s available throughout the app. Additionally, these providers are also available to all the classes in the app as long they have the lookup token.
You should always provide your service in the root injector unless there is a case where you want the service to be available only if the consumer imports a particular @NgModule.
16.3.4. Modify a service to fetch data
-
For this module the following knowledge is assumed
-
knowledge of REST
-
knowledge of JSON
-
As you mentioned the UserService is pretty empty now. We will give the Service more bacon and eggs to fetch data
Let’s start with adding the list method of all Users from the REST api of http://jsonplaceholder.typicode.com
Add list method and consequences
-
private url: string = "http://jsonplaceholder.typicode.com";
-
private endpoint: string = "users";
constructor(private httpClient: HttpClient) {
}
Understand that now we are using the above constructor we created a field or instance variable for the class UserService with type type HttpClient and name httpClient (pretty weird but it rocks) |
1
2
3
list(): Observable<User[]> {
return this.httpClient.get<User[]>(`${this.url}/${this.endpoint}`); // returns an Observable of type User
}
Explaination
Assignment and validation and explaination which is here pretty straighforward explain what asynchronous is and that it is widely used (not only in Angular)
16.4. Summary
We just generated a service and made the service salonfahig for fetching data. In the next module we will plumb this newly service to the UsersComponent
17. Plumbing the Service to the Component
NB: I (author) came to the conclusion that code here is better than text :-) So … read on …
Now that we have the service and the userscomponent we have to plumb those
17.1. Modifying the users.component.ts to invoke the service
To do this we have to add a dependency (aka wiring) using the constructor in users.component.ts, following the same principle as wiring the httpClient in the Service.
So to do this we add the following dependency in the users.component
1
2
3
4
...
constructor(private userService: UserService) {
}
...
1
2
3
import { Component } from '@angular/core';
import { UserService } from 'src/app/user.service';
import { User } from 'src/app/user';
1
private users: User[]; // a field containing an array of User with name users
this.userService.list().subscribe(data => {
this.users = data;
});
-
1: invoke the userService his get method which returns some data obviously. We subscribe ourself to the data and when the data is received please invoke the arrow function after that
-
So … the result of the call is asynchronously placed in the local data variable and … 2: than stored in the field users
-
17.2. Modifying the users.component.html to render the data in users.component.ts
In the users.component.html we now still have to render the users to the browser.
We will handle the simplest case first using an Unorder List using the HTML tag ul and it’s child tag li (List Item)
1
2
3
4
5
6
<div>
Exampe list
<ul>
<li *ngFor="let user of users">id: {{ user.id }} has name: {{ user.name }}</li>
</ul>
</div>
-
1: opening div
-
2. opening ul
-
3: li ⇒ opening tag with uses the directive ngFor to say to loop over the users list and use each element as user and print the user.id and user.name to the browser window
-
3: the so called mustache {{ operator is used to print the content of user.id and user.name
17.3. Modifying the app.component.html to show the users.component.html in his html
Almost done, the only thing we have to do still is to tell Angular through the app.component.html that we will render the users.component when it loads.
Since, after creation, the tag <app-users> is assigned to users.component.html we add the following tag in app.component.html so that it looks like this or contains this
<p>
<app-users></app-users>
<p>
17.4. Assignment
-
To learn how to wire/inject the Service to the component
-
During this assignment you will inject the UserService to the UsersComponent
-
During this assignment you will modify the component to show a list of users
-
Following the steps above modify the UsersComponent and wire the UserService to the UsersComponent
-
After the changes you have made there should be a list of users in your screen showing the users of http://jsonplaceholder.typicode.com/users
After this module we now know how to generate a component and plumb the the service to the component. Our UsersComponent now shows the list of users. Would n’t it be nice to be able to create a user ?
In the next module we will generate a component and modify our service to create a fresh users, so follow along …
18. Introduction to Http Verbs
Before diving into the generating of the CreateUserComponent we have to learn how the HTTP protocol is using verbs
Http methods and verbs points merely to the same thing. Me as trainer personally like verb more to distinguish between the Java / .NET method and the Http verbs which is bound to the method |
18.1. Why: Http verbs
In the previous module we used the so called GET HTTP verb to show a list of users In the module we explain WHY http has more than only the GET verb
There are more methods than GET alone to use in the http protocol and REST uses it conform a stanard REST convention way
-
To (re) usage a single url and endpoint
-
so a list and a new user can be created by respectively sending a GET and POST to http://jsonplaceholder.typicode.com
-
-
Because while starting the internet those verbs were already build on so why not use them
-
Because it is clean since we express WHAT we want to do
18.2. What: Http verbs
-
GET ⇒ to fetch a list OR to fetch a single element (e.g. one single user)
-
POST ⇒ to create a NEW element, e.g. to create a NEW user
-
PUT ⇒ to UPDATE an element, e.g. to change the name of a user from John to Jane
-
DELETE ⇒ TO DELETE an element, .e.g to delete a user from the list of users
18.3. How: Http verbs
A REST service is build and we assume that we now how in this entire course. But a small recap … Since jsonplaceholder.typicode.com uses GET to fetch a list and POST to create a user we have to be able to set the HEADER of the HTTP request to use the Method: GET or POST
Setting GET in the browser (e.g. Internet exploder, Firefox or Mozilla) is simple since it uses it by default, but setting the request method using a browser to POST is impossible hence we can skip this option
There is a application called Postman which every current developer should have installed. It is a so called REST client. Using Postman you can set the request method / verb to GET, POST, PUT or DELETE
In Angular the usage of verbs was necessary up till the Angular 5 version. After this version we can use the new HttpClientModule which supports transparent http calls since it has methods for post, get, update and delete in itself and finds the rest out for itself
Since we now know enough of the http verbs we can dive into the real generating of the UserCreateComponent and modifying the UserService to create a User
19. Add create method to UserService
During this module we will modify the UserService to be able to POST a request to the API
19.1. How: Add create method to UserService
The userservice must have a create method for sending out the REST request to the jsonplaceholder.typicode.com url
Using the HttpClientModule we will create the following method in the service to create a user
1
2
3
4
5
6
create(user: User): Observable<User> {
let resultFromService: Observable<User> = this.httpClient.post<User>(`${this.url}/${this.endpoint}`, user);
return resultFromService;
// or shorthand: return this.httpClient.post<User>(`${this.url}/${this.endpoint}`, user);
}
-
1: Create a create method which expects a User parameter (between parenthesis) and returns an Observable object specific for a User object
-
(we expect the REST backend method to response to us with the newly generated user (with id) and that object is than returned from the backend to our component)
-
-
2: Get the result from the REST service (backend) using the URL specified and sending out the newly to be created user als second parameter
-
4: return the call to the restservice to the component
-
5: shorthand for line 3 and 5 on one line (which you might or might not prefer)
19.1.1. Assignment: Add create method to UserService
Using the code above of the UserService modify your code to have a create method
-
For now validation of this assignment is pretty hard. We will see it when we generated the CreateUserComponent which is part of the next module …
20. CreateUserComponent
During this module we will generate a CreateUserComponent to create users for the application
20.1. Why: CreateUserComponent
Why would we want to make a (separate) component for creating users. The answer to this question lies in the above answered questions, but to recap: to have a single responsible component for one part of the application, to reuse it later and to have a loose coupling.
Hence, the CreateUserComponent is ONLY responsible for creating a user
20.2. What: CreateUserComponent
Our CreateUserComponent will be responsible for creating a user. It will send out a request to the UserService to create a new user
Our UserService will send out the request of the component to the API using a POST request.
20.3. How: CreateUserComponent
To generate a CreateUserComponent we invoke the following command on the terminal
$ ng generate component create-user
After invoking this command, the Angular CLI should have * generated a src/app/create-user/create-user.component.ts file and accompanying .css, .html and .spec.ts file * should have modified the *app.module.ts file which now declarates the CreateUserComponent
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
import { Component, OnInit } from '@angular/core';
import { UserService } from '../user.service';
import { User } from '../user';
@Component({
selector: 'app-user-create',
templateUrl: './user-create.component.html',
styleUrls: ['./user-create.component.css']
})
export class UserCreateComponent implements OnInit {
private user: User;
constructor(private userService: UserService) { }
ngOnInit() {
this.user = new User();
}
create(): void {
this.userService.create(this.user).subscribe(user => {
this.user = new User(); // or call ngOnInit();
});
}
}
-
12: a user field to hold the newly created user
-
14: a constructor to inject the UserService
-
18: the create method with is a call to the userService and subscribing to the response
-
20: set the user field to the received data from the Service, this user will be used in the upcoming create-user.component.html explaining
A way to go for creating a user (again … a way to go) is using some input tags like this in the CreateUserComponent.html
1
2
3
4
5
6
<div>
<label>id: <input readonly [(ngModel)]="user.id" placeholder="generated id" /></label>
<label>name:<input [(ngModel)]="user.name" placeholder="name" /></label>
<input type="button" (click)="create()" value="Create user">
</div>
-
2: a READONLY field for showing the id later
-
3: a labelled field for typing in the name of the new to be created user
-
5: the button, when pressed to invoke the create method
In the code above you see some [(ngModel)] parts which are new for you
It means that we are saying to the Angular framework that the user.name is bound to the property user.name in the corresponding create-user.component.ts file (some counts for user.id)
-
surrendering some with [] means that we are using property binding which means that if the property in the .ts file changes, the content of the corresponding input field also changes
-
surrendering some with () means that we are using event binding which means that if the event or data in the .html file changes, the content of the property in the .ts file also changes
Also called banana in the box since we see here with some imagination a banana in a box
It just means that you are using property binding and event binding so both of the mentioned above
This is also called two way binding
20.3.1. Impediment: Adding forms support
Before diving in to the assignment of adding this create-user.component we have to add some to get the code running using the ngModel
-
modify app.module.ts: import { FormsModule } from '@angular/forms';
-
modify app.module.ts: add FormsModule to the imports sesction
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
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { UsersComponent } from './users/users.component';
import { HttpClientModule } from '@angular/common/http';
import { UserCreateComponent } from './user-create/user-create.component';
import { FormsModule } from '@angular/forms'; // <==
@NgModule({
declarations: [
AppComponent,
UsersComponent,
UserCreateComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule, // <==
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
20.3.2. Impediment: Use the app-create-user tag
Since we did not really do routing yet, we now still have to add a tag to the app.module.html so render the CreateUserComponent when the app runs
Hence, modify your app.component.html to have the app-create-user tag some like this
1
2
3
4
5
6
7
8
9
10
11
12
.
.
.
<router-outlet></router-outlet>
<app-users></app-users>
<app-user-create></app-user-create>
.
.
.
20.4. Assignment: Create a CreateUserComponent and consequences
Following the code above now please add a CreateUserComponent and modify your service to follow it’s need
-
After creating and modifying code, now please run the app
-
Check that you can insert a new user and that an id is printed to the id field in the html
20.5. Assignment: Add some more field to the user model
The user also should have an username and email property.
Implement the username and email property (just like you did with the name) in the CreateUserComponent and Service
21. Enable routing
21.1. Why: Routing
Routing is the way in Angular to map an URL in the form of a string to a component
We route to have a way to navigate between the various components
A routing component is a component which handles the URL(string) to component mapping of the app
The most important part of this routing is to know where to find it: app-routing.module.ts The other important part of this routing is to know where to configure it: routes field / array which contains a map of URL to Component mapping(s), with key: path and value: component
The only remaining work to do is when you create your app with routing enabled ($ ng new <appName> --routing) is the step below 8 on the article above |
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 { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UsersComponent } from './users/users.component';
import { UserCreateComponent } from './user-create/user-create.component';
// a json expressing our routes
const routes: Routes = [
{
path: 'users',
component: UsersComponent
},
{
path: 'users/create',
component: UserCreateComponent
},
{
path: '',
component: UsersComponent // default route
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
22. UserShowComponent
This module describes how to fetch a user from the DB (using the service which calls the REST api) and displays that user on the view using the UserShowComponent
22.1. Why: UserShowComponent
To have a single responsible component for fetching a detail overview of the user we are interested in
22.2. What: UserShowComponent
The UserShowComponent will be a component which shows all the details (which might be more properties than in the UsersComponent) of the user. It will fetch the data based on the id property of the user
22.3. How: UserShowComponent
22.3.1. Impediment: Service should return user based on id
At first, we have to instruct our service to return a user based on the id
1
2
3
4
5
6
7
...
findById(id: Number): Observable<User> {
return this.httpClient.get<User>(`${this.url}/${this.endpoint}/${id}`);
}
...
-
3: declare a function findById in the service which expects an id and returns the Observable object with the found user by id
-
4: return (asynchronous) and calls the REST api with the extension e.g. users/15
22.3.2. Generate the UserShowComponent
$ ng generate component user-show
-
The file(s) for the UserShowComponent should be generated in src/app/user-show (user-show.component.ts, .html, .css and .spec.ts)
-
The file app.module.ts should be modified to contain UserShowComponent in the declarations
22.3.3. Modify the routing
Explaining routing with id
1
2
3
4
5
6
...
{
path: 'users/:id',
component: UserShowComponent
}
...
-
3: We want to be able to route to e.g. users/5 which should send an id parameter with the request with a value of 5 to the component
-
e.g. http://localhost:4200/users/5 is now a valid url pattern
-
-
4: please route to UserShowComponent when the application routes to something with /users/:id in it
Using the routing in users.component.html
Since we are adding the routing above you might be interested when we are using that url
1
2
3
4
5
6
7
8
<div>
Exampe list
<ul>
<li *ngFor="let user of users">
<a [routerLink]="['/users', user.id]">id: {{ user.id }} has name: {{ user.name }} </a>
</li>
</ul>
</div>
-
5: Watch closely; we wrapped the rendering of the id 3 has name: John Doe in a so called HTML a*nchor tag, which links to users, and adds *id to the link
-
so if the id is 3 of the user than a link like this will be generated: <a href="http://localhost:4200/users/3>id 3 has John Doe</a>
-
22.3.4. Modifying / creating the user-show.component.ts
Now we have to add the view-logic in the UserShowComponent (the .ts file)
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
import { Component, OnInit } from '@angular/core';
import { UserService } from '../user.service';
import { User } from '../user';
import { ActivatedRoute, ParamMap } from '@angular/router';
@Component({
selector: 'app-user-show',
templateUrl: './user-show.component.html',
styleUrls: ['./user-show.component.css']
})
export class UserShowComponent implements OnInit {
private user: User;
constructor(
private userService: UserService,
private route: ActivatedRoute
) {}
ngOnInit(): void {
const id = +this.route.snapshot.params["id"];
this.userService.findById(id).subscribe( user => {
this.user = user;
})
}
}
-
4: import (also) the ParamMap class which is needed to find out the current id in the code later
-
13: the field user which will be the current user we are interested in
-
15: constructor with the UserService and the (new for you) ActivatedRoute class which holds the map of the above ParamMap
-
21: read out the id parameter out of the route map. This code is on it’s on new but it just happens to be this way :-)
-
please watch the smart + sign just before the this.route… expressions. It maps the string id to a number id
-
-
23: fetch the user by id from the service
22.3.5. Modifying / creating the user-show.component.html
1
2
3
4
5
6
7
8
9
<h1>Overview of details for user {{ user.name }}</h1>
<div>
id: {{ user.id }} <br/>
name: {{ user.name }} <br />
username: {{ user.username }} <br />
email: {{ user.email }} <br />
</div>
-
X: In fact this snippet just renders using the mustache ({{ … }}) operator the user details (id, name, username and email)
22.4. Assignment: Generate UserShowComponent
Using the instruction above now please generate your own UserShowComponent and modify the service
22.5. Assignment: Implement customer change
The customer does not like the fact that the entire line of the user with id is able for clicking. He prefers a Show link on the right side of the row
Please implement this!
23. UserEditComponent
23.1. Why: UserEditComponent
To have a single component to edit the user
23.2. What: UserEditComponent
The UserEditComponent will be responsible for fetching the user by id, updating the content of the user en sending the update request from the component to the service
23.3. How: UserEditComponent
23.3.1. Modify the UserService
1
2
3
4
5
...
update(user: User): Observable<User> {
return this.httpClient.put<User>(`${this.url}/${this.endpoint}/${user.id}`, user);
}
...
-
2: declare method update which expects a User and returns an Observable with a User in it
-
3: just invoke the httpClient which updates the user using a PUT request
23.3.2. Modify the routing
1
2
3
4
5
6
...
{
path: 'users/edit/:id',
component: UserEditComponent
}
...
-
X: It might be clear now but we just add a route users/edit/:id to that e.g. http://localhost:4200/users/edit/3 renders the UserEditComponent and sends out the id param with value 3
23.3.3. Modify the users.component.html for edit route
1
2
3
...
id: {{ user.id }} has name: {{ user.name }} <a [routerLink]="['/users', user.id]">Show</a> <a [routerLink]="['/users/edit', user.id]">Edit</a>
...
-
2: At the end you see the link to the users/edit/:id which renders the url to the UserEditComponent
23.4. Add the user-edit.component.ts
-
It has an extra update() method to update the user
1
2
3
4
5
6
7
8
...
update(): void {
this.userService.update(this.user).subscribe(updatedUser => {
console.log("Updated in Component: "+updatedUser.id)
});
}
}
...
-
2: declare update method with expects nothing (Why?) and returns nothing
-
3: invoke the userService to update the user which is in the current bound scope (this.user)
-
4: You might subscribe to it but that is more to print out the changed value or something
-
Should the update not return to the list of users?
-
How we are going to do that is part of the upcoming module(s)
-
23.4.1. Add the user-edit.component.html
1
2
3
4
5
6
7
8
9
10
11
<h1>Overview of details for user {{ user?.name }}</h1>
<div *ngIf="user">
id: <input readonly [(ngModel)] = "user.id"><br/>
name: <input [(ngModel)]= "user.name"> <br />
username: <input [(ngModel)] = "user.username"><br />
email: <input [(ngModel)]="user.email"> <br />
<input type="button" value="Save" (click)="update();">
</div>
-
4: Render an input field using the user.id property which is readonly
-
5: Render an input field using the user.name property
-
9: Add a button which invokes the update method in the component when clicked on it
-
X: In fact the input fields are using the ngModel to bind the property to the user field in user-edit.component.ts
24. Remove a User
24.1. Assignment: Remove a user with no warnings (yet)
Now that we did a lot regarding the component and such the customer called in that he is unable to remove a user
-
Implement a Delete link right beside the Show link we added which removes a user from the system
-
See how you implemented the UserShowComponent
-
Is there a user-delete.component.html or is there just a method in the users.component ???
25. Routing and Navigation
Indeed, we already did some routing but this part is the official courseware regarding routing
25.1. Why: Routing and Navigation
Using Routing and Navigation it is possible to redirect from one component to an other component. This is needed since for instance we would have to redirect to the list of users after adding a new user
25.2. What: Routing and Navigation
In Angular routing and navigation is build upon the next parts
-
Router
-
Route
-
routerLink directive
-
Location service
-
routerLinkActive directive
25.3. How: Routing and Navigation
There might be some overlap in the following module which we already did during the days/evenings since it was somehow what reactive
25.3.1. Setup routing (automatically)
In fact when we created the application you might already have created the app with the following command
$ ng new <appName> --routing
If you did that the following module is optional
25.3.2. Setup routing (manually)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>UserApp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
-
6: Validate that this line is at the correct location in your index.html
25.3.3. Router imports
The Angular Router is an optional service that presents a particular component view for a given URL, hence it is not part of the Angular core.
It is in its own library package, @angular/router. Import what you need from it as you would from any other Angular package
If you used the --routing option while creating the app, the file AppRoutingModule is created and has the imports instead of the code below |
1
2
3
...
import { RouterModule, Routes } from '@angular/router';
...
25.3.4. Configuration
As we already saw, the configuration if the app is done in the file app-routing.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
...
const routes: Routes = [
{
path: 'users',
component: UsersComponent
},
{
path: 'users/create',
component: UserCreateComponent
},
{
path: '',
component: UsersComponent // default route
},
{
path: 'users/:id',
component: UserShowComponent
},
{
path: 'users/edit/:id',
component: UserEditComponent
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
-
X: This file contains the routes const of type Routes which contains an array of Route objects having a path key and component value
-
path: contains a string which is an URL
-
component: contains a Component which the path URL should redirect to
-
Matching strategy
By default the router will look at what is left in the url, and check if it starts with the specified path (e.g., /team/11/user starts with team/:id).
This is particularly important when redirecting empty-path routes, e.g. ⇒
1
2
3
4
5
6
7
8
[{
path: '',
pathMatch: 'prefix', //default
redirectTo: 'main'
}, {
path: 'main',
component: Main
}]
-
What is wrong here above?
-
every url will always start with a single null character hence the first path mathes everything!!!
1
2
3
4
5
6
7
8
[{
path: '',
pathMatch: 'full',
redirectTo: 'main'
}, {
path: 'main',
component: Main
}]
-
Besides path and component there are some more attributes
-
pathMatch: 'full' means to match the entire string, else it finds if the beginning of the string matches the url
-
redirectTo: to redirect to (an)other component
-
e.g. path: '' ⇒ redirectTo: '/users'; means that this URL redirects to /users (hence shows the list)
-
-
1
2
3
4
5
6
7
8
9
10
11
[{
path: 'team/:id',
component: Team,
children: [{
path: 'legacy/user/:name',
redirectTo: 'user/:name' <=
}, {
path: 'user/:name',
component: User
}]
}]
25.4. Further reading
Route API |
|
Routes API |
25.5. Assignment: Add routing
-
learn how to add a route
-
During this assignment you will learn to add a route from 'users/create' to create a user with the UserCreateComponent
-
You will remove all the tags out of app.component.html BESIDES THE VERY IMPORTANT <router-outlet> tag WHICH REMAINS THERE!!!
-
After creating a user you will redirect to the users list
The steps for this exercise are below in the next (sub) module ⇒
25.5.1. Modify users.component.html
1
2
3
4
5
6
7
8
9
10
<div>
List of Users
<ul>
<li *ngFor="let user of users">
id: {{ user.id }} has name: {{ user.name }} <a [routerLink]="['/users', user.id]">Show</a> <a [routerLink]="['/users/edit', user.id]">Edit</a>
</li>
</ul>
<button [routerLink]="['/users', 'create']">Create a new user ... </button>
</div>
-
9: Add a button which links to 'users/create' using Array hence
-
The array method is used to easier express links like: /users/show/3 to ['users', 'show', id] …
-
25.5.2. Validate user-create.component.html
-
<input type="button" (click)="create()" value="Create user">
25.5.3. Modify user-create.component.ts
-
an injection of the router: Router in the constructor
-
a navigation to users on the last line of the create() method
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
...
import { Router } from '@angular/router';
@Component({
selector: 'app-user-create',
templateUrl: './user-create.component.html',
styleUrls: ['./user-create.component.css']
})
export class UserCreateComponent implements OnInit {
private user: User;
constructor(private userService: UserService, private router: Router) { } // <=
ngOnInit() {
this.user = new User();
}
create(): void {
this.userService.create(this.user).subscribe(user => {
console.log("Created user");
this.router.navigate(["users"]); // <=
});
}
}
-
13: inject the field router of type Router
-
22: after creating the user, please route to users hence go to the UsersComponent (using the app-routing.component.ts his routes field)
1
2
3
4
5
6
7
8
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<router-outlet></router-outlet>
-
The last line is enough to have the entire routing in, no extra tags should remain here
-
Start the application
-
There should be a user list
-
Click *Create new user …"
-
Angular should show a Create user screen
-
Fill in the input fields
-
Click Save
-
-
Angular routers to the users component
-
The user should now be present in the users view
If you had a user: User field in your users.component.ts you might remove that now since the newly user is now applicable in the UserCreateComponent only
25.6. Assignment: Route from detail to edit
25.6.1. Introduction
-
To learn to route from detail to edit
-
During this assignment you will refactor the views so that the
-
userlist screen only has a Show button to show the details of a user
-
When you Show the details of the user there will appear an Edit button which will redirect to the UserEditComponent, which
-
on it’s turns makes it possible to edit the details of the user
-
-
After clicking Save on that button the Saved details are show in the UserShowComponent again
-
-
in the user-show.component.html add button
-
edit
-
delete
-
-
in user-show.component.ts add
-
field router: Router
-
delete method
-
-
in user-service.ts add
-
delete (user:User) method
-
25.6.2. Validation
-
I have a users view
-
And I have a Show button on that screen
-
I click on the Show button
-
I see the details of the user
-
There is an Edit button on the User show view
-
I click on the Edit button
-
There is an edit view
-
There is an edit view
-
I update some details of the user
-
And I click on the save button
-
The data is saved
-
The user show view is rendered again with the just modified data
-
I am on the users show view
-
And there is a Back button
-
I click on the back button
-
The users view is rendered again
-
refactor the anchor (a) tags to buttons (button) tags
25.7. (Sub) Topic: routerLinkActive
-
is for adding a CSS class to an (anchor <a>) tag when you clicked on the link
-
<a routerLink="…" routerLinkActive="red" means that if you click the link the CSS class red is applied to the anchor tag
25.8. (Sub) Topic: Location
This topic is related to routing
Using the Location service is considered not the best practice since you are using the browser’s location (affecting the address bar) which might lead to impredictable results |
Still I explain it here, since it is here! And relates to Routing
25.8.1. What: Location
Location is a service available in Angular 2+ apps that makes it easy to interact with the current URL path.
25.8.2. Why and When: Location
For most navigation needs, the Angular router is the correct solution, but in a few cases the location service is needed to affect the url without involving the router. Plus, the location service can come-in really handy when coupled with the router to perform certain operations
Try not to use it unless you cannot access the router directly
25.8.3. How: Location
In order to have access to the location service, import it with LocationStrategy and PathLocationStrategy from @angular/common, add the members to your list of providers and inject Location in the constructor:
1
2
3
4
5
6
7
8
9
10
11
...
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
@Component({
...
})
export class AppComponent {
constructor(private location: Location) { }
// ...
}
1
2
3
4
5
6
7
8
// Let’s say that we want methods to go forward or back in the navigation:
goBack() {
this.location.back();
}
goForward() {
this.location.forward();
}
-
2: a method in a component which simulates to press the "Back" button on your browser
-
6: a method in a component which simulates to press the "Forward" button on your browser
1
2
3
4
// You can get the current path with the Location.path method:
getPath() {
console.log(this.location.path());
}
25.8.4. Assignment: No
I will leave exercises with the Location Service for yourself or not … since it is not the best to do during your projects
1
2
<button [routerLink]="['users']">home</button>
<a [routerLink]="['users']">home</a
Using the box/square brackets are mandatory in the button tag whilst optional in the a tag. I just use the box-brackets everywhere I use routerLink but just to remind it! |
-
We covered Route, Router, Location and routerLinkActive during this chapter
In the next module we will learn more regarding Asynchronous Programming and Observable and than we will dive into Angular again …
26. Asynchronous Programming with JS
Please follow this link Asynchronous Programming
27. Observable
27.1. Introduction
In the previous module - async programming - we learned that there are several ways to code asynchronously using JavaScript. That module was primairly meant to have your mind upto par with working with asynchronous code using callbacks, promises, async await and generators.
When version 2 of Angular came out, it introduced us to observables. Angular uses observables extensively in the event system and the HTTP service. Getting your head around observables can be quite a thing
In Angular there is another way to program asynchronously using Observables. Learning the principles of Observables and (correct) programming with Observables is the subject of this module.
The Observable isn’t an Angular specific feature, but a new standard for managing async data that will be included in the ES7 release. |
27.2. What you will learn
-
What Observables are
-
Why to use them
-
When and How to use them
27.3. Why: Observable
The answer to why using Observables lies in simplicicatoin of asynchronous programming starting from EcmaScript 7 using Observables.
Observables are a way of implementing the Publish / Subscribe design pattern which uses an Observable and one ore more Observers
27.4. When: Observable
You can use an Observable everywhere in Angular when you expect an asynchronous stream of data
27.5. What: Observable
Observables are lazy collections of multiple values over time.
Observables provide support for passing messages between publishers and subscribers in your application. Observables offer significant benefits over other techniques for event handling, asynchronous programming, and handling multiple values.
Observables are declarative—that is, you define a function for publishing values, but it is not executed until a consumer subscribes to it. The subscribed consumer then receives notifications until the function completes, or until they unsubscribe.
An observable can deliver multiple values of any type—literals, messages, or events, depending on the context. The API for receiving values is the same whether the values are delivered synchronously or asynchronously. Because setup and teardown logic are both handled by the observable, your application code only needs to worry about subscribing to consume values, and when done, unsubscribing. Whether the stream was keystrokes, an HTTP response, or an interval timer, the interface for listening to values and stopping listening is the same.
Because of these advantages, observables are used extensively within Angular, and are recommended for app development as well.
27.6. How: Observable
There are in fact two ways of using an Observable which is more based on taste than on tactics …
27.6.1. Observable using the older way, manuallu subscribing and unsubscribing
This is the most seen way (but not the best and smartest way)
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
...
import { Subscription } from 'rxjs'; // <= watch this new class
@Component({
selector: 'app-user-show',
templateUrl: './user-show.component.html',
styleUrls: ['./user-show.component.css']
})
export class UserShowComponent implements OnInit, OnDestroy {
private user: User;
private subscription: Subscription;
constructor(
private userService: UserService,
private route: ActivatedRoute,
private router: Router
) {}
// @PostConstruct :-) // fetching our task using the id since we are sending it using a GET request
ngOnInit(): void {
const id = +this.route.snapshot.params["id"];
this.subscription = this.userService.findById(id).subscribe( user => {
this.user = user;
})
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
-
2: import the Subscription class from the rxjs library
-
This is the return type of the subscribe method of the Observable class used in line 13
-
-
13: field for storing the subscription result of the subscribe method on line 25
-
25: here we get the Observable of the userService and subscribe to it, storing the result
-
31: when the ngOnDestroy lifecycle hook is call(back)ed please unsubscribe from the Observable
<h1>Overview of details for user {{ user?.name }}</h1>
<div *ngIf="user">
id: {{ user.id }} <br/>
name: {{ user.name }} <br />
username: {{ user.username }} <br />
email: {{ user.email }} <br />
<button class="edit" [routerLink]="['/users', 'edit', user.id]">Edit</button>
<button class="delete" (click)="delete();">Delete</button>
</div>
27.6.2. Observable using async filter
You can use the Observable directly in the component using the following code below which is explained later
The convention to postfix an async / observable variable is a convention in Angular5+ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
import { Observable } from 'rxjs';
@Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {
private users$: Observable<User[]>;
// dependency injects the userService here as a field / instance var
constructor(private userService: UserService) {
}
ngOnInit() {
this.users$ = this.userService.list();
}
}
-
10: Here the users$ is the async result of the call to the server on line 18
-
18: Fetch the users as users$ from the service and set this async result to the field
Now … we can use the fetched async result in the component below ⇒
1
2
3
4
5
6
7
8
9
10
<div>
Exampe list
<ul>
<li *ngFor="let user of users$ | async">
id: {{ user.id }} has name: {{ user.name }} <button [routerLink]="['/users', user.id]">Show</button>
</li>
</ul>
<button class="create" [routerLink]="['/users', 'create']">Create a new user ... </button>
</div>
-
4: watch the async keyword after the 'let users of users$' which subscribes and unsubscribes from the Observable users$
27.7. Assignment: Observable ⇒ refactor to use the Subscription class
-
to use the Subscription class of the rxjs library
-
during this exercise you will refactor the users.component (the List component!) to use the Subscription class as shown above
-
Given the code shown and described above
-
Refactor the users-component to use the subscription class
27.8. Assignment: Observable ⇒ refactor to use the async filter
-
to learn to work using the observable using the async filter
-
during this exercise you will refactor the users.component from the subscribe model to the async filter using model
-
Given the code shown and described above
-
Refactor the users.component from
-
using the .subscribe and unsubscribe method ( so remove some code )
-
to the async filter ( so add code to the HTML template )
-
27.9. Conclusion
During this module we learned to work with the details of the Observable class. Although we already worked with it, during this module we learned the details of the Observable class and worked with the Subscription and the async filter
27.10. Further reading
Understanding, creating and subscribing to observables in Angular |
28. Observable ⇒ Pipe
28.1. Introduction
Since Angular 7 we can use the pipe method after having fetched an Observable. It is handy for filtering the returned data of the Observable e.g. to filter out some elements of the Observable which returns all numbers from 1 to 10 and you only are interested in the even numbers. That is a good use case for using the .pipe method.
28.2. What you will learn
-
Why and When you want to use the pipe method of the Observable API
-
What the pipe method does
-
How to use it
28.3. Why and When: Observable ⇒ Pipe
When you have an Observable and want to pass the result through a chain of methods which filter out the data
28.4. What: Observable ⇒ Pipe
This "link" describes very good how the usage of the pipe is
28.5. How: Observable ⇒ Pipe
The How of using a pipe is best demonstrated using an example.
28.5.1. Example 1
1
2
3
getNumbers(): Observable<number> {
return of(1, 2, 3, 4, 5, 6, 7);
}
1
2
3
4
5
6
ngOnInit() {
this.userService.getNumbers().pipe(filter(n => n % 2 === 0)).subscribe(n => {
console.log(`Number ${n} is an even number`); // or do something else with this number
});
}
28.5.2. Example 2
1
2
3
list(): Observable<User[]> {
return this.httpClient.get<User[]>(`${this.url}/${this.endpoint}`);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private users$: Observable<User[]>;
private filter = "";
ngOnInit() {
this.users$ = this.userService.list().pipe(
retry(3), // if the REST api fails, please do it three times
map(users => // map the users to a users list with only the users which contain the value of the field filter in their name
users.filter(n => n.name.includes(this.filter))
),
catchError(err => { // and invoke this method when an error occurs
console.log(err);
return of([]); // return an empty list ...
})
);
}
28.6. Assignment: Observable ⇒ Pipe
To learn working with the Observable and Pipe
During this assignment you will add a Filter to the user app which filters the users based on the username
28.7. Conclusion
During this module we learned how to make use of Observable in general and especially the *pipe(…) method
28.8. Further reading
Angular Observable pipe |
https://www.concretepage.com/angular/angular-observable-pipe |
29. Lifecycle Hooks
29.1. Introduction
A component has a lifecycle managed by Angular. Angular creates it, renders it, creates and renders its children, checks it when its data-bound properties change, and destroys it before removing it from the DOM Angular offers lifecycle hooks that provide visibility into these key life moments and the ability to act when they occur.
29.2. What you will learn
-
why we would want to use lifecycle hooks
-
What the most important lifecycle hooks are
-
How to use the lifecycle hooks in your code
29.3. Why and When: Lifecycle Hooks
Directive and component instances have a lifecycle as Angular creates, updates, and destroys them.
Developers can tap into key moments in that lifecycle by implementing one or more of the lifecycle hook interfaces in the Angular core library
It is sometimes useful to invoke code when e.g. a Component start his life or finally dies
29.4. What: Lifecycle Hooks
Each interface has a single hook method whose name is the interface name prefixed with ng. For example, the OnInit interface has a hook method named ngOnInit() that Angular calls shortly after creating the component
29.5. How: Lifecycle Hooks
29.6. Component lifecycle hooks overview
1
2
3
4
5
6
7
8
9
10
11
12
13
export class PeekABoo implements OnInit { // <=
constructor(private logger: LoggerService) { }
// implement OnInit's `ngOnInit` method
ngOnInit() {
this.logIt(`OnInit`); // <=
}
logIt(msg: string) {
this.logger.log(`#${nextId++} ${msg}`);
}
}
No directive or component will implement all of the lifecycle hooks. Angular only calls a directive/component hook method if it is defined.
-
Respond when Angular (re)sets data-bound input properties. The method receives a SimpleChanges object of current and previous property values. Called before ngOnInit() and whenever one or more data-bound input properties change
-
Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component’s input properties. Called once, after the first ngOnChanges()
-
Detect and act upon changes that Angular can’t or won’t detect on its own. Called during every change detection run, immediately after ngOnChanges() and ngOnInit()
-
Respond after Angular projects external content into the component’s view / the view that a directive is in. Called once after the first ngDoCheck()
-
Respond after Angular checks the content projected into the directive/component. Called after the ngAfterContentInit() and every subsequent ngDoCheck().
-
Respond after Angular initializes the component’s views and child views / the view that a directive is in. Called once after the first ngAfterContentChecked().
-
Respond after Angular checks the component’s views and child views / the view that a directive is in. Called after the ngAfterViewInit and every subsequent ngAfterContentChecked()
-
Cleanup just before Angular destroys the directive/component. Unsubscribe Observables and detach event handlers to avoid memory leaks. Called just before Angular destroys the directive/component.
29.6.1. Lifecycle sequence
After creating a component/directive by calling its constructor, Angular calls the lifecycle hook methods in the following sequence at specific moments
29.6.2. Interfaces are optional (technically)
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective. The JavaScript language doesn’t have interfaces. Angular can’t see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
Fortunately, they aren’t necessary. You don’t have to add the lifecycle hook interfaces to directives and components to benefit from the hooks themselves.
Angular instead inspects directive and component classes and calls the hook methods if they are defined. Angular finds and calls methods like ngOnInit(), with or without the interfaces.
Nonetheless, it’s good practice to add interfaces to TypeScript directive classes in order to benefit from strong typing and editor tooling.
29.7. Assignment: Lifecycle Hooks
-
To learn working with THE MOST IMPORTANT lifecycle hooks e.g. ngOnInit and ngOnDestroy
-
During this exercise you will learn how to invoke the ngOnInit and ngOnDestroy to VALIDATE that the users.component.ts is ALWAYS having new data when reloaded and mentions that it is reloaded
-
Given I have this user.show.component.ts .Description
... export class UserShowComponent /* good practice, unnecessary implements OnInit */ { private user: User; constructor( private userService: UserService, private route: ActivatedRoute, private router: Router ) {} ngOnInit(): void { const id = +this.route.snapshot.params["id"]; this.userService.findById(id).subscribe( user => { this.user = user; }) } delete(): void { this.userService.delete(this.user).subscribe(victim => { // console.log(`Deleted user ${victim.id}`); this.router.navigate(['/users']); }); } }
-
When
-
This component is unloaded
-
-
Then
-
I want to have the user field unset
-
And I want to see in the console.log that the user field is unset
-
BONUS: And I want to be sure that it is null or else I want to throw an Error (Hint: create an assert method)
-
29.8. Solution: Lifecycle Hooks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
...
export class UserShowComponent implements OnInit, OnDestroy {
private user: User;
constructor(
private userService: UserService,
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit(): void {
const id = +this.route.snapshot.params["id"];
this.userService.findById(id).subscribe( user => {
this.user = user;
})
}
delete(): void {
this.userService.delete(this.user).subscribe(victim => {
// console.log(`Deleted user ${victim.id}`);
this.router.navigate(['/users']);
});
}
ngOnDestroy(): void {
this.user = null;
console.log(this.user);
this.assert("user should be null", this.user == null);
}
}
assert(message, assertion): void {
if(!assertion) {
throw new Error(message);
}
}
-
28: add ngOnDestroy lifecycle method which is invoked when the component is destroyed (ends his lifecycle)
-
31: calls the assert method
-
35: the assert method
29.9. Conclusion
During this module we learned how to make use of the (sometimes) handy lifecycle methods so that we can inject code during lifecycle hooks of the component
29.10. Further reading
Lifecycle Hooks |
Be aware that SOME LIFECYCLE HOOKS ( e.g. ngOnChanges ) are only usable and effective when using input and output from and to a Component. We will handle those hooks during the module regarding the input and output to a component which is the subject of the next module |
30. Component Interaction
In the previous module we learned several lifecycle hooks. In this module we learned there are also some lifecycle hooks which can be used when passing data between parent and child component Working with the communication between parent and child component using the @Input en @Output Angular decorator is the subject of this module
30.1. What you will learn
-
Using the @Input decorator
-
Using the @Output decorator
-
How to work with the @Input and @Output decorator
30.2. @Input
-
Setters
-
Explain ngOnChanges
30.2.1. Parent and child explained
At first we have to make clear what a parent component is and what a child component is …
is a component where some other component is nested within
is a component which is nested in some other component

30.2.2. What you will learn
-
Using the @Input decorator
30.2.3. Why and When: @Input
In several reason we have to pass some data (input) from a parent component to a child component. e.g. when we have a UsersComponent and click on a User. Then you might have to show the UserShowComponent which has a nested PostsComponent, which contains the Posts which are written by that user. But … the PostComponent should then know for which user he has to show the Posts. Sending something from a component to a child component is done using the @Input decorator which is the subject of this module
30.2.4. What: @Input

Image you have a component where you nest in an other component. In our example - and assignment - we want to add the feature to our application so that it is able to show the Posts a User has written.
Than we have to use the @Input decorator
30.2.5. How: @Input
The following code shows the @Input decorator working using code …
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
[{
"id": 1,
"name": "John Doe",
"username": "jdoe",
"email": "john.doe@example.com",
"posts": [{
"id": 1,
"userId": 1,
"title": "Lorem ipsum dolor sit amet.",
"body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eu magna ac dolor vehicula sollicitudin. "
},
{
"id": 2,
"userId": 1,
"title": "I think it rocks",
"body": "This rocks and it rocks"
}
,
{
"id": 3,
"userId": 1,
"title": "I love coding Angular",
"body": "Since I always find what the problem is while coding ... :-)"
}
]
},
...
]
-
6: Add posts to the REST response to have posts per user
$ ng generate class post (enter)
-
Add the following fields to the domain class Post
-
id:Number
-
userId: Number
-
title: string
-
body: string
-
1
2
3
4
5
6
export class Post {
id: Number;
userId: Number;
title: string;
body: string;
}
-
X: This class represents a Post which is written by a User
-
3: the userId field is used to represent the user which has written the Post to have the foreign key relationship in order (not perse for this demo)
$ ng generate component posts (enter)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Component, OnInit, Input } from '@angular/core';
import { Post } from '../post';
@Component({
selector: 'app-posts',
templateUrl: './posts.component.html',
styleUrls: ['./posts.component.css']
})
export class PostsComponent {
@Input()
posts: Post[]; //<===
}
-
11: @Input: says that someone will inject the Posts from outside
-
12: The Posts
<div *ngIf="posts">
List of Posts
<ul>
<li *ngFor="let post of posts">
<p>
id: {{ post.id }}
</p>
<p>title: {{ post.title }} </p>
<p>body {{ post.body }}</p>
</li>
</ul>
</div>
-
X: in fact just a template to show all posts which are in global scope (just like the UsersComponent)
1
2
3
4
5
6
7
8
9
10
11
import { Post } from "./post";
export class User {
id: number;
name:string;
username: string;
email: string;
posts: Post[]; // <=
}
-
10: the (added) line for having the posts of the user
1
<app-posts [posts]="user.posts"></app-posts>
-
1: this (single) line says that on this location there should be a rendered PostsComponent which should have a posts input (!!!) which are the posts of the user (user.posts)
30.2.6. Assignment: @Input
To learn to work with the @Input decorator
During this assignment you will (as the code above) add Posts to the User to show when a user is shown
-
Given the code above explained
-
Add the POSTS to the JSON rest response
-
Add the modification to your own files
-
Finally, when the app is run you should be able to click on Show User to show the user AND the posts of that user should be shown
30.2.7. Conclusion
During this module we learned how to make use of the @Input decorator so that we can send data from a component to a nested child component.
But what if the nested child component want to tell something to this parent???
In the following module we will learn that we can make use of the @Output decorator to send something from a child component to a parent component
30.3. @Output
30.3.1. Introduction
In the previous module we learned that we can use @Input to send data from a parent component to a child component
In some cases it is also handy to send data from a child component to a parent component
We can do that using the @Output decorator
Using the @Output decorator is the subject of this module
30.3.2. What you will learn
-
Why you would want to use the @Output decorator
-
When you use it
-
How to use it
30.3.3. Why and When: @Output
You can use the @Output decorator when you want to send data from a child component to a parent component,
For example, when you have a PostsComponent which shows the Posts of a user and want to be able to click a Like button in the PostsComponent which increases the number of likes for that particular User in the UserShowComponent!!!
30.3.4. What: @Output

Image you have a component where you nest in an other component. In our example - and assignment - we want to be able to click a Like button which on it’s turn sends data to the parent (usershow) component
Than we have to use the @Output decorator
30.3.5. How: @Output
The following code shows the @Output decorator working using code …
1
2
3
4
5
6
7
8
9
10
11
12
<div *ngIf="posts">
List of Posts
<ul>
<li *ngFor="let post of posts">
<p>
id: {{ post.id }} <a href="javascript:void(0);" (click)="vote(1)">Like</a>
</p>
<p>title: {{ post.title }} </p>
<p>body {{ post.body }}</p>
</li>
</ul>
</div>
-
6: Here we add a simple anchor tag which onclick invokes the vote method in the PostsComponent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { Post } from '../post';
@Component({
selector: 'app-posts',
templateUrl: './posts.component.html',
styleUrls: ['./posts.component.css']
})
export class PostsComponent {
@Input()
posts: Post[];
@Output()
voted = new EventEmitter<number>();
vote(like: number) {
this.voted.emit(like);
}
}
-
1: Add import Output and EventEmitter for later use
-
14: Add an @Output decorator; which says: hey I am outputting/exporting the votes I receive
-
15: field voted is the event emitter to send out the newly vote
-
17: method vote receives a like parameter and emits it (later this will become clear(er))
-
Add likes as variable to count the likes
-
Add PostComponent::(voted) as Output parameter and if updated please invoke UserShowComponent::onVoted(…)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<h1>Overview of details for user {{ user?.name }}</h1>
<div *ngIf="user">
id: {{ user.id }} <br/>
name: {{ user.name }} <br />
username: {{ user.username }} <br />
email: {{ user.email }} <br />
likes: {{ likes }}
<app-posts [posts]="user.posts" (voted)="onVoted($event);"></app-posts>
<button class="edit" [routerLink]="['/users', 'edit', user.id]">Edit</button>
<button class="delete" (click)="delete();">Delete</button>
</div>
-
8: Add likes using the likes field of the user-show.component.ts
-
10: Hey Angular, if the voted of the PostComponent is changed THEN I want to be informed of that … than please invoke My (the UserShowComponent) onVoted(…) method please ( in fact the Publish / Subscribe model )
-
Add likes field
-
Add onVoted(…) method
1
2
3
4
5
6
7
8
9
10
11
12
13
...
private likes = 0;
...
onVoted(like: number) {
console.log("voted: "+like);
like > 0 ? this.likes++ : this.dislikes++;
}
...
-
3: Add field likes
-
7: Add method onVoted which is invoked when PostComponent::voted changes!!!
30.3.6. Assignment: @Output
To learn to work with @Output
-
During this exercise you will implement a like button and consequences to the user his PostsComponent which increments the likes of the user
-
Given the code above as an example please implement it in your own environment
-
When
-
I click the Like button (in the posts.component.html)
-
-
Then
-
the number of Like(s) in the user-show.component.html is increased
-
30.3.7. Assignment: Bonus: @Output
To learn to work with @Output more than once
-
During this exercise you will implement a dislike button and consequences to the user his PostsComponent which increments the dislikes of the user (hence add a dislikes field to the User)
-
Given the code above as an example please implement it in your own environment
-
When
-
I click the Dislike button (in the posts.component.html)
-
-
Then
-
the number of Dislike(s) in the UserShowComponent is increased
-
30.3.8. Conclusion
During this module we learned how to make use of the @Output decorator so that we can send out data from a child component (Posts) to a parent component (User) In the following module we will learn that we can (also) make use of some more LifeCycle Event Hooks which are part of the following (sub) topic
30.4. Further reading
Component Interaction |
https://angular.io/guide/component-interaction#component-interaction |
31. Testing
31.1. Pre introduction
-
During this session we will ONLY focus on what testing is and what good testing is …
-
And we will learn how to make two tests. Fix a failing test and build a newly one
31.2. Introduction
-
we did not know that skeletons for test were created
-
we did not make any tests
That time is now over … it is time to get acquinted with testing with Angular.
Testing with Angular will be the subject of this module
31.3. What you will learn
-
some introduction regarding good testing
-
what test are
-
how to test
31.4. Why: Testing
Testing shows the presence of errors, not the absence of errors
-
to show the presence of errors
-
to know that we created pretty good software (how else would we know?)
-
to begin with the end in mind - using Test Driven Development (TDD) we create tests before production-code
31.5. When: Testing
Act or be acted upon
The above quote means so much as: If you don’t test properly, the rest of the world will do it for you!
-
during development
-
testing done by developer
-
-
after development and before acceptance; realisation-testing
-
testing done by tester
-
-
in production
-
testing done by customer
-
What the above means? That you do a good testing or the testing is done by your customer! So … when should you test? Before the small fish become big sharks! As soon as possible!
31.6. What: Testing
-
Unittests: to test some unit (method) which is considerably small
-
Integrationtests: to test some integration between to classes; the test might be small but the unit of tests is most of the time pretty big and the focus lies on the integration between them
-
end to end tests: just a test from Database / Datastore to View (html)
31.7. How: Testing
Regarding to Angular we can use the following tools for testing
Jasmine is a javascript testing framework that supports a software development practice called Behaviour Driven Development, or BDD for short. It’s a specific flavour of Test Driven Development (TDD). Jasmine, and BDD in general, attempts to describe tests in a human readable format so that non-technical people can understand what is being tested. However even if you are technical reading tests in BDD format makes it a lot easier to understand what’s going on.
1
2
3
function helloWorld() {
return 'Hello world!';
}
1
2
3
4
5
6
describe('Hello world', () => {
it('says hello', () => {
expect(helloWorld())
.toEqual('Hello world!');
});
});
Manually running Jasmine tests by refreshing a browser tab repeatedly in different browsers every-time we edit some code can become tiresome.
Karma is a tool which lets us spawn browsers and run jasmine tests inside of them all from the command line. The results of the tests are also displayed on the command line.
Karma can also watch your development files for changes and re-run the tests automatically.
Karma lets us run jasmine tests as part of a development tool chain which requires tests to be runnable and results inspectable via the command line.
It’s not necessary to know the internals of how Karma works. When using the Angular CLI it handles the configuration for us and for the rest of this module we are going to run the tests using only Jasmine.
-
End to End testing tool for Angular
31.8. Structure and files
We will sequentially run through this page of the Angular guide since it seems it up very consisely. So please click here |
In the testworld there is a very strong principle we have to explain here ( a little ). Given ⇒ When ⇒ Then
It is even used in Scrumteams to create stories (which are testable!) and it is some like this
Given: I have a calculator
When: I add 2 + 3
Then: The result is 5
Sometimes also called the AAA principle: Arrange, Act, Assert
Angular created testfile (stubs) when creating services, component and classes
The files are located near the generated files itself and are postfixed with *.spec.ts)
-
describe: a describe of some tests which are co-located and should be seen as a whole (testsuite)
-
it: a test itself which is a test (in the body you find the when)
-
expect: an expectation after running the test (then)
We will dive (pretty) deep in this above in the upcoming module
Note to self: Let’s also browse through the slides of the Capgemini pdfs regarding Testing (day 7)
31.9. Conclusion
During this module we learned some background info on Testing so that we now know that we have unittests, integration test and e2e-tests and tools like Jasmine, Karma and Protactor
In the following module we will learn that HOW to make a unittests with Angular
31.10. Further reading and watching
Testing |
|
Jasmine and Karma |
https://codecraft.tv/courses/angular/unit-testing/jasmine-and-karma/ |
Protractor |
|
Youtube movie of Mosh Hamedani regarding Automated Testing |
32. Unittesting
32.1. Introduction
In the previous module we learned that we have to test our code and we know some background regarding Testing
But we still have to learn to make tests in Angular, starting with unittests
Learning how to make unittest with Angular will be the subject of this module
The fixing of that problem will be the subject of this module
32.2. What you will learn
-
why we have to have a lot of unittests (and even that might be enough for your project, really!)
-
what a good unittests is
-
and of course … how to write them
-
We will run $ ng test and see that it might fail (or not) and we will fix the failings tests on your current user-app project
-
we will add a unittests for the vote component
We might add more regarding testing (integration testing) but that is not part of this evening. For now and this evening … unittesting is key! |
32.3. Why: Unittesting

Given the image above which is called the Testing Pyramid we have to have a lot of unittests and than we are pretty confident about our work
32.4. When: Unittesting
Always create unittests … even create unitests before writing code is the principle of Test Driven Development (TDD)
32.5. What: Unittesting
-
method
-
some methods together
-
testing the combination of a setter and a getter
-
32.6. How: Unittesting
32.6.1. Hickups
-
Add some imports in the beforeEach to have testing of Forms (FormsModule), HttpClient (HttpClientModule) and router (RouterTestingModule) so add this to the import of beforeEach
imports: [FormsModule, HttpClientModule, RouterTestingModule]
1
2
3
4
5
6
7
describe('UserService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [UserService],
imports: [HttpClientModule] // <= add this!!!
});
});
-
3: TestBed: the bed to test … which means so much as of a ground to test on. Where you can configure your tests, add imports and dependencies
On my machine the Chrome driver still keeps failing after running. Although the tests succeed the Chrome browser is uncloseable my ng test. That might be also for you or not :-) |
32.6.2. Our first test!!!
Let’s look at a part of the code of the user-show.component
1
2
3
4
5
6
7
8
9
10
11
export class UserShowComponent implements OnInit, OnDestroy {
...
likes = 0;
private dislikes = 0;
onVoted(like: number) {
console.log("voted: "+like);
like > 0 ? this.likes++ : this.dislikes++;
}
At first, we can create a very simple test like this (including imports and such for now)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UserShowComponent } from './user-show.component';
import { FormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientModule } from '@angular/common/http';
import { PostsComponent } from '../posts/posts.component';
describe('UserShowComponent', () => {
let component: UserShowComponent;
let fixture: ComponentFixture<UserShowComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ UserShowComponent, PostsComponent ],
imports: [FormsModule, RouterTestingModule, HttpClientModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(UserShowComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should increment votes when clicked on', () => {
component.onVoted(1);
expect(component.likes).toBe(1);
});
});
-
9: the describe, the start of a testsuite
-
13: a setup: before each test (it) this will be run
-
21: a setup: asynchronously: before each test (it) this will be run
-
27: a test to test of the component is correctly instantiated (more like a given you might say)
-
31: the test of the vote and voting button
To have this test successfully run I had to add this to the declarations of the beforeEach::TestBed::configuration |
1
declarations: [ UserShowComponent, PostsComponent ], // <= had to add PostsComponents since it is used in the user-show.component
32.7. Assignment I: Fix the failing Angular tests
To get and to learn how to get the ng test command succeeding
Using the instruction above and help of the trainer try to get the following command running in your env: ng test (enter)
Really, some adding of the above modules in the imports and | or providers will get it running |
32.8. Assignment II: Unittesting a Component
To learn to create your first unittest
Using the example above, for starters, create a unittest for your voting in the app
33. Guards
33.1. Introduction
In the previous module we learned that we can create a user by clicking Create user in the Angular UI. But … the customer complained … since not everybody should be able to create a new user
So we have now come to a very (im) populair topic Security and especially Guards
Creating and working with Guards will be the subject of this module
Credits: A lot of this module is used from this page regarding Guards on Angular.io so a lot of kudos go to them!!!
33.2. What you will learn
-
Why you would want to use guards
-
When to use them (mostly security related in practise)
-
How to create and implement them!!!
33.3. Why: Guards
Using Guards in necessary when you have an app and should be able to implement security on it. You might wonder … we should add security sooner? Or you might wonder … we should add security at last? There are several opinons (polymics) and discussions around it! Fact remains that we should be security-aware with EVERY app we make and when to implement it is more customer based.
What we want is an security agnostic application
(Personal note: I like to create my app with all the first needed features in it and them add the security although I am aware that it must be added)
33.4. When: Guards
You use guards when you want to add security and have not much code impact on the existing app! (and it is the best way to add security)
33.5. What: Guards
Guards are guards :-) They are classes which conduct the access of the application / components / routing and are not a part of the application, hence the name Guards
-
CanActivate
-
to mediate navigation to a route
-
we will see this one in depth
-
-
CanDeactivate
-
to mediate navigation away from the current route
-
we will see this one in depth
-
-
CanActivateChild
-
to mediate navigation to a child route
-
-
CanLoad
-
to mediate navigation to a feature module loaded asynchronously
-
-
Resolve
-
to perform route data retrieval before route activation
-
You can have multiple guards at every level of a routing hierarchy. The router checks the CanDeactivate and CanActivateChild guards first, from the deepest child route to the top. Then it checks the CanActivate guards from the top down to the deepest child route. If the feature module is loaded asynchronously, the CanLoad guard is checked before the module is loaded. If any guard returns false, pending guards that have not completed will be canceled, and the entire navigation is canceled.
In the following parts of this module we will investigate them (especially CanActivate and CanDeactivate since it is used most and the principles are (almost) the same)
33.6. How: Guards
Applications often restrict access to a feature area based on who the user is.
You could permit access only to authenticated users or to users with a specific role. You might block or limit access until the user’s account is activated.
The CanActivate guard is the tool to manage these navigation business rules.
33.6.1. Generate a guard
syntax: $ ng generate guard <guardName>
e.g.
$ ng generate guard can-enter-this-page
$ ng generate guard can-activate
33.6.2. Implement a Guard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class CanActivateGuard implements CanActivate {
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
// simulate successful login (true) or simulate failing login (false)
return true;
}
}
The only (well only???) thing we have to do is implement some code, e.g. call a backend authentication service, use a file to read the username and password from and return true or false in the end
-
true: the user is permitted
-
false: the user is NOT permitted to enter that route ⇒
1
2
3
4
5
{
path: 'users/create',
component: UserCreateComponent,
canActivate: [CanActivateGuard] // <=
},
-
4: Just add the classname of the guard to the property of canActivate in the routing path and you are on!
33.7. Assignment: Use this guard above
Having the code above using the CanActivateGuard returning only true or false please
implement the guard for the users/create to be open (returning true in the guard) and if that rocks … return false and than the url of users/create should not be able to open anymore
-
when returning false in the guard the users/create should be unable to open!
33.7.1. Use a Guard
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
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { tap, delay } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor() { }
isLoggedIn = false;
// store the URL so we can redirect after logging in
redirectUrl: string;
login(): Observable<boolean> {
return of(true).pipe(
delay(1000),
tap(val => this.isLoggedIn = true)
);
}
logout(): void {
this.isLoggedIn = false;
}
}
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
export class CanActivateGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {
}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
let url: string = state.url;
return this.checkLogin(url);
}
checkLogin(url: string): boolean {
if (this.authService.isLoggedIn) { return true; }
// Store the attempted URL for redirecting
this.authService.redirectUrl = url;
// Navigate to the login page with extras
this.router.navigate(['/login']);
return false;
}
}
-
This guard returns a synchronous boolean result. If the user is logged in, it returns true and the navigation continues.
-
The ActivatedRouteSnapshot contains the future route that will be activated and the RouterStateSnapshot contains the future RouterState of the application, should you pass through the guard check
-
If the user is not logged in, you store the attempted URL the user came from using the RouterStateSnapshot.url and tell the router to navigate to a login page—a page you haven’t created yet
-
This secondary navigation automatically cancels the current navigation; checkLogin() returns false just to be clear about that
-
You need a LoginComponent for the user to log in to the app. After logging in, you’ll redirect to the stored URL if available, or use the default URL. There is nothing new about this component or the way you wire it into the router configuration.
$ ng generate component auth/login (enter)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../admin/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
message: string;
constructor(private authService: AuthService, private router: Router) {
this.setMessage();
}
setMessage() {
this.message = 'Logged ' + (this.authService.isLoggedIn ? 'in' : 'out');
}
login() {
this.message = 'Trying to log in ...';
this.authService.login().subscribe(() => {
this.setMessage();
if (this.authService.isLoggedIn) {
// Get the redirect URL from our auth service
// If no redirect has been set, use the default
let redirect = this.authService.redirectUrl ? this.authService.redirectUrl : '/crisis-center/admin';
// Redirect the user
this.router.navigate([redirect]);
}
});
}
logout() {
this.authService.logout();
this.setMessage();
}
}
<h2>Please login</h2>
<p>{{message}}</p>
<p>
<button (click)="login()" *ngIf="!authService.isLoggedIn">Login</button>
<button (click)="logout()" *ngIf="authService.isLoggedIn">Logout</button>
</p>
1
2
3
4
5
{
path: 'login',
component: LoginComponent
},
The above mentioned login route should be before the path:"*" routing since else the login route will not be picked up!!! |
Congratulations, we / you have now added a pretty good useful use of the CanActivateGuard principle! So time for assignments! |
33.8. Assignment I: Implement the CanActivate guard
To learn to work with guards, especially (first) the CanActivateGuard
During this assignment you will implement the CanActivateGuard to the user application including a pretty real simulated authentication
-
the code shown above
-
Please implement the CanActivate Guard to your application
33.9. Assignment II: Add real(er) security to the just created CanActivateGuard
To learn to work with guards and add realer security
During this assignment you will add more security to the CanActivateGuard so that we are having a more real feeling and a realer user realm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { tap, delay } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor() { }
// a very, very simple realm
private realm: { [key: string]: string; } = {
rloman: "testing2018!",
george: "testingwithgeorge",
erika: "erikarocks!" // and more and more names :-)
}
isLoggedIn = false;
// store the URL so we can redirect after logging in
redirectUrl: string;
login(username, password): Observable<boolean> {
let result;
// first validate for null input
if (!username || !password) {
result = false;
}
else {
// validate the user
if (this.realm[username] === password) {
result = true;
}
else {
result = false;
}
}
return of(result).pipe(
delay(1000),
tap(val => this.isLoggedIn = result)
);
}
logout(): void {
this.isLoggedIn = false;
}
}
1
2
3
4
5
6
7
8
9
10
<h2>Please login</h2>
<p>{{message}}</p>
<p>
<input [(ngModel)]='username' />
<input type="password" [(ngModel)]='password' />
<button (click)="login()" *ngIf="!authService.isLoggedIn">Login</button>
<button (click)="logout()" *ngIf="authService.isLoggedIn">Logout</button>
</p>
-
Finish the login.component.ts file
-
Add your personal username and password to the auth.service.ts
-
You might have other clues regarding the security which are nicer???
33.10. Solution for Assignment II: Add real(er) security to the just created CanActivateGuard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../admin/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
message: string;
private username: string;
private password: string;
constructor(private authService: AuthService, private router: Router) {
this.setMessage();
}
setMessage() {
this.message = 'Logged ' + (this.authService.isLoggedIn ? 'in' : 'out');
}
login() {
this.message = 'Trying to log in ...';
this.authService.login(this.username, this.password).subscribe(() => {
this.setMessage();
if (this.authService.isLoggedIn) {
// Get the redirect URL from our auth service
// If no redirect has been set, use the default
let redirect = this.authService.redirectUrl ? this.authService.redirectUrl : '/crisis-center/admin';
// Redirect the user
this.router.navigate([redirect]);
}
});
}
logout() {
this.authService.logout();
this.setMessage();
}
}
33.11. Further reading
Angular Authentication: Using Route Guards |
https://medium.com/@ryanchenkie_40935/angular-authentication-using-route-guards-bf7a4ca13ae3 |
Milestone 5 Route Guards |
|
Routing and Navigation |
34. Can Deactivate Guard
34.1. Introduction
In the previous module we learned that we have a CanDeactivate guard such that we can implement some kind of (pretty simple) security.
Guard can be used for several reasons. Another good reason for using a guard is for moving away from a webpage where you changes something in inputfields. It is quite pityful if a customer moves away from a site forgetting to save
The fixing of that problem will be the subject of this module
34.2. What you will learn
-
The CanDeactivateGuard
-
Why and how to use the CanDeactivateGuard
-
Practising with it
34.3. Why and When: Can Deactivate Guard
You use a CanDeactivate guard for several reaons but a much used use case in the moving away from a website|form use case
34.4. What: Can Deactivate Guard
1
CanDeactivate<T>
1
canDeactivate(component: T) {
In this example type T here is a so called generic type
34.5. How: Can Deactivate Guard
In this module we will add the following use case
-
I am in the user-edit component
-
I have changed some data in the edit window
-
I try to move away from the user-edit component
-
There should be some kind of alert box to remind me that I did change anything
-
I am in the user-edit component
-
I DID NOT change some data in the edit window
-
I try to move away from the user-edit component
-
There should no alert
-
I am in the user-edit component
-
I did or did not change anything in that component
-
I click the save button
-
There should no alert
-
The data should be saved
34.5.1. How: To generate a can-deactivate guard
$ ng generate guard can-deactivate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class CanDeactivateGuard implements CanActivate {
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return true;
}
}
Yes, you read it well! That command just generated a can-activate guard and that is correct! There is no other way to generate a guard, ⇒ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';
export interface CanLeaveComponent {
onLeave() : Observable<boolean> | Promise<boolean> | boolean
}
@Injectable({
providedIn: 'root'
})
export class CanDeactivateGuard implements CanDeactivate<CanLeaveComponent> {
canDeactivate(component: CanLeaveComponent) {
return component.onLeave ? component.onLeave(): true;
}
}
-
5: Add an interface NEW AND NOT RELATED TO THE CAN-DEACTIVATE GUARD for using in the next lines and other component which need to be shielded from moving away from
-
6: add an interface method in this case: onLeave() which should return a boolean of Observable or Promise containing a boolean
-
12: add a class which is the CanDeactivateGuard itself
-
14: add the method canDeactivate from the CanDeactivate interface which on it’s turn uses the onLeave method from the CanLeaveComponent method
Simply said: this CanDeativateGuard runs the onLeave method on a component when he is about to me moved away from (we will see this in action in the upcoming paragraphs)
$ ng generate service dialog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DialogService {
constructor() { }
confirm(message?: string): Observable<boolean> {
const confirmation = window.confirm(message || 'Is it OK?');
return of(confirmation);
};
}
-
I would think now the code is pretty self-explaining after this couple of weeks angular???
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
.
.
.
import { CanLeaveComponent } from '../can-deactivate.guard';
import { Observable } from 'rxjs';
import { DialogService } from '../dialog.service';
@Component({
selector: 'app-user-edit',
templateUrl: './user-edit.component.html',
styleUrls: ['./user-edit.component.css']
})
export class UserEditComponent implements OnInit, CanLeaveComponent {
private user: User;
private editUser: User; //<= Add this to know that we did or did not change the user!!!
private saving: boolean = false; // <= to set that we are saving or not!
constructor(
private userService: UserService,
private route: ActivatedRoute,
private router: Router,
private dialogService: DialogService // <= inject/wire the DialogService!
) { }
// @PostConstruct :-) // fetching our task using the id since we are sending it using a GET request
ngOnInit(): void {
const id = +this.route.snapshot.params["id"];
this.userService.findById(id).subscribe(user => {
this.user = user;
// save the user for later to test if it is changed here! (might be a better way :-)
this.editUser = new User();
this.editUser.id = user.id;
this.editUser.name = user.name;
this.editUser.username = user.username;
this.editUser.email = user.email;
})
}
// imlementation of the interface CanLeaveComponent (see above in the class construct)
onLeave(): boolean | Observable<boolean> | Promise<boolean> {
if(this.saving) {
return true;
}
else {
// be aware of order this.user fails since this.user seems not to be a User (typeof is object)
if(this.editUser.equals(this.user)) {
console.log("no changes!");
return true;
}
else {
console.log("Changes detected");
console.log(this.user);
console.log(this.editUser);
return this.dialogService.confirm("Abandon changes?");
}
}
}
update(): void {
this.userService.update(this.user).subscribe(updatedUser => {
console.log("Updated in Component: " + updatedUser.id)
this.saving = true;
this.router.navigate(["users", this.user.id]);
});
}
}
Now we have a user-edit component which is able to handle the onLeave event but … does the router know it?? No, so we finally should inform the router that onLeaving this user-edit component the onLeave method should be invoked, we inform the router like this
1
2
3
4
5
path: 'users/edit/:id',
// you can make a button now like this <button [routerLink]="['users', 'edit', user.id]">Edit</button>
component: UserEditComponent,
canDeactivate: [CanDeactivateGuard] // <=!!!
},
1
2
3
4
5
6
7
8
equals(user: User): boolean {
if(this.name === user.name && this.email === user.email && this.username === user.username) {
return true;
}
else {
return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Post } from "./post";
export class User {
id: number;
name:string;
username: string;
email: string;
posts: Post[];
equals(user: User): boolean {
if(this.name === user.name && this.email === user.email && this.username === user.username) {
return true;
}
else {
return false;
}
}
}
After implementing all this code above - which looks more work than it is - we have reached our above mentioned requirements (Given When Then)
34.6. Assignment: Can Deactivate Guard
To learn working with the CanDeactivateGuard
During this assignment you will add and use the CanDeactivate guard principle
-
the code above
-
Implement the CanDeActivate guard for your user-edit component
-
Please verify that the above mentioned Given / When / Then is reached!!!
34.7. Conclusion
During this module we learned what guards are, why and when to use them and even how to make them and make them working with the Angular app and show the worth them have!
34.8. Follow-up: Can Deactivate Guard
Below you find for this topic some extra resources to watch after the week the topic Can Deactivate Guard is introduced during the training
35. Angular Forms
35.1. Introduction
In the previous modules we implicitly learned and worked with the (older) template driven forms. So we will not dive deep(er) into that since we already know enough of it.
But … In this module we will learn how to create fancy forms using the Angular Reactive Forms module which is new since Angular2+
35.2. What you will learn
-
How to setup the Angular Reactive Forms module
-
How to create Angular Reactive Forms
-
Learn working with it using the API
35.3. Why: Angular Forms
We will use Forms when we want to submit some data. So Forms are a very much used HTML principle. So the Why question here is obsolete?
35.4. What: Angular Forms
-
Template driven (older)
-
Model Driven | Reactive Forms (newer and subject of this module)
We will browse through the link above BUT we will use for this evening and assignment ONLY the part of the document which describes the way using a formGroup in a form, so to send a complete object using the onSubmit. You might have the first part of the document for knowledge later but we will do the latter part of it since that is (my personal opinion) more usable for enterprise apps. |
So be aware; although we will browse through the whole link above, we will use formGroups!!! |
35.5. When: Angular Forms
-
You use Angular (Template Driven) Forms when you create a simple app with is not to be exhaustingly tested and uses not a lot of fancy stuff
-
You use Angular (Reactive) Forms when you create an app which is exhaustingly tested and adheres to the more recent techniques
35.6. Optional: Intermezzo: Modify REST and MySQL
Choose to implement the persistence using Array or DB or … just leave as is and use console.log to view that you see that your object is correctly when in the onSubmit method So implement your backend quickly or use array quickly or leave open and just use console.log Being able to make a form which is able to be send to the user.service is enough. This is to prevent we will have problems with the backend and do not learn how Angular Reactive Forms work since that is the topic of this evening. |
35.6.1. Optional: Intermezzo: Modify the REST service
-
amend the REST service to have POST:http://localhost:8081/api/users/3 with RequestBody a post object (userId, title, body)
-
amend the REST service to have GET:http://localhost:8081/api/users/3/posts to fetch all posts for the user (in this code for user with id: 3)
-
Amend your array persistence to add posts to the user
35.6.2. Optional: Intermezzo: Modify the MySQL tables if working with MySQL
-
user
-
id: int
-
name: varchar(255)
-
username: varchar(255)
-
email: varchar(255)
-
-
post
-
id: int
-
userId: int
-
title: varchar(255)
-
body: varchar(255)
-
35.7. Introducing the assignment
-
Story
-
During this module and corresponding assignment we will implement the following use case
AS a user I WANT to be able to add a post with a title and a body to the system SO THAT someone can read my posts and like them!
-
-
Given
-
I am on the user-show.component
-
-
When
-
I enter a new post for that user there with a title and a body
-
-
Then
-
The post is saved to the DB or Array and will be visible in the user’s posts later
-
35.8. How: Angular Forms
-
To use reactive forms, import ReactiveFormsModule from the @angular/forms package and add it to your NgModule’s imports array.
1
2
3
4
5
6
7
imports: [
BrowserModule, // this Module also re-exports CommonModule which is imported ngIf, ngFor and the now used async pipe in UsersComponent.html
AppRoutingModule,
FormsModule,
HttpClientModule,
ReactiveFormsModule // <=
],
$ ng generate component CreatePost (enter)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { Component, OnInit, Input } from '@angular/core';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { User } from '../user';
import { UserService } from '../user.service';
@Component({
selector: 'app-create-post',
templateUrl: './create-post.component.html',
styleUrls: ['./create-post.component.css']
})
export class CreatePostComponent implements OnInit {
constructor(
private fb: FormBuilder, // <=
private userService: UserService) {
}
@Input()
user: User;
postForm: FormGroup = this.fb.group(
{
title: ['', Validators.required], //<= title is default blank and is required!
body: ['', Validators.required]
}
);
ngOnInit() {
}
onSubmit(): void {
this.userService.addPost(this.user, this.postForm.value).subscribe(newPost => {
this.user.posts.push(newPost); // <= to reload the posts of this user
});
}
}
-
13: In the constructor inject the FormBuilder to build our beloved new form
-
17-18: Add user as input, since we will be creating a post for this particular user
-
20: Add the postForm which is a FormGroup which contains the …
-
21-23: items we will be filling in for the new post (title and body), which are first blank and both required (Validators.required)
-
30: This method will be invoked when submitting the form, which on it’s turn calls the userService::addPost
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<h3>Create a Post for this user ({{ this.user.id}})</h3>
<form [formGroup]="postForm" (ngSubmit)="onSubmit();">
<label for="title">
<input type="text" formControlName="title">
</label>
<label for="body">
<input type="text" formControlName="body">
</label>
<button type="submit" [disabled]="!postForm.valid">Save</button>
</form>
-
1: h3 to have a bigger font for title
-
3: Here we will create the HTML-form corresponding to the postForm in create-post.component.ts and when we click submit we invoke onSubmit
-
6: the title input text field which is bound to title in the .ts file (formControlName)
-
8: same for the body
Now we have created and finished and explained the newly create-post.component.ts we can use this component in the user-show.component.ts since we will add the posts there
.
.
.
<app-create-post [user]="this.user"></app-create-post> <!-- // Add this line, and look closely at the input parameter [user] -->
.
.
.
1
2
3
4
5
6
7
8
9
10
11
12
ngOnInit(): void {
const id = +this.route.snapshot.params["id"];
this.subscription = this.userService.findById(id).subscribe(user => {
this.userService.findPostsForUser(user).subscribe(posts => { // <=
this.user = user;
this.user.posts = posts;
}
);
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.
.
.
findPostsForUser(user: User): Observable<Post[]> {
return this.httpClient.get<Post[]>(`${this.url}/${this.endpoint}/${user.id}/posts`);
}
addPost(user: User, post: Post): Observable<Post> {
post.userId = user.id;
return this.httpClient.post<Post>(`${this.url}/${this.endpoint}/${user.id}`, post);
}
.
.
.
-
Run the app
-
Enter some posts for a user
-
Validate that they are listed when saved
35.8.1. Sidenotes: Managing control values
The tips below might be handy for usage during the assignment and later
Reactive forms give you access to the form control state and value at a point in time. You can manipulate the current state and value through the component class or the component template. The following examples display the value of the form control instance and change it.
Displaying a form control value
-
Through the valueChanges observable where you can listen for changes in the form’s value in the template using AsyncPipe or in the component class using the subscribe() method.
-
With the value property. which gives you a snapshot of the current value
-
e.g. this.postForm.value.title to print the title of the newly post
-
<p>
Value: {{ this.postForm.value.title }}
</p>
Replacing a form control value
Reactive forms have methods to change a control’s value programmatically, which gives you the flexibility to update the value without user interaction. A form control instance provides a setValue() method that updates the value of the form control and validates the structure of the value provided against the control’s structure. For example, when retrieving form data from a backend API or service, use the setValue() method to update the control to its new value, replacing the old value entirely.
<p>
<button (click)="reset()">Reset</button>
</p>
1
2
3
reset() {
this.postForm.setValue({title: 'Empty title', body: 'Empty body'});
}
35.9. Assignment: Angular Forms
To learn to start working with the Angular Reactive Forms module
During this assignment you will implement a new form to create a Post for a User using the Angular Reactive Forms Manual and the flow we just described above
-
Following the description above, implement the story to add a post to a user
35.10. Conclusion
During this module we learned how to make use of the Angular Reactive Forms library which concludes the topic of this Angular training.
35.11. Further reading
Angular (1) older Template Driven Forms |
|
Angular.io (2+) Model Driven / Reactive Forms |
|
Overview Key differences of the Angular Form Types |
|
Comparing Angular 2 Forms |
https://blog.angular-university.io/introduction-to-angular-2-forms-template-driven-vs-model-driven |
Ahead of time compilation |
36. Configurations
36.1. Introduction
Uptil now we were always working on our own machine (development) and now it is time to deliver our app to the customer. But not before testing it thoroughly using an other collegae which is a real tester and not before the customer is willing to accept the app. Only after the customer accepting the app we will bring the app to production.
-
develop(ment)
-
test
-
acceptance
-
prod(duction)
Working with these environment and especially the location of the files how to work with these environments if the subject of this module
36.2. What you will learn
-
Where the files for setting per environment settings are
-
How to configure them
-
How to use them
In previous Angular version (pre 6) they used the word environment and environments for separate environments as develop, test, acceptance and production. Since Angular 6 that term is renamed to Configuration. Please be aware of that! |
36.3. How: Configurations
36.3.1. How: To create a build
$ ng build --configuration=<configuration>
// this generates a build for the (development) environment since we did not supply an argument after the command
$ ng build
They changed the --env to --configuration in Angular6 |
$ ng serve --configuration=<configuration>
36.3.2. How: Configurations
Configurations in Angular is done using the following files
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
export const environment = {
production: false,
name: "development",
tasksUrl: 'http://localhost:8080/api/tasks',
authUrl: 'http://localhost:8080/auth'
};
export const environment = {
production: true,
name: "",
tasksUrl: 'http://api.planner.prod.carpago.nl/api/tasks',
authUrl: 'http://api.planner.prod.carpago.nl/auth'
};
import { Injectable } from '@angular/core';
import { Task } from './task';
import { Dashboard } from './dashboard';
import { Http, Headers } from '@angular/http';
// import { AuthenticationService } from './authentication.service';
import { environment } from '../environments/environment';
import { AuthenticationService } from './authentication.service';
import 'rxjs/add/operator/toPromise';
@Injectable()
export class TaskService {
// the taskUrl injected / set from the current environment
private tasksUrl:string = environment.tasksUrl; // URL to web api
constructor(
private http: Http,
private authenticationService: AuthenticationService
) { }
getTasks(): Observable<Task[]> {
return this.http.get(this.tasksUrl); //<= using the taskUrl
}
.
.
.
Using private fields in the component will now fails since they are not accessible when transpiling to JavaScript. |
-
Always try to deploy as fast as possible in the start phase of a project
-
Try to make a build to another environment (like prod) to have a clue about wheter or not our app is deployable
36.4. Assignment: Configurations
To learn to deploy your app using an other URL to fetch data from
During this assignment you will create an other configuration, set an other url: String in your UserService for fetching data and then make a build for a different environment and deploy that build (for brevity and handy-ness on your own machine)
-
I have the user app
-
Which uses the REST api we created
-
I create an acceptance configuration
-
The url used to fetch users is from http://jsonplaceholder.typicode.com/users
-
Implement this
36.5. Conclusion
In this module we learned how to create an environment (Since Angular6 being called a configuration). In the following module we will learn how to install that build what is called Deployment
Deployment will be the topic of the next module
37. Deployment
37.1. Introduction
In the previous module we learned that we can have multiple environments for deployment
During this -smaller- module we will learn how to deploy the app
37.2. What you will learn
-
How to make a build for deployment
-
How to deploy the build
-
How to test the deploy
37.3. Why and When: Deployment
Obviously, the customer would want to use the application not only on your machine !
37.4. What: Deployment
Deployment in it’s simplest form is copying the created JavaScript files to the target destination
37.5. How: Deployment
$ ng build --configuration=acct (enter)
-
look in the dist directory
-
copy the content of dist to a public_www / webroot folder of a server and you are done
37.6. Assignment: Deployment
To learn to create, find and deploy a build
During this assignment you will create a build, find the result of the build and copy the build to a location of your choice. (which might be even just a directory on your filesystem (e.g. C:\Users\johndoe\acceptance\user-app) or something
-
Go to the user-app root dir
-
issue: ng build --configuration=<some config>
-
cd dist
-
copy . to a webroot of a server like Apache, NGinx or Microft IIS
-
copying it to a local directory seems not to function
-
-
Using your internet browser open the index.html file in the directory above
During my build I had issues regarding fields which were private and I had to change them to default access (so removed the private which is explainable but unfortunate) |
37.7. Conclusion
During this module we learned how to make a build and to deploy the build
In the following module we will recreate an entire app from scratch using the hobby from one of the teammembers: BoardGames
38. Boardgame Case
39. Notes on Service pre Angular5
1
Was the previous TaskService here see comment in adoc above
-
Dropped Http in favor of HttpClient