You are on page 1of 112

Angular Lecture Notes

Thomas W. Stütz – Version 1.0.0, 2020-07-26


| NVS 4th grade HTL Leonding Vocational College

Table of Contents
1. Angular CLI
1.1. Scripts
1.1.1. start
1.1.2. build
1.1.3. test
1.1.4. linting
1.1.5. end-to-end testing
1.2. EditorCon몭g
1.3. angular.json
1.4. tscon몭g.json
1.5. Bootstrapping Process
1.6. styles.css
2. Con몭guring WebStorm
2.1. Format Space within braces for ES6 import/export
2.1.1. Option 1: Con몭g in preferences
2.1.2. Option 2: Con몭g in tslint.json
2.2. Turn O몭 IntelliJ Auto Adding to VCS/GIT
2.3. Turn O몭 Warning about Missing Return Type of Function
3. Compiler
3.1. For Debugging
3.2. For Production
3.2.1. Run the app in the dist-folder locally
Python Web Server
Chrome Extension - Web Server for Chrome
4. Angular Ecosystem
4.1. StackBlitz
4.2. Angular Material
4.3. Angular Flex Layout
4.4. Kendo UI for Angular
4.5. Prime NG
4.6. Semantic UI
5. Templates and Data Binding
5. Templates and Data Binding
5.1. Interpolation
5.2. Event-Binding
5.3. Template Expressions
5.4. Property Binding
5.5. Two-Way-Binding
5.6. Structural Directive - *ngFor
5.7. Event with Parameter
5.8. Structural Directive - *ngIf
5.9. Exercise: Turmrechnen
6. Components and Modules
6.1. Components
6.2. Module
6.2.1. Creating a component
6.2.2. Programming a component
6.2.3. Input Parameter
6.2.4. Output Event
6.3. Exercise: Einheitenumrechner
6.3.1. Simple Design of the Application
6.3.2. Set up the Project
6.3.3. Start Coding
6.3.4. Add Communication to the Parent Component
value
unit-of-measure
6.3.5. Exercise
Create a single 몭le component
Extend the unit-converter-example
7. Accessing Web APIs
7.1. Run Demo Server
7.2. Use REST Client in WebStorm
7.3. Consuming Web APIs
7.3.1. Get a List of Persons
7.3.2. Get a List of ToDos
7.3.3. Add a Todo
7.3.4. Add Delete Buttons
7.3.5. Cooking Recipe
7.3.6. Exercise
8. Routing
8.1. What is Routing?
8.2. Implement Routing
8.3. router-outlet
8.3.1. loading the full page
8.3.2. routerLink - partially loading a page
8.3.3. Access the PathParam (the id in the route)
9. RxJS Basics
9.1. Recap
9.2. Introduction
9.3. Method 'of()'
9.4. Method 'interval(…)'
9.5. Unsubscribe with a timer
9.6. Operator 'take(…)'
9.7. Operator 'map(…)'
9.8. Operator '몭lter(…)'
9.9. Function 'concat(…)'
9.10. Function 'merge(…)'
9.11. RxJS for the Angular UI
9.12. RxJS for Web APIs (REST)
10. Flexbox
10.1. 몭ex-direction: row and column
10.2. 몭ex-wrap
10.3. 몭ex-items
10.4. Media Query
10.5. Angular Flex Layout
10.5.1. Installation
10.5.2. Usage
10.5.3. Live Demo
10.5.4. Exercise: conference-agenda
10.5.5. Static API
10.5.6. Grid System
10.5.7. TypeScript API
10.5.8. ngClass-, ngStyle-, …-API
11. Testing with Jasmine
11.1. Testing
11.2. Testing in Angular
11.3. How to write tests
11.3.1. Add a Simple Service
11.3.2. Writing a Function to test
11.3.3. AAA-Pattern
11.3.4. Implement the Test
11.4. To Focus on one Test
11.4.1. How to set a breakpoint in the browser
11.4.2. How to set a breakpoint in Webstorm
11.5. Testing "Edge Cases"
11.6. Mock Objects
11.6.1. Excursus: Inline Reference Information in WebStorm
11.6.2. Create a Spy
11.6.3. Resources for Jasmine
11.7. Disable a Test (Test is Pending)
11.8. Test Modules in Angular
11.8.1. Fixing the example
11.9. Homework
12. OpenID Connect

  

Credits to Rainer Stropek. This lecture note are based on his Angular Course.

lecture notes

github repo

1. Angular CLI
http://cli.angular.io
Windows

npm install -g @angular/cli

Linux, MacOS

sudo npm install -g @angular/cli

Ivy is the new compiler of Angular.


Angular CLI is a command line interface.

-g → installs global

Angular CLI is a kind of meta-programming environment.

meta data → data about data meta programming → writing programs that write
programs

Angular CLI is based on schematics (aka sca몭olding)

The structure of the program 몭les is given. With schematics you can change the way
Angular CLI creates the 몭les.

mkdir angular-intro
cd angular-intro

Angular CLI is just a node project

$ where ng
/usr/local/bin/ng

Version of Angular CLI

ng --version

List of Commands

ng

Table 1. Description

Command Description

new Creates a new workspace and initialize an Angular app. It is possible to


host multiple apps in a workspace

List of Commands

ng new --help
Table 2. ng new --help

Command Description

--dry-run (-d) Very important. Kind of simulator. Run through and reports
activity without writing out results.

--skip-git (-g) Do not initialize a git repository.

--routing In the beginning we will not deal with it

--style SASS → like typescript to javascript, you code in SASS and compile
it down to CSS. SASS supports variables, nestings, etc

ng new my-great-app --dry-run

? Would you like to add Angular routing? No


? Which stylesheet format would you like to use? CSS
CREATE my-great-app/README.md (1028 bytes)
CREATE my-great-app/.editorconfig (274 bytes)
CREATE my-great-app/.gitignore (631 bytes)
CREATE my-great-app/angular.json (3614 bytes)
CREATE my-great-app/package.json (1255 bytes)
CREATE my-great-app/tsconfig.base.json (458 bytes)
CREATE my-great-app/tsconfig.json (426 bytes)
CREATE my-great-app/tslint.json (3184 bytes)
CREATE my-great-app/.browserslistrc (853 bytes)
CREATE my-great-app/karma.conf.js (1024 bytes)
CREATE my-great-app/tsconfig.app.json (292 bytes)
CREATE my-great-app/tsconfig.spec.json (338 bytes)
CREATE my-great-app/src/favicon.ico (948 bytes)
CREATE my-great-app/src/index.html (296 bytes)
CREATE my-great-app/src/main.ts (372 bytes)
CREATE my-great-app/src/polyfills.ts (2835 bytes)
CREATE my-great-app/src/styles.css (80 bytes)
CREATE my-great-app/src/test.ts (753 bytes)
CREATE my-great-app/src/assets/.gitkeep (0 bytes)
CREATE my-great-app/src/environments/environment.prod.ts (51 bytes)
CREATE my-great-app/src/environments/environment.ts (662 bytes)
CREATE my-great-app/src/app/app.module.ts (314 bytes)
CREATE my-great-app/src/app/app.component.css (0 bytes)
CREATE my-great-app/src/app/app.component.html (25725 bytes)
CREATE my-great-app/src/app/app.component.spec.ts (960 bytes)
CREATE my-great-app/src/app/app.component.ts (216 bytes)
CREATE my-great-app/e2e/protractor.conf.js (869 bytes)
CREATE my-great-app/e2e/tsconfig.json (299 bytes)
CREATE my-great-app/e2e/src/app.e2e-spec.ts (645 bytes)
CREATE my-great-app/e2e/src/app.po.ts (301 bytes)

NOTE: The "dryRun" flag means no changes were made.


 This syntax is called kebap-syntax (aka kebap-case)

ng new my-great-app

no routing

choose CSS

One of the biggest disadvantages is the amount of 몭le Angular installs


 for a hello-world-app

417 MB and 37.298 몭les !!!

 Don’t check in `node_modules`in your git repo!!!!


cd my-great-app
webstorm .

Angular CLI generates .gitignor, package.json, tscon몭g.json etc.

package.json-File

@angular/ - dependencies come from the Angular core team

the other dependencies are (open-source) libraries that the Angular team
choosed to use. Parts of theselibraries (like rxjs) are maintained by members of
the Angular core team.

devDependencies

@types/

Jasmine, karma, protractor are used for tests.

one hour programming → minimum one hour writing automated tests

w/o automated tests, we ship crappy software

every project needs some level of manual testing, but we want to reduce
manual testing and increase automated tests

Jasmine is a framework for writing tests

Karma is a framework for running tests (run tests remotely; in the cloud; on 10
di몭erent smartphones)

Protractor is a tool for end-to-end testing. End-to-end testing means a user is


simulated (the system is simulating: moving the mouse, clicking on a button,
entering text in a text몭eld)

the versions are responsible for the compability of the libraries to each other.

Never change a single version. If you want to update an project →


 https://update.angular.io

1.1. Scripts
scripts:

start → like browser-sync

lint → linter checks the quality of your source code


build → compiles an Angular app into an output directory named dist/.

1.1.1. start
Open a terminal in Webstorm and run the app

npm start

http://localhost:4200

Stop the server with Ctrl + C

1.1.2. build

npm run build

The result appears in the dist-folder. This is called a debug-buils. It will run on any
web-server. But don’t use it in a production environment at the moment. We will hear
why later on.

1.1.3. test

npm run test


1.1.4. linting

npm run lint


1 WebStorm will tell you immediately the problem
2 When running the lint-script you get a list of errors

1.1.5. end-to-end testing

npm run e2e

based on protractor

1.2. EditorCon몭g
https://editorcon몭g.org/

WebStorm supports EditorCon몭g. There is a plugin available (and bundled in the


defailt con몭guration)

Sets the con몭guration for several IDE’s / editors (VSC, Atom, WebStorm, ….)
1.3. angular.json
Setting 몭le. Don’t touch it at the moment.

1.4. tscon몭g.json

1.5. Bootstrapping Process


index.html

main.ts

1.6. styles.css
Styles can applied globally.

changes the font to serifs

*{
font-family: "Times New Roman", Times, serif;
}

2. Con몭guring WebStorm
2.1. Format Space within braces for ES6 import/export
Instead of import brackets w/o spaces …

import {Component, OnInit} from '@angular/core';

... you can con몭gure IntelliJ WebStorm to

import { Component, OnInit } from '@angular/core';

2.1.1. Option 1: Con몭g in preferences


enable Preferences | Editor | Code Style | TypeScript, Spaces / Within / ES6 import/export
braces
Use the shortcut Option + Command + L to apply the changes to the code

2.1.2. Option 2: Con몭g in tslint.json


# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single
ij_javascript_spaces_within_imports = true 1
ij_typescript_spaces_within_interpolation_expressions = true

[*.md]
max_line_length = off
trim_trailing_whitespace = false

1 con몭g-options for IntelliJ start with 'ij_'

to reformat the code 몭les use

2.2. Turn O몭 IntelliJ Auto Adding to VCS/GIT


File → Settings → Version Control → Con몭rmation → When Files are created
2.3. Turn O몭 Warning about Missing Return Type of Function
tslint.json

...
"typedef": [
false,
"call-signature"
],
...

3. Compiler
3.1. For Debugging
Angular compiles your html code into javascript. You write Angular templates (which
looks like html)

npm run build

Normally you have a html page and javascript is used for dynamic behaviour. Angular
works di몭erent. Angular compiles all (html) down to javascript.
the compiler runs just in time → just-in-time-compiler

3.2. For Production


npm run build -- --prod

-- → appends "--prod" to "ng build" -→ ng build "--prod"


Now the size of the folder is smaller then before (debug-build).

The 몭le names look pretty strange → a hash of the content of the 몭le is added. For
performance reasons. So the compiler knows which 몭le changed.

When there is a error (ie wrong closed html element) the error is at build time (not
at runtime like before)

This compiler is a ahead-of-time compiler (AOT Compiler)

Maybe the JIT compiler (for debugging) will be removed in the future

3.2.1. Run the app in the dist-folder locally

Python Web Server


open a terminal (in Webstorm)

cd dist/my-great-app
python3 -m http.server

open an browser with http://localhost:8000

source: How do you set up a local testing server?

Chrome Extension - Web Server for Chrome

Web Server for Chrome


4. Angular Ecosystem
4.1. StackBlitz
https://stackblitz.com/ https://stackblitz.com/edit/angular-ivy-g2몭mm

StackBlitz is not as powerful than a local installation

for demonstrating small projects

4.2. Angular Material


https://material.angular.io/

4.3. Angular Flex Layout


https://github.com/angular/몭ex-layout

The real power of Flex Layout, however, is its responsive engine.

4.4. Kendo UI for Angular


Very interesting controls

https://www.telerik.com/kendo-angular-ui

4.5. Prime NG
https://www.primefaces.org/primeng/

4.6. Semantic UI
https://semantic-ui.com/

5. Templates and Data Binding


5.1. Interpolation
src/app/app.component.html

<h1>
Welcome to {{ title }}
</h1>
src/app/app.component.ts

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-great-app';

constructor() {
setInterval(() => this.title += '!', 250);
}
}

→ one-way interpolation

5.2. Event-Binding
app.component.ts

import { Component } from '@angular/core';

@Component({
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-great-app';

constructor() {
}

public onClicked(): void { 1

this.title += '!!';
}
}

1 add this function

app.component.html

<h1>
Welcome to {{ title }}
</h1>

<button type="button" (click)="onClicked()"> 1

Click me!
</button>

1 add this element. Use AutoComplete

It would als possible

<h1>
Welcome to {{ title }}
</h1>

<button type="button"
(click)="title = title + '!'"> 1

Click me!
</button>

1 There is a danger of getting spaghetti code - mixing ie view and logic → never add
business logic to a view

5.3. Template Expressions


Binding from the view (html) to the logic (ts)

app.component.ts
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-great-app';
myNumber = 41; 1

constructor() {
}

public onClicked(): void {


this.title += '!!';
}
}

app.component.html

<h1>
Welcome to {{ title }}
</h1>

<button type="button" (click)="onClicked()">


Click me!
</button>

<h2>{{ myNumber + 1 }}</h2> 1

Calculations are possible

5.4. Property Binding


A string works

...
<input type="text" value="asdf" />

Result

Bind the 'title' does not work

...
<input type="text" value="title" />
Result

We have to use "Property Binding"

...
<input type="input" [value]="title" />

Result

5.5. Two-Way-Binding
Combine data binding with event binding

We have to use "Property Binding"

...
<input type="input" [(ngModel)]="title" />

The site is broken

Click on "Import FormsModule"

app.module.ts
Check the result in app.module.ts

The FormsModule has to be imported

Eventually restart the server

The two-way-binding works as expected


5.6. Structural Directive - *ngFor
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-great-app';
myNumber = 41;
todos = [ 1

'Shopping',
'Homework',
'Listen to music'
];

constructor() {
}

public onClicked(): void {


this.title += '!!';
}
}

<h1>
Welcome to {{ title }}
</h1>

<button type="button" (click)="onClicked()">


Click me!
</button>

<h2>{{ myNumber + 1 }}</h2>

<input type="input" [(ngModel)]="title" />

<p>{{ todos }}</p> 1

not very promising


app.component.html

...
<ul>
<li *ngFor="let item of todos">
{{item}}
</li>
</ul>

The presentation of the array is much prettier now.

So we can also add a button

app.components.html

...
<button type="button" (click)="addItem()">
Add item
</button>

app.components.ts

public addItem(): void {


this.todos.push('something');
}

The button is working


5.7. Event with Parameter
public removeItem(item: string): void {
this.todos.splice(this.todos.indexOf(item),1);
}

<ul>
<li *ngFor="let item of todos">
{{item}}
<button type="button"
(click)="removeItem(item)">X
</button>
</li>
</ul>

We don’t have to use the indexOf()-method


5.8. Structural Directive - *ngIf
app.component.html

...
<p *ngIf="alert">
We have a critical alert!!!
</p>

5.9. Exercise: Turmrechnen


Turmrechnen

The solutions are included. But give it a try.

6. Components and Modules


6.1. Components
In Angular is all based on components. The AppComponent is the root component
which is auto-generated when creating an Angular app.

Components structure your user interface into pieces like structuring


 the logic in to pieces with classes.

An Angular component represents a visual part of your application. Together you can
use all components to arrange them on the screen.
The Decorator for Components

@Component({ 1

selector: 'app-root', 2
templateUrl: './app.component.html', 3

styleUrls: ['./app.component.css'] 4
})
export class AppComponent {
...
}

1 component decorator (comparable to annotations in Java)


2 Selector: The name of the selector to create your own html-tags

index.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyGreatApp</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> 1
</body>
</html>

1 html-element of the root component

3 Instead of using a template html 몭le you can also write the html code directly here

app.component.ts

@Component({
selector: 'app-root',
template: '<h2>xxx</h2>', 1
styleUrls: ['./app.component.css']
})
export class AppComponent {
...
}

1 with template you can include html code (for small templates). It is not
recommended to do so.
4 The style only applies to the current component.

app.component.css

h1 {
background: darkorange;
}

 This is a local style for this component.

6.2. Module
All components are arranged in modules.

A javascript module is a 몭le.

In larger projects your modules tend to grow. It is not a practical approach to put all in
a single 몭le.

In Angular modules represent a broader concept. Angular provides a module-system


which includes several 몭les.

Angular libraries are NgModules, such as FormsModule, HttpClientModule, and


RouterModule. Many third-party libraries are available as NgModules such as Material
Design, Ionic, and AngularFire2.

NgModules consolidate

components,

directives, and

pipes

into cohesive blocks of functionality, each focused on a feature area, application


business domain, work몭ow, or common collection of utilities.

You can group your components ie to:

invoice module

customer management module

administration module

accounting module

app.module.ts

import { BrowserModule } from '@angular/platform-browser';


import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
declarations: [ 1
AppComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
})
export class AppModule { }

1 This declaration section contains all the components which are part of this module.

6.2.1. Creating a component

ng generate component numeric-input --dry-run

app.component.html

<h1>
Welcome to {{ title }}
</h1>

<app-numeric-input></app-numeric-input> 1
app.module.ts

import { BrowserModule } from '@angular/platform-browser';


import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';


import { NumericInputComponent } from './numeric-input/numeric-input.component'; 1

@NgModule({
declarations: [
AppComponent,
NumericInputComponent 1
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

1 The NumericComponent is imported and added to the module-declaration


automatically, when creating the module by Angular CLI.

It is also possible to use npm packages to distribute your modules.

6.2.2. Programming a component


numeric-input.component.html

<input type="number"><button>Reset</button>

app.component.html

<h1>
<h1>
Welcome to {{ title }}
</h1>

<app-numeric-input></app-numeric-input>

app.component.html

<h1>
Welcome to {{ title }}
</h1>

<app-numeric-input></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>
6.2.3. Input Parameter
Communication between child- and parent-component

@Input-Decorator for passing a parameter

numeric-input.component.ts
import {Component, Input, OnInit} from '@angular/core'; 1

@Component({
selector: 'app-numeric-input',
templateUrl: './numeric-input.component.html',
styleUrls: ['./numeric-input.component.css']
})
export class NumericInputComponent implements OnInit {

@Input() public value = 0; 2

constructor() { }

ngOnInit(): void { 3

}
}

1 Input was added to the imports


2 @Input()
3 btw: The ngOnInit() is a lifecycle hook. Angular calls ngOnInit() shortly after
creating a component. It’s a good place to put initialization logic.

app.component.html

<h1>
Welcome to {{ title }}
</h1>

<app-numeric-input [value]="42"></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>

numeric-input.component.html

<input type="number" [value]="value">


<button>Reset</button>
<input type="number" [value]="value">
<button>Reset</button>

6.2.4. Output Event


@Output()

not so easy

what should be the outpur?

a signal telling the parent that something happened (ie clicked)

In Angular exists the class EventEmitter


numeric-input.component.ts

import {Component, Input, OnInit, Output, EventEmitter} from '@angular/core'; 1

@Component({
selector: 'app-numeric-input',
templateUrl: './numeric-input.component.html',
styleUrls: ['./numeric-input.component.css']
})
export class NumericInputComponent implements OnInit {

@Input() public value = 0;


@Output() public resetClicked = new EventEmitter<void>(); 2

constructor() { }

ngOnInit(): void {
}

public onResetClicked() { 3
this.resetClicked.emit();
}

1 a
2 b
3 we delegate it to the parent. The child informs the parent, that the click event
happened.

In the parent component now the resetClicked-event is available


numeric-input.component.html

<input type="number" [value]="value">


<button (click)="onResetClicked()">Reset</button>

1 Create method with <Alt>-Enter …


2 … or wait until the popup appears

app.component.ts

...
onReset() {
console.log('Reset clicked!');
}
...

app.component.ts

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-great-app';

myFirstValue = 42; 1
constructor() {
}

onReset() {
this.myFirstValue = 0; 2

}
}

app.component.html

<h1>
Welcome to {{ title }}
</h1>

<app-numeric-input
[value]="myFirstValue" 1

(resetClicked)="onReset()">
</app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>
<app-numeric-input></app-numeric-input><br/>

Now the number is set to 0 after clicking Reset

 So you can build generic usable components

Repeat less code!

Reuse more code!

6.3. Exercise: Einheitenumrechner


6.3.1. Simple Design of the Application

<table>
<td><app-unit-ps-kw
[value]="30"
[unit]="'PS'"
>

6.3.2. Set up the Project


Create new project (dry run)
ng new unit-converter --dry-run

Create new project

ng new unit-converter

Create a new component "input-ps-kw"

Alternatively you can use;

ng generate component input-ps-kw

The new component is automaticallly added to app.module.ts

import { BrowserModule } from '@angular/platform-browser';


import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';


import { InputPsKwComponent } from './input-ps-kw/input-ps-kw.component'; 1

@NgModule({
declarations: [
AppComponent,
AppComponent,
InputPsKwComponent 2

],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Start the app

Alternatively you can use npm start or ng serve

6.3.3. Start Coding


input-ps-kw.component.html
<input type="number">
<select>
<option>PS</option>
<option>kW</option>
</select>

https://de.wikipedia.org/wiki/VW_K%C3%A4fer

https://de.wikipedia.org/wiki/Ferrari_LaFerrari

app.component.html

<table>
<thead>
<tr>
<td>Car Model</td>
<td>Power</td>
</tr>
</thead>
<tbody>
<tr>
<td>VW Käfer 1936</td>
<td>
22PS
<app-input-ps-kw></app-input-ps-kw> 1

</td>
</tr>
</tbody>
</table>

1 The parent component recognises already the new component


Make sure, that you import the FormsModule

input-ps-kw.component.html

<input type="number" [(ngModel)]="value"> 1

<select>
<option>PS</option>
<option>KW</option>
</select>
input-ps-kw.component.html

<input type="number" [(ngModel)]="value">


<select [(ngModel)]="uom"> 1
<option>PS</option>
<option>kW</option>
</select>

input-ps-kw.component.ts

...
export class InputPsKwComponent implements OnInit {
value = 30;
uom = "PS"; // unit of measures
...

Now change the uom to "KW" and check if it’s working.

6.3.4. Add Communication to the Parent Component

value
1. Add the @Input-Decorator to the 몭eld (value).

2. Check, if input is imported.

3. Add the parameter in the parent component to the elements.

input-ps-kw.component.ts

import {Component, Input, OnInit} from '@angular/core'; 1

@Component({
selector: 'app-input-ps-kw',
templateUrl: './input-ps-kw.component.html',
styleUrls: ['./input-ps-kw.component.css']
})
export class InputPsKwComponent implements OnInit {
@Input() value = 30; 2
uom = "KW"; // unit of measures

constructor() { }
ngOnInit(): void {
}

app.component.html

<table>
<thead>
<tr>
<td>Car Model</td>
<td>Power</td>
</tr>
</thead>
<tbody>
<tr>
<td>VW Käfer 1936</td>
<td>
22PS
<app-input-ps-kw value="22"></app-input-ps-kw> 1
</td>
</tr>
<tr>
<td>Ferrari LaFerrari</td>
<td>
588KW
<app-input-ps-kw value="588"></app-input-ps-kw> 2
</td>
</tr>
</tbody>
</table>
unit-of-measure

...
export class InputPsKwComponent implements OnInit {
@Input() value = 30;
@Input() uom = "KW"; // unit of measures 1

constructor() { }

ngOnInit(): void {
}
...

...
<tr>
<td>VW Käfer 1936</td>
<td>
<app-input-ps-kw value="22" [uom]="'PS'"></app-input-ps-kw> 1

</td>
</tr>
<tr>
<td>Ferrari LaFerrari</td>
<td>
<app-input-ps-kw value="588" uom="KW"></app-input-ps-kw> 2
</td>
</tr>
...

1 because here the square brackets are used (one-way-data-binding), "PS" must
surrounded additionally be single quotes. Otherwise PS would be interpreted as
variable name.
2 without the square brackets, "KW" is 몭ne w/o single quotes

When using one-way-data-binding, single quotes are necessary when


 using strings (→ uom) but not when using numbers (→ value)

After removing the 몭xed values "22PS" and "588KW"


app.component.ts

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
kaeferPower = 22; 1
kaeferUom = 'PS'; 2
}

app.component.html

...
<tr>
<td>VW Käfer 1936</td>
<td>
<app-input-ps-kw
[value]="kaeferPower" 1
[uom]="kaeferUom" 2

></app-input-ps-kw>
</td>
</tr>
<tr>
<td>Ferrari LaFerrari</td>
<td>
<app-input-ps-kw value="588" uom="KW"></app-input-ps-kw>
</td>
</tr>
</tbody>
</tbody>
</table>

<h1>Our VW K&auml;fer has {{ kaeferPower }} {{ kaeferUom }}</h1> 3

But changes are not re몭ected (because of one-way-data-binding)


input-ps-kw.component.ts

...
export class InputPsKwComponent implements OnInit {
@Input() value = 30;
@Output() valueChange = new EventEmitter();

@Input() uom = "KW"; // unit of measures


@Output() uomChange = new EventEmitter();
...

The name of the output is the same as the name of the input + the su몭x
 'Change' → naming convention

input-ps-kw.component.html

<input type="number" [(ngModel)]="value" (change)="onPowerChanged()">


<select [(ngModel)]="uom" (change)="onUomChanged()">
<option>PS</option>
<option>KW</option>
</select>

input-ps-kw.component.js

...
onPowerChanged() {
this.valueChange.emit();
}

onUomChanged() {
this.uomChange.emit();
}
}

app.component.html

...
<tr>
<td>VW K&auml;fer 1936</td>
<td>
<app-input-ps-kw
[(value)]="kaeferPower"
[(uom)]="kaeferUom"
></app-input-ps-kw>
</td>
</tr>
...
...

Unfortunately this doesn’t work

we have to send also the value in the event

input-ps-kw.component.js

...
export class InputPsKwComponent implements OnInit {
@Input() value = 30;
@Output() valueChange = new EventEmitter<number>(); 1

@Input() uom = "KW"; // unit of measures


@Output() uomChange = new EventEmitter<string>(); 2

constructor() { }

ngOnInit(): void {
}

onPowerChanged() {
this.valueChange.emit(this.value); 3

onUomChanged() {
this.uomChange.emit(this.uom); 4

}
}

 This is a often used pattern.

6.3.5. Exercise

Create a single 몭le component

ng generate component --help

terminal output

--flat
When true, creates the new files at the top level of the current project.
...
--inline-style (-s)
When true, includes styles inline in the component.ts file. Only CSS styles can
be included inline. By default, an external styles file is created and referenced in
the component.ts file.
--inline-template (-t)
When true, includes template inline in the component.ts file. By default, an
external template file is created and referenced in the component.ts file.
external template file is created and referenced in the component.ts file.

long version

ng generate component hello --inline-style --inline-template --flat --dry-run

short version

ng g c hello -s -t --flat -d

terminal output

CREATE src/app/hello.component.spec.ts (621 bytes)


CREATE src/app/hello.component.ts (262 bytes)
UPDATE src/app/app.module.ts (545 bytes)

NOTE: The "dryRun" flag means no changes were made.

Now create the component and delete afterwards the test-몭le.

ng g c hello -s -t --flat
rm ./src/app/hello.component.spec.ts

import {Component, Input, OnInit} from '@angular/core';

@Component({
selector: 'hello',
template: `<h1>Hello {{name}}!</h1>`,
styles: [
]
})
export class HelloComponent implements OnInit {
export class HelloComponent implements OnInit {
@Input() name: string;

constructor() { }

ngOnInit(): void {
}

 The selector should be kebab-cased (ie app-hello)

Don’t forget to insert the <hello>-Tag into the app.component.html

see also:
Use Single File Components by Default in Angular

Extend the unit-converter-example


몭ll the options of the select-element from an array

refactor the conversion algorithm


use css to

make the width of the input 몭eld to 50px

use the font-family "Lato"

Table → border-collapse: collapse;

make the padding 5px

make the table-header background lightgrey with a solit black border at the
bottom

input-ps-kw.component.ts

input-ps-kw.component.html

input-ps-kw.component.css

app.component.ts

app.component.html

app.component.css

Solution on StackBlitz

7. Accessing Web APIs


7.1. Run Demo Server
Here we use a simple Quarkus Server

available as jar-몭le and on github

download

curl -L https://github.com/htl-leonding-example/demo-
api/releases/download/1.0.0/demo-api.jar -o demo-api.jar

run

java -jar demo-api.jar

check, if it works
http://localhost:8080/q/swagger-ui/
Use nodejs

retrieve all persons


http://localhost:8080/api/people

7.2. Use REST Client in WebStorm


Create an empty .http - 몭le (here: requests.http) …

... in a folder (here: http-requests)

you can access examples for GET, POST, … - requests

"Add environment 몭le" → http-client.env.json


{
"dev": {
"host": "http://localhost:8080/api"
}
}

requests.http

// Get a list of people


GET {{host}}/people

###
// Get a list of todo items
GET {{host}}/todos

###
// Add a todo item
POST {{host}}/todos
Content-Type: application/json

{
"description": "Shopping",
"assignedTo": "Eve"
}

###
// Add a todo item with unknown person
POST {{host}}/todos
Content-Type: application/json

{
"description": "Shopping",
"assignedTo": "Nobody"
}

###
// Get first todo item
GET {{host}}/todos/0

###
// Update todo item
PATCH {{host}}/todos/0
Content-Type: application/json

{
"description": "Homework"
}

###
// Update todo item to unknown person
PATCH {{host}}/todos/0
Content-Type: application/json

{
"assignedTo": "Nobody"
"assignedTo": "Nobody"
}

###
GET {{host}}/todos

###
// Update todo item
PATCH {{host}}/todos/0
Content-Type: application/json

{
"done": true
}

###
GET {{host}}/todos

###
// Delete todo item
DELETE {{host}}/todos/0

###
GET {{host}}/todos

Install REST Client Plugin in VsCode

7.3. Consuming Web APIs


Angular docs

import {HttpClient} from '@angular/common/http';

@Component({ ...
})
export class MyComponent {

constructor(private http: HttpClient) { ... }


...
}

HttpClient is a service

Dependency Injection is used

ng new todo-rest
cd todo-rest
webstorm .

app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';


import {HttpClientModule} from '@angular/common/http'; 2

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule, HttpClientModule 1

],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

1 add HttpClientModule
2 the import will be done automatically

app.component.ts

import {Component} from '@angular/core';


import {HttpClient} from '@angular/common/http'; 2

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'todo-rest';

constructor(private http: HttpClient) { 1

}
}

7.3.1. Get a List of Persons


1 add the constructor
2 the import will be done automatically
Observable
You get noti몭ed, when a asynchronous operation is done

app.component.ts

import {Component} from '@angular/core';


import {HttpClient} from '@angular/common/http';

interface IPerson {
name: string;
}

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
people: IPerson[] = []; 1

constructor(private http: HttpClient) {


}

async loadPeople() { 2
this.people = await this.http
.get<IPerson[]>('http://localhost:8080/api/people')
.toPromise();

}
}
app.component.html

<button (click)="loadPeople()">Load People</button>

<ul>
<li *ngFor="let person of people">{{person.name}}</li>
</ul>

7.3.2. Get a List of ToDos


app.component.html

<button (click)="loadPeople()">Load People</button>

<ul>
<li *ngFor="let person of people">{{person.name}}</li>
</ul>

<button (click)="loadTodos()">Load Todos</button>

<ul>
<ul>
<li *ngFor="let todo of todos">{{todo.description}}</li>
</ul>

app.component.ts

import {Component} from '@angular/core';


import {HttpClient} from '@angular/common/http';

interface IPerson {
name: string;
}

interface ITodoItem {
id: number;
description: string;
assignedTo: string;
}

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
people: IPerson[] = [];
todos: ITodoItem[] = [];

constructor(private http: HttpClient) {


}

async loadPeople() {
this.people = await this.http
.get<IPerson[]>('http://localhost:8080/api/people')
.toPromise();
}

async loadTodos() {
this.todos = await this.http
.get<ITodoItem[]>('http://localhost:8080/api/todos')
.toPromise();
}
}

The response is empty, but it works

7.3.3. Add a Todo


app.component.html

<button (click)="loadPeople()">Load People</button>

<ul>
<li *ngFor="let person of people">{{person.name}}</li>
</ul>
</ul>

<button (click)="loadTodos()">Load Todos</button>


<button (click)="addDemoData()">Add Demo Data</button>

<ul>
<li *ngFor="let todo of todos">{{todo.description}}</li>
</ul>

app.component.ts

...
async addDemoData() {
await this.http
.post('http://localhost:8080/api/todos', {
'description': 'Shopping',
'assignedTo': 'Eve'
}).toPromise();
this.loadTodos();
}
...

7.3.4. Add Delete Buttons


app.component.html

<button (click)="loadPeople()">Load People</button>

<ul>
<li *ngFor="let person of people">{{person.name}}</li>
</ul>

<button (click)="loadTodos()">Load Todos</button>


<button (click)="addDemoData()">Add Demo Data</button>

<ul>
<li *ngFor="let todo of todos">
{{todo.description}} <button (click)="deleteItem(todo.id)">X</button>
</li>
</ul>

app.component.ts

...
async deleteItem(id: number) {
await this.http.delete(`http://localhost:8080/api/todos/${id}`).toPromise();
this.loadTodos();
}
...

Parameter: ${id}
use backticks, because of parameters

7.3.5. Cooking Recipe


async

await

.toPromise()

 In real world nobody uses http directly. You create your own service.

7.3.6. Exercise
https://github.com/htl-leonding-college/htl-mobile-
computing/tree/master/angular/9020-ng-todo
8. Routing
8.1. What is Routing?
A router is a component that takes the url of a page and turns it into html. Navigation
in the app

 Diese De몭nition überarbeiten

8.2. Implement Routing


Create a demo project with routing (몭rst with "-d")

ng new router-demo --routing

A project with routing

import { BrowserModule } from '@angular/platform-browser';


import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module'; 1

import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule 2
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

 This is the default in an Angular application

The routing module - app-routing.module.ts

import { NgModule } from '@angular/core';


import { Routes, RouterModule } from '@angular/router'; 1

const routes: Routes = []; 2


@NgModule({
imports: [RouterModule.forRoot(routes)], 3

exports: [RouterModule] 4
})
export class AppRoutingModule { }

The RouterModule ⓸ is loaded from Angular ⓵. Thr RouterModule ⓶ uses a route


connection ⓷ to control the routing.

Create a new component

ng generate component Customers


ng generate component CustomersDetail
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'customers' }, 1
{ path: 'customers', component: CustomersComponent }, 2
{ path: 'customers/:id', component: CustomersDetailComponent } 3

];

1 When the full path is empty, redirect to 'customers'.

8.3. router-outlet
app-component.ts

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'router-demo';
}

In app.component.html we see the <router-outlet></router-outlet>-Tag. But


we will use a template now.

1 import {Component} from '@angular/core';


2
3 @Component({
4 selector: 'app-root',
5 template: `
6 <h1>Welcome</h1>
6 <h1>Welcome</h1>
7 <router-outlet></router-outlet>
8 <p>Footer, (c) by ...</p>
9 `,
10 styleUrls: ['./app.component.css']
11 })
12 export class AppComponent {
13 title = 'router-demo';
14 }
8.3.1. loading the full page
customers.component.ts

1 import { Component, OnInit } from '@angular/core';


2
3 @Component({
4 selector: 'app-customers',
5 templateUrl: './customers.component.html',
6 styleUrls: ['./customers.component.css']
7 })
8 export class CustomersComponent implements OnInit {
9
10 public customers = [
11 { name: 'Customer One', id: 1},
12 { name: 'Customer Two', id: 2},
13 { name: 'Customer Three', id: 3},
14 ];
15
16 constructor() { }
17
18 ngOnInit(): void {
19 }
20 }

customers.component.html

<ul>
<li *ngFor="let c of customers">{{ c.name }}</li>
</ul>
8.3.2. routerLink - partially loading a page

<ul>
<li *ngFor="let c of customers">
<a href="/customers/{{ c.id }}">{{ c.name }}</a>
</li>
</ul>

Problem:
Every time when loading a detail page and when returning to the overview page,
the whole pages are loaded.

1 <ul>
2 <li *ngFor="let c of customers">
3 <a [routerLink]="c.id">{{ c.name }}</a>
4 </li>
5 </ul>

Now, there is no network activity anymore


8.3.3. Access the PathParam (the id in the route)
customers.component.html

<h1>This is Customer Number {{ ?? }}</h1>

We call a service Activatedroute also part of @angular/router.


The paramMap is an observable. Because in single-page-applications, there could be
back and forward - buttons for ie the detail data. That means the id would change
often. But it is not necessary to load the whole page. So the paramMap is observable to
react on changes of the id.

In our simple app this is not necessary, the id wil not change all the time. We will
simplify the work with the paramMap and work w/o observables.

customers-detail.components.ts

public id = '';

constructor(private route: ActivatedRoute) {


route.paramMap.subscribe(map => this.id = map.get('id'));
}

We subscribe to the paramMap and get the id once at the beginnening, when the
customer is loaded.

customers.component.html

<h1>This is Customer Number {{ id }}</h1>


 Homework - Pokemon

9. RxJS Basics
9.1. Recap
For better understanding please view this …

What the heck is the event loop anyway…


Watch later Share
Watch on

Overview of Blocking vs Non-Blocking

Promises

9.2. Introduction
Reactive extensions for Javascript

It is a collection of helper methods which operates on top of the observer pattern

Observable: object where an outsider (observer) can look at and the observer is
noti몭ed when something interesting happens

ie an asynchronous operation has completed. The the observable pushes


information to the observer

Our user interface could be the observer. Whenever an asynchronous operation


completes we will like to display the result on the user interface.
Our typescript code (the ui) is the observer, the observable could be a button.

When clicking the button an event is sent to the observer (our typescript project)

Another observable is an web api call. If we call a web api the browser is doing the
network tra몭c in an asynchronous way. So you are just initiating the network call,
but then you have to wait until the browser actively noti몭es you: "I am done"

This is the reason why the network call (http client GET, POST, …) is an observable.

You will be noti몭ed once the network request has been completed

The http client is the observable.

Our project is the observer.

A very important concept of observables is the concept of pushing

The observer does not de몭ne, when he gets the data.

The observable is NOT be active until somebody asks me, wether something is
happening. This would be polling.

The observable activiely pushes data to the observer when something happens.

The observer (as the consumer) only registrates a function and the observable will
call this function.

ng new rxjs-demo --inlineStyle=true --inlineTemplate=true --routing=false --style=css


--skip-tests=true
cd rxjs-demo
webstorm .

app.component.ts

import { Component } from '@angular/core';


//import {} from 'rxjs'; // will be auto-imported when using Observable
import {Observable} from 'rxjs';

@Component({
selector: 'app-root',
template: `
<button (click)="doSomethingWithRx()">Start</button>
`,
styles: []
})
export class AppComponent {

doSomethingWithRx() {
console.log("Let's start ...");
const observable = new Observable()
const observable = new Observable()
}
}

http://reactivex.io/

https://rxjs-dev.몭rebaseapp.com/

https://angular.io/guide/rx-library

https://www.learnrxjs.io/

RxJS is available in many languages


http://reactivex.io/languages.html

Observable gets a subscribe method

This is the method which will be called as soon as the observer starts to listen.

The observer provides a method which will be called when something happens.

app.component.ts
app.component.ts

...
const observable = new Observable(subscriber => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
...

With .next(…) the observable can send data to the subscriber (observer).

This observable is inactive, cold, dead, … Because nobody is listening.

if we want somebody to listen, we have to call a method

There are three methods. They will be called …:


next: … whenever something interesting happens

error: … whenever an error happens

complete: … when the observer says: "I am done"

app.component.ts

...

doSomethingWithRx() {
console.log("Let's start ...");
const observable = new Observable(subscriber => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
subscriber.complete();
});

observable.subscribe( 1

val => console.log(`I got ${val}`)


val => console.log(`I got ${val}`)
);
}
}

1 this method is called, when the observable calls .next(…)

add delayed actions

1 doSomethingWithRx() {
2 console.log('Let\'s start ...');
3 const observable = new Observable(subscriber => {
4 subscriber.next(1);
5 subscriber.next(2);
6 subscriber.next(3);
7 setTimeout(() => { 1
8 subscriber.next(4);
9 subscriber.next(5);
10 subscriber.complete();
11 }, 250);
12 });
13
14 observable.subscribe(
15 val => console.log(`I got ${val}`)
16 );
17 }
1 here we simulate the execution of asynchronous code

addan error message

1 doSomethingWithRx() {
2 console.log('Let\'s start ...');
3 const observable = new Observable(subscriber => {
4 subscriber.next(1);
5 subscriber.next(2);
6 subscriber.next(3);
7 setTimeout(() => {
8 subscriber.next(4);
9 subscriber.error('Something bad happened ...');
10 subscriber.next(5);
11 subscriber.complete();
12 }, 250);
13 });
14
15 observable.subscribe(
16 val => console.log(`I got ${val}`),
17 err => console.log(`Ops: ${err}`)
18 );
19 }

9.3. Method 'of()'


Helper-method which converts the arguments to an observable sequence.

https://rxjs-dev.몭rebaseapp.com/api/index/function/of

// observable.subscribe(
// val => console.log(`I got ${val}`),
// err => console.log(`Ops: ${err}`)
// );

of(1, 2, 3, 4, 5, 6).subscribe(
val => console.log(`I got ${val}`)
);

This is still synchronous!!!

9.4. Method 'interval(…)'


Creates an Observable that emits sequential numbers every speci몭ed interval of time,
on a speci몭ed

https://rxjs-dev.몭rebaseapp.com/api/index/function/interval
interval(250).subscribe(
val => console.log(`I got ${val}`)
);

This runs noow for ever

But how can we control this?

Any kind of observable gives me back a subscription

Observer

Observable

Subscription

The Observer has a subscription at the Observable

With a timeout we can unsubscribe.

9.5. Unsubscribe with a timer


...
const subscription = interval(250).subscribe(
const subscription = interval(250).subscribe(
val => console.log(`I got ${val}`)
);

setTimeout(() => subscription.unsubscribe(), 5000);


...

But there is a more elegant way.

9.6. Operator 'take(…)'


...
interval(250)
.pipe(take(5))
.subscribe(
val => console.log(`I got ${val}`)
);

9.7. Operator 'map(…)'


...
interval(250)
.pipe(map(x => x * 2), take(5))
.subscribe(val => console.log(`I got ${val}`)
);
...

9.8. Operator '몭lter(…)'


...
interval(250)
.pipe(
map(x => x * 2),
filter(x => x <= 2),
take(5)
)
.subscribe(val => console.log(`I got ${val}`)
);

The order of the operators (map and 몭lter) does matter

9.9. Function 'concat(…)'


https://rxjs-dev.몭rebaseapp.com/api/index/function/concat

concat(
interval(250)
.pipe(take(5)),
.pipe(take(5)),
interval(100)
.pipe(take(5))
).pipe(map(x => x * 2))
.subscribe(val => console.log(`I got ${val}`));

9.10. Function 'merge(…)'


https://rxjs-dev.몭rebaseapp.com/api/index/function/merge

merge(
interval(250)
.pipe(take(5)),
interval(100)
.pipe(take(5))
).pipe(map(x => x * 2))
.subscribe(val => console.log(`I got ${val}`));

 What is the di몭erence between concat and merge?

RxJS is very powerful. But what does this mean for Angular?

9.11. RxJS for the Angular UI


1 import {Component} from '@angular/core';
2 import {concat, interval, merge, Observable, of} from 'rxjs';
3 import {filter, map, take} from 'rxjs/operators';
4
5 @Component({
6 selector: 'app-root',
7 template: `
8 <button (click)="doSomethingWithRx()">Start</button>
9 <p>{{ values }}</p>
10 `,
11 styles: []
12 })
13 export class AppComponent {
14
15 public values: Observable<number>;
16
17 doSomethingWithRx() {
18 ...
19
20 this.values = merge(
21 interval(250)
22 .pipe(take(5)),
23 interval(100)
24 .pipe(take(5))
25 ).pipe(map(x => x * 2));
26 } 1
26 }
27 }

1 Because we removed the .subscribe(…) this Observable is cold and will not work.

{{ values }} shows only a "object"

@Component({
selector: 'app-root',
template: `
<button (click)="doSomethingWithRx()">Start</button>
<p>{{ values | async }}</p> 1
`,
styles: []
})

1 when we add "| async" the ui subscribes to the observable and it works
9.12. RxJS for Web APIs (REST)
cd ./htl-mobile-computing/angular/0010-demo-api
npm start

app.module.ts

import { BrowserModule } from '@angular/platform-browser';


import { NgModule } from '@angular/core';
import { HttpClientModule} from '@angular/common/http'; 1

import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule 2
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

return type is an observable of an array


app.component.ts

import {Component} from '@angular/core';


import {concat, interval, merge, Observable, of} from 'rxjs';
import {filter, map, retry, take} from 'rxjs/operators';
import {HttpClient} from '@angular/common/http';

@Component({
selector: 'app-root',
template: `
<button (click)="doSomethingWithRx()">Start</button>
<ul>
<li *ngFor="let v of values | async">{{v.name}}</li> 1

</ul>
`,
styles: []
})
export class AppComponent {

public values: Observable<any[]>;

constructor(private http: HttpClient) {


constructor(private http: HttpClient) {
}

doSomethingWithRx() {
this.values = this.http.get<any[]>("http://localhost:8080/api/people")
.pipe(retry()); 2
}
}

1 Pay attention to the subscription (| async)

10. Flexbox
responsive apps

https://caniuse.com/#feat=몭exbox

https://developer.mozilla.org/en-
US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox
Semantic-ui is based on 몭exbox: https://semantic-ui.com/examples/grid.html

10.1. 몭ex-direction: row and column


display:몭ex enables Flexbox

.box {
display: flex;
}

10.2. 몭ex-wrap

10.3. 몭ex-items
Google-Suche: css 몭exbox

 A Complete Guide to Flexbox

10.4. Media Query


Google-Suche: css media query

CSS Media Queries

10.5. Angular Flex Layout


https://github.com/angular/몭ex-layout

10.5.1. Installation
install @angular/몭ex-layout, @angular/cdk

import FlexLayoutModule

https://github.com/angular/몭ex-layout#getting-started

10.5.2. Usage
instead of css-몭les you can use Angular-directives

<div fxLayout="row" fxLayoutAlign="space-between">


</div>

it is enabled for data-binding


10.5.3. Live Demo
https://tburleson-layouts-demos.몭rebaseapp.com/#/docs

10.5.4. Exercise: conference-agenda


https://github.com/htl-leonding-college/htl-mobile-
computing/tree/master/angular/9120-conference-agenda

Rainer Stropeks explanation

mediaQuery de몭nitions
https://github.com/angular/몭ex-layout/wiki/API-Documentation

https://github.com/angular/몭ex-layout/wiki/Responsive-API

https://github.com/angular/몭ex-layout/wiki/Responsive-API#mediaqueries-and-
aliases

<div class="slot" *ngFor="let slot of slots" fxLayout.lt-md="column" fxLayout.gt-


sm="row"
fxLayoutAlign.gt-sm="start center">
<div class="time nowrap">{{ slot.start }}-{{ slot.end }}</div>
<div fxLayout.xs="column" fxLayout.gt-xs="row">
<div *ngFor="let session of getSessionsOfSlot(slot.id)">
<div class="session">
<div class="session-title">{{ session.title }}</div>
<div class="session-speaker">{{ session.speaker }}</div>
<div class="session-room">Room {{ session.room }}</div>
</div>
</div>
</div>
</div>

10.5.5. Static API


https://github.com/angular/몭ex-layout/wiki/Declarative-API-Overview

10.5.6. Grid System


https://github.com/angular/몭ex-layout/wiki/Declarative-API-Overview#api-for-dom-
containers

gdAlignColumns, gdAlignRows, gd Areas, …

10.5.7. TypeScript API


https://github.com/angular/몭ex-layout/wiki/API-Documentation
 MediaObserver

A service injectable through the constructor

import {MediaObserver} from '@angular/flex-layout';


constructor(public mediaObserver: MediaObserver ) {
mediaObserver.media$.subscribe(...); 1
}

1 media$ is an observable ($)

10.5.8. ngClass-, ngStyle-, …-API


https://github.com/angular/몭ex-layout/wiki/Declarative-API-Overview#api-for-any-
element

https://github.com/angular/몭ex-layout/wiki/ngClass-API

11. Testing with Jasmine


11.1. Testing
Customers want error free, working software.

Errors (always) occurs.

Errors will be 몭xed.

Sometimes, when 몭xing an error (ie login) another error occurs (ie in registration
page).

The error on the registration page exists, because of 몭xing the login.

This is called a regression bug. Fixing or extending your code may have side-
e몭ects to existing parts of code.

Therefore you have to use regresseion testing: Testing the existing code again and
again.

For regression testing you use automated tests.

With automated tests you are preventing bugs not only 몭xing bugs.

11.2. Testing in Angular


Create a demo project

ng new jasmine-intro --routing=false --style=css


cd jasmine-intro
webstorm .

Open a terminal in Webstorm

npm test

The browser is opened automatically


Angular runs the application when testing

The terminal shows the success of the test

When changing something, and clicking into the terminal window, the tests run
again automatically.

Now the tests breaks


11.3. How to write tests
11.3.1. Add a Simple Service
Services are available in the whole Angular app.
ng generate service circle-math

11.3.2. Writing a Function to test


circle-math.service.ts

1 import { Injectable } from '@angular/core';


2
3 @Injectable({
4 providedIn: 'root'
5 })
6 export class CircleMathService {
7
8 constructor() { }
9
10 calculateCircleArea(radius: number): number {
11 return radius * radius * Math.PI;
12 }
13 }

The test 몭le for our simple test is unnecessarily too complex

import { TestBed } from '@angular/core/testing';

import { CircleMathService } from './circle-math.service';

describe('CircleMathService', () => {
let service: CircleMathService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CircleMathService);
});

it('should be created', () => {


expect(service).toBeTruthy();
});
});

Only two lines remain

import { TestBed } from '@angular/core/testing';

import { CircleMathService } from './circle-math.service';

describe('CircleMathService', () => { 1

it('should be created', () => { 2


});
});

1 describe is like a headline for tests (part of jasmine)

it
2 it should be created

11.3.3. AAA-Pattern

import { TestBed } from '@angular/core/testing';

import { CircleMathService } from './circle-math.service';

describe('CircleMathService', () => {
it('should calculate the circles area correctly', () => {
// Prepare (arrange, given)

// Execute (act, when)

// Check results (assert, then)


});
});

11.3.4. Implement the Test

1 import { TestBed } from '@angular/core/testing';


2
3 import { CircleMathService } from './circle-math.service';
4
5 describe('CircleMathService', () => {
6 it('should calculate the circles area correctly', () => {
7 // Prepare (arrange, given)
8 const mathService = new CircleMathService();
9
10 // Execute (act, when)
11 const result = mathService.calculateCircleArea(1);
12
13 // Check results (assert, then)
14 expect(result).toBe(Math.PI);
15 });
16 });
Now there are 4 tests.

Write nearly passing tests at the beginning. Don’t write passing tests.
 Every test should fail once. (ie const result =
mathService.calculatorCircleArea(1.1);)

11.4. To Focus on one Test


use fit`instead of `it.

The other test will not be executed

1 import { TestBed } from '@angular/core/testing';


2
3 import { CircleMathService } from './circle-math.service';
4
5 describe('CircleMathService', () => {
6 fit('should calculate the circles area correctly', () => {
7 // Prepare (arrange, given)
8 const mathService = new CircleMathService();
9
10 // Execute (act, when)
11 const result = mathService.calculateCircleArea(1.1);
12
13 // Check results (assert, then)
14 expect(result).toBe(Math.PI);
15 });
16 });
11.4.1. How to set a breakpoint in the browser
Open the source code in the browser

Reload the Browser ( F10 or Cmd - R )


Now you can use the button in the upper right corner to continue …

ie step into the function call

remove the error and the test works again

11.4.2. How to set a breakpoint in Webstorm


1 Set the breakpoint in the code.
2 Choose the test-con몭guration.
3 Click the Debug button
4 When reaching the breakpoint, you get the debug window

You can start/test/debug the app in the terminal but also in Webstorm
 itself.

11.5. Testing "Edge Cases"


ng generate service graphic-math

graphic-math.service.ts

1 import { Injectable } from '@angular/core';


2 import {CircleMathService} from './circle-math.service';
3
4 @Injectable({
5 providedIn: 'root'
6 })
7 export class GraphicMathService {
8
9 constructor(private circleMath: CircleMathService) { } 1
10
11 circleRadiuses: number[] = [];
12 circleAreas: number[] = [];
13
14 calculateAreasOfAllCircles() {
15 this.circleAreas = [];
16 for (const r of this.circleRadiuses) {
17 this.circleAreas.push(this.circleMath.calculateCircleArea(r));
18 }
19 }
20 }

1 Because CircleMathService is a service you can use it like a httpService


The unit-tests still run

Create a test
graphic-math.service.spec.ts

import { TestBed } from '@angular/core/testing';

import { GraphicMathService } from './graphic-math.service';


import {CircleMathService} from './circle-math.service';

describe('GraphicMathService', () => {
it('should calculate areas of circles correctly', () => {
// arrange
const graphicMathService = new GraphicMathService(new CircleMathService());
graphicMathService.circleRadiuses.push(1);
graphicMathService.circleRadiuses.push(2);

// act
graphicMathService.calculateAreasOfAllCircles();

// assert
expect(graphicMathService.circleAreas.length).toBe(2);
expect(graphicMathService.circleAreas[0]).toBe(Math.PI);
expect(graphicMathService.circleAreas[1]).toBe(2 * 2 * Math.PI); 1

});
});

1 2 * 2 * Math.PI → This is not good. Don’t repeat the business logic. When the
algorithm for calculting the area would change, all tests must be rewritten

add an "edge test"

it('should handle empty input array correctly', () => {


// arrange
const graphicMathService = new GraphicMathService(new CircleMathService());

// act
graphicMathService.calculateAreasOfAllCircles();

// assert
expect(graphicMathService.circleAreas.length).toBe(0);
});

In a ideal project the end users will give you the test cases to implement

The customers tells you: "If I input this, I will get that" → write SysSpec and or User
Stories

Ask the customers for Acceptance Criterias

 These are no unit tests


We are not testing one "thing"

We are not only testing GraphicMathService, we are also testing


CircleMathService

We need mock objects to test only one aspect that means only one class.

11.6. Mock Objects


11.6.1. Excursus: Inline Reference Information in WebStorm
View - Quick De몭nition or Alt - Space

Code reference information

in VSC: peek-in-de몭nition

How to show inline reference information of an object in WebStorm

11.6.2. Create a Spy

1 it('should calculate areas of circles correctly with mocking', () => {


2 // arrange
3 const graphicMathServiceMock = {
4 calculateCircleArea:
4 calculateCircleArea:
5 jasmine.createSpy('calculateCircleArea').and.returnValue(42)
6 };
7
8 const graphicMathService = new GraphicMathService(graphicMathServiceMock);
9 1
10 graphicMathService.circleRadiuses.push(1);
11 graphicMathService.circleRadiuses.push(2);
12
13 // act
14 graphicMathService.calculateAreasOfAllCircles();
15
16 // assert
17 expect(graphicMathService.circleAreas.length).toBe(2);
18 expect(graphicMathService.circleAreas[0]).toBe(42); 2
expect(graphicMathService.circleAreas[1]).toBe(42); 2

});

1 you use now the mock object


2 the return value is always 42

11.6.3. Resources for Jasmine


Jasmine Introduction

On the left hand, you see the theory, on the right hand the code
11.7. Disable a Test (Test is Pending)
use xit instead of it

app.component.html

<h1>Calculator</h1>

While writing the html code, the tests break

So we can disable the tests

Remove the x after refactoring your app !!!

app.component.html

<h1>Calculator</h1>

<p>
<label>Radius:</label>
<input type="number" [(ngModel)]="radius"><br/>
<button (click)="calculate()">Calculate</button>
</p>
<p>
The result is <span>{{ result }}</span>
</p>

11.8. Test Modules in Angular


A separate TestModule has to be de몭ned for every Test Suite

Because of Mockups

app.component.spec.ts

import { TestBed, async } from '@angular/core/testing';


import { AppComponent } from './app.component';

describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents(); 1
}));

it('should create the app', () => {


const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
expect(app).toBeTruthy();
});

it(`should have as title 'jasmine-intro'`, () => {


const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('jasmine-intro');
});

it('should render title', () => {


const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain('jasmine-
intro app is running!');
});
});

1 this module is for testing only with mockups. It does not run dependencies.

1 import { TestBed, async } from '@angular/core/testing';


2 import { AppComponent } from './app.component';
3 import {FormsModule} from '@angular/forms';
4
5 describe('AppComponent', () => {
6 beforeEach(async(() => {
7 TestBed.configureTestingModule({
8 imports: [
9 FormsModule
10 ],
11 declarations: [
12 AppComponent
13 ],
14 }).compileComponents();
15 }));
16
17 it('should create the app', () => {
18 const fixture = TestBed.createComponent(AppComponent);
19 const app = fixture.componentInstance;
20 expect(app).toBeTruthy();
21 });
22
23 it(`should have as title 'jasmine-intro'`, () => {
24 const fixture = TestBed.createComponent(AppComponent);
25 const app = fixture.componentInstance;
26 expect(app.title).toEqual('jasmine-intro');
27 });
28
29 // it('should render title', () => {
30 // const fixture = TestBed.createComponent(AppComponent);
31 // fixture.detectChanges();
32 // const compiled = fixture.nativeElement;
33 // expect(compiled.querySelector('.content
34 span').textContent).toContain('jasmine-intro app is running!');
35 // });
35 // });
});

Now the test are passing

11.8.1. Fixing the example

<h1>Calculator</h1>

<p>
<label>Radius:</label>
<input type="number" [(ngModel)]="radius"><br/>
<button (click)="calculate()">Calculate</button>
</p>
<p>
The result is <span>{{ result }}</span>
</p>

import { Component } from '@angular/core';


import {CircleMathService} from './circle-math.service';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'jasmine-intro';
radius = 1;
result: number;

constructor(private calculator: CircleMathService) {


}

calculate() {
this.result = this.calculator.calculateCircleArea(this.radius);
}
}
11.9. Homework
https://github.com/htl-leonding-college/htl-mobile-
computing/tree/master/angular/0080-testing

12. OpenID Connect


OAUTH2

In former times each app had its own user data

Now the user data is centralized, the apps / services are accessing this central store

example: employee leaves the company,

hires at a competitor

you forget to remove this employee

now a competitor can access this service ie discussion group for important stu몭

resource owner

id-token → passport

access-token → visa
Version 1.0.0
Last updated 2021-04-28 09:53:44 UTC

You might also like