Slide 1

Slide 1 text

Angular Solid Foundations

Slide 2

Slide 2 text

@GregOnNet Hello _der_markusende

Slide 3

Slide 3 text

Schedule ☕ 11:00 13:00 ☕ 16:00

Slide 4

Slide 4 text

What is Angular?

Slide 5

Slide 5 text

SPA Framework Angular is a Web Framework for SPAs SPA: Single Page Application Framework: ”reusable set of libraries or classes for a software system or subsystem” “provides a standard way to build and deploy applications.” https://en.wikipedia.org/wiki/Software_framework

Slide 6

Slide 6 text

History Angular v0.9.0 2010 v1.7.0 2018 v1.7.x LTS until 2021 v1.6.0 2016 complete rewrite v2.0.0 2016 v4.0.0 2016 v3 skipped v8.0.0 2019

Slide 7

Slide 7 text

Overview … and more!

Slide 8

Slide 8 text

Overview

Slide 9

Slide 9 text

Release Cycle https://angular.io/guide/releases In general, you can expect the following release cycle: ● A major release every 6 months ● 1-3 minor releases for each major release ● A patch release almost every week

Slide 10

Slide 10 text

Mission Apps that users ♥ to use Apps that developers ♥ to build Community where everyone feels welcome

Slide 11

Slide 11 text

Community https://angular.io/events

Slide 12

Slide 12 text

Angular@Google …

Slide 13

Slide 13 text

Hands on Meet Angular http://bit.do/ngde19-sf-tasks

Slide 14

Slide 14 text

‍ Hands on npm start -- --open opens your browser Meet Angular

Slide 15

Slide 15 text

automatic application reload

Slide 16

Slide 16 text

Components

Slide 17

Slide 17 text

Think in small building blocks Toggle All Prepare Workshop Hold the Workshop ToDo App

Slide 18

Slide 18 text

Group wisely Toggle All Prepare Workshop Hold the Workshop ToDo App

Slide 19

Slide 19 text

Think in features Toggle All Prepare Workshop Hold the Workshop ToDo App

Slide 20

Slide 20 text

What is a Component? Toggle All Prepare Workshop Hold the Workshop ToDo App Representation & Interaction ⚡

Slide 21

Slide 21 text

What is a Component? Template & Styling

Slide 22

Slide 22 text

What is a Component? Interaction

Slide 23

Slide 23 text

app.component.ts Component Declaration import { Component } from '@angular/core'; @Component({ selector: 'nde-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'Your'; }

Slide 24

Slide 24 text

Component Declaration app.component.ts index.html import { Component } from '@angular/core'; @Component({ selector: 'nde-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'Your'; } Look, an Angular Component In the wild.

Slide 25

Slide 25 text

app.component.ts Component Declaration import { Component } from '@angular/core'; @Component({ selector: 'nde-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'Your'; }

Slide 26

Slide 26 text

app.component.ts Component Declaration import { Component } from '@angular/core'; @Component({ selector: 'nde-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'Your'; }

Slide 27

Slide 27 text

app.component.ts Component Declaration import { Component } from '@angular/core'; @Component({ selector: 'nde-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'Your'; }

Slide 28

Slide 28 text

app.component.ts app.component.html Bind component-values {{ title }} app is running! /* imports */ @Component(/* … */) export class AppComponent { title = 'Your'; } It’s a match!

Slide 29

Slide 29 text

app.component.ts app.component.html Bind component-values {{ title }} app is running! /* imports */ @Component(/* … */) export class AppComponent { title = 'Your'; } Bind values of a component with double curly braces to the template.

Slide 30

Slide 30 text

http://bit.do/ngde19-sf-tasks Hands on Enhance AppComponent

Slide 31

Slide 31 text

Beyond AppComponent AppComponent We are here! index.html

Slide 32

Slide 32 text

Beyond AppComponent AppComponent TodoCheckerComponent Next! app.component.html

Slide 33

Slide 33 text

Beyond AppComponent AppComponent TodosComponent TodoCheckerComponent Finally! TodoCheckerComponent TodoCheckerComponent

Slide 34

Slide 34 text

Beyond AppComponent child of A component can have one child...

Slide 35

Slide 35 text

Beyond AppComponent child of child of child of child of … A component can have multiple children.

Slide 36

Slide 36 text

Beyond AppComponent You build a Component tree .

Slide 37

Slide 37 text

Hands on Create your 1st component http://bit.do/ngde19-sf-tasks

Slide 38

Slide 38 text

todo-checker.component.ts todo-checker.component.html Property-Binding {{ todo.text }} @Component(/* … */) export class TodoCheckerComponent { todo = { text: 'Buy ', isDone: true }; }

Slide 39

Slide 39 text

todo-checker.component.ts todo-checker.component.html Event-Binding {{ todo.text }} @Component(/* … */) export class TodoCheckerComponent { // todo = ... emitToggle() { alert( `${this.todo.text} clicked.` ); } When change is fired emitToggle gets executed.

Slide 40

Slide 40 text

Hands on Simple bindings http://bit.do/ngde19-sf-tasks

Slide 41

Slide 41 text

Beyond AppComponent AppComponent TodoCheckerComponent app.component.html

Slide 42

Slide 42 text

app.component.html Feed your component with some data AppComponent @Input Define your own property binding. TodoCheckerComponent

Slide 43

Slide 43 text

todo-checker.component.ts Accept data through @Input todo-checker.component.ts todo-checker.component.html import { Input } from '@angular/core'; @Component(/* … */) export class TodoCheckerComponent { // @Input() todo; // ... {{ todo.text }}

Slide 44

Slide 44 text

React to events of a child component AppComponent TodoComponent app.component.html @Output Create your own event binding.

Slide 45

Slide 45 text

Emit custom events using @Output todo-checker.component.ts import { Output, EventEmitter } from '@angular/core'; @Component(/* … */) export class TodoCheckerComponent { // … // @Output() toggle = new EventEmitter(); emitToggle() { this.toggle.emit(this.todo); } }

Slide 46

Slide 46 text

app.component.ts React to events of a child component AppComponent TodoComponent app.component.html @Output notify(todo): void { alert(`${todo.text} clicked.`); } $event transports the todo.

Slide 47

Slide 47 text

Hands on Dataflow http://bit.do/ngde19-sf-tasks

Slide 48

Slide 48 text

Handle collections with *ngFor

Slide 49

Slide 49 text

Render components with *ngFor todo-checker.component.ts @Component({ /* … */ }) export class TodoCheckerComponent { todos = [ { text: 'Buy ', isDone: true }, { text: 'Plant ', isDone: false } ]; } todo-checker.component.html Imagine you need to render a collection of data.

Slide 50

Slide 50 text

todo-checker.component.html Render components with *ngFor todo-checker.component.ts @Component({ /* … */ }) export class TodoCheckerComponent { todos = [ { text: 'Buy ', isDone: true }, { text: 'Plant ', isDone: false } ]; } Use the built-in directive *ngFor rendering a collection of data.

Slide 51

Slide 51 text

todo-checker.component.html Render components with *ngFor todo-checker.component.ts @Component({ /* … */ }) export class TodoCheckerComponent { todos = [ { text: 'Buy ', isDone: true }, { text: 'Plant ', isDone: false } ]; }

Slide 52

Slide 52 text

todo-checker.component.html Render components with *ngFor todo-checker.component.ts @Component({ /* … */ }) export class TodoCheckerComponent { todos = [ { text: 'Buy ', isDone: true }, { text: 'Plant ', isDone: false } ]; }

Slide 53

Slide 53 text

http://bit.do/ngde19-sf-tasks Hands on Multiply your components

Slide 54

Slide 54 text

TypeScript Interfaces

Slide 55

Slide 55 text

Secure your coding with types app.component.ts notify(todo) { alert(`${todo.text} clicked.`); } No information about the passed parameter.

Slide 56

Slide 56 text

Secure your coding with types app.component.ts notify(todo): void { alert(`${todo.txt} clicked.`); } ⚠ produces error at runtime hard to identify at development-time

Slide 57

Slide 57 text

todo.ts Introducing the Interface export interface Todo { text: string; isDone: boolean; } A blueprint for your type

Slide 58

Slide 58 text

todo-checker.component.ts Using an Interface import { Todo } from '../models'; @Component(/* … */) export class TodoCheckerComponent { @Input() todo: Todo; @Output() toggle = new EventEmitter(); // ... }

Slide 59

Slide 59 text

Autocompletion for

Slide 60

Slide 60 text

Autocompletion for You need to have -Language-Service installed.

Slide 61

Slide 61 text

http://bit.do/ngde19-sf-tasks Hands on Secure your coding with types

Slide 62

Slide 62 text

Template Reference Variables

Slide 63

Slide 63 text

Interact with template elements Template What if you need to - … read attribute values? - … change attribute values?

Slide 64

Slide 64 text

Simple HTML-Element Interaction Template Define a template reference by using the #-sign + a freely chosen name.

Slide 65

Slide 65 text

Simple HTML-Element Interaction Template Pass the template reference to your component by using your freely chosen name without #.

Slide 66

Slide 66 text

Simple HTML-Element Interaction Component Template @Component(/* … */) export class MyComponent { doSomething( myInput: HTMLInputElement ) { // ... myInput.value = ''; } }

Slide 67

Slide 67 text

Simple HTML-Element Interaction Component Template @Component(/* … */) export class MyComponent { doSomething( myInput: HTMLInputElement ) { } } Please don’t miss the type annotation giving you ‍♀ more safety while you are coding,.

Slide 68

Slide 68 text

http://bit.do/ngde19-sf-tasks Hands on Create your 1st form

Slide 69

Slide 69 text

Modules

Slide 70

Slide 70 text

Think in Features Angular applications are organized in features. Features are bundled in modules.

Slide 71

Slide 71 text

Think in Modules Module Module Module Module

Slide 72

Slide 72 text

2 Different Types of Modules TypeScript (or JavaScript) Module Abstraction level: files Angular Module (NgModule) Abstraction level: app aspects VS

Slide 73

Slide 73 text

TypeScript Modules main.ts Organize source code in different files: import {MyClass} from ’../sub-folder/my-class.ts’; import {LibClass} from ’@npmscope/external-lib’; my-class.ts export class MyClass {}; lib.js export class LibClass {}; own source folder node_modules

Slide 74

Slide 74 text

Angular Modules Organize aspects (or features) of an Angular app: HttpClientModule interaction with APIs AppModule the app SharedModule Reusable components LoginModule Login services and ui TodosModule Todo list own modules built-in modules

Slide 75

Slide 75 text

todos.module.ts Module Declaration import { NgModule } from '@angular/core'; @NgModule({ declarations: [ TodosComponent, TodoCheckerComponent, // ... ] exports: [ TodosComponent ] }) export class TodosModule { } Use @NgModule directive

Slide 76

Slide 76 text

todos.module.ts Module Declaration import { NgModule } from '@angular/core'; @NgModule({ declarations: [ TodosComponent, TodoCheckerComponent, // ... ] exports: [ TodosComponent ] }) export class TodosModule { } Declare all internal components Use @NgModule directive

Slide 77

Slide 77 text

Module Declaration Declare externally usable components Declare all internal components Use @NgModule directive todos.module.ts import { NgModule } from '@angular/core'; @NgModule({ declarations: [ TodosComponent, TodoCheckerComponent, // ... ] exports: [ TodosComponent ] }) export class TodosModule { }

Slide 78

Slide 78 text

Module Declaration app.module.ts @NgModule({ declarations: [ // ... ] imports: [ TodosModule ] }) export class AppModule { } Import other modules to use exported components todos.module.ts import { NgModule } from '@angular/core'; @NgModule({ declarations: [ TodosComponent, TodoCheckerComponent, // ... ] exports: [ TodosComponent ] }) export class TodosModule { }

Slide 79

Slide 79 text

Beyond AppComponent AppComponent TodosComponent TodoCheckerComponent Now! TodoCheckerComponent TodoCheckerComponent Todos Feature / Module

Slide 80

Slide 80 text

http://bit.do/ngde19-sf-tasks Hands on Extract a feature module

Slide 81

Slide 81 text

Services

Slide 82

Slide 82 text

Business Logic Component @Component(/* … */) export class MyFirstComponent { doBusinessLogic() { // step 1 // step 2 // step 3 // ... } } Component @Component(/* … */) export class MySecondComponent { doBusinessLogic() { // step 1 // step 2 // step 3 // ... } } Don’t repeat yourself (DRY)!

Slide 83

Slide 83 text

Extract Service Class Component @Component(/* … */) export class MyFirstComponent { private service = new MyService(); doBusinessLogic() { this.service.doBusinessLogic() } } Service Class export class MyService { doBusinessLogic() { // step 1 // step 2 // step 3 // ... } } Code can be reused

Slide 84

Slide 84 text

Extract Service Class Component @Component(/* … */) export class MyFirstComponent { private service = new MyService(); doBusinessLogic() { this.service.doBusinessLogic() } } Service Class export class MyService { doBusinessLogic() { // step 1 // step 2 // step 3 // ... } } But the component creates the Service. Should not be its responsibility!

Slide 85

Slide 85 text

⚙ ➡ Dependency Injection

Slide 86

Slide 86 text

todos.service.ts Injectable Service Declaration import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class TodosService { } Define class to be injectable

Slide 87

Slide 87 text

todos.service.ts Injectable Service Declaration import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class TodosService { } Define class to be injectable globally*) *) There are other possibilities

Slide 88

Slide 88 text

Use Injectable Services Component @Component(/* … */) export class MyFirstComponent { private service: MyService; constructor(service: MyService) { this.service = service; } doBusinessLogic() { this.service.doBusinessLogic() } } Injectable Service @Injectable({ providedIn: 'root' }) export class MyService { doBusinessLogic() { // ... } } Constructor Injection by type

Slide 89

Slide 89 text

TypeScript’s parameter properties Component @Component(/* … */) export class MyFirstComponent { private service: MyService; constructor(service: MyService) { this.service = service; } // ... } Component @Component(/* … */) export class MyFirstComponent { constructor( private service: MyService ) { } // ... } Syntactic sugar with equivalent meaning! Also possible with: - public - protected - readonly

Slide 90

Slide 90 text

Component Service doing initialization work @Component(/* … */) export class NewlyGeneratedComponent implements OnInit { constructor() { } ngOnInit() { } } Ever wondered about the generated ngOnInit() method? It is a life-cycle hook that gets called When component is ready for rendering!

Slide 91

Slide 91 text

Component Service doing initialization work @Component(/* … */) export class NewlyGeneratedComponent implements OnInit { data: Data[]; constructor(private service: MyService) { } ngOnInit() { this.data = this.service.getData(); } } Use ngOnInit() for initialization work!

Slide 92

Slide 92 text

http://bit.do/ngde19-sf-tasks Hands on Create your 1st service

Slide 93

Slide 93 text

Communicate with HTTP APIs

Slide 94

Slide 94 text

CRUD operations via HTTP HTTP C R U D

Slide 95

Slide 95 text

Plug & Play | Setting up HTTP AppComponent TodosComponent

Slide 96

Slide 96 text

Register of HttpClientModule import { HttpClientModule } from '@angular/common/http'; // ... @NgModule({ imports: [ CommonModule, HttpClientModule ], //... }) export class TodosModule {}

Slide 97

Slide 97 text

http://bit.do/ngde19-sf-tasks Hands on Set up Http

Slide 98

Slide 98 text

HttpClient API of HttpClient class HttpClient { get (/* various overloads */) {} post (/* various overloads */) {} put (/* various overloads */) {} delete (/* various overloads */) {} options (/* various overloads */) {} head (/* various overloads */) {} patch (/* various overloads */) {} jsonp (/* various overloads */) {} request (/* various overloads */) {} }

Slide 99

Slide 99 text

HttpClient API of HttpClient class HttpClient { get (/* various overloads */) {} post (/* various overloads */) {} put (/* various overloads */) {} delete (/* various overloads */) {} options (/* various overloads */) {} head (/* various overloads */) {} patch (/* various overloads */) {} jsonp (/* various overloads */) {} request (/* various overloads */) {} }

Slide 100

Slide 100 text

HttpClient API of HttpClient class HttpClient { get (/* various overloads */) {} post (/* various overloads */) {} put (/* various overloads */) {} delete (/* various overloads */) {} } represents the shape of data you receive from your API.

Slide 101

Slide 101 text

HttpClient API of HttpClient class HttpClient { get (/* various overloads */): Observable {} post (/* various overloads */): Observable {} put (/* various overloads */): Observable {} delete (/* various overloads */): Observable {} } The data structure supporting you handling asynchronous operations.

Slide 102

Slide 102 text

Http GET API Demonstration httpClient.get('api.com/data') .subscribe(data => /* do something */); An Observable provides the subscribe-method. Once the request is complete you can process the loaded data.

Slide 103

Slide 103 text

Observable The callbacks of subscribe class Observable { subscribe( (value: T) => {}, (error: any) => {}, () => {}, ) // ... // see https://rxjs.dev/api/index/class/Observable for more details }

Slide 104

Slide 104 text

API Demo Understanding subscribe HTTP subscribe( (value: T) => {}, (error: any) => {}, () => {}, )

Slide 105

Slide 105 text

API Demo Understanding subscribe HTTP subscribe( (value: T) => {}, (error: any) => {}, () => {}, )

Slide 106

Slide 106 text

API Demo Understanding subscribe HTTP subscribe( (value: T) => {}, (error: any) => {}, () => { /* complete */ }, )

Slide 107

Slide 107 text

API Demo Lifecycle of a successful request subscribe ⏱ next complete subscribe( (value: T) => {}, (error: any) => {}, () => {}, )

Slide 108

Slide 108 text

API Demo Lifecycle of a failing request subscribe error ⏱ subscribe( (value: T) => {}, (error: any) => {}, () => {}, )

Slide 109

Slide 109 text

Component Inject of HttpClient @Injectable({ providedIn: 'root' }) export class TodosService { constructor( private http: HttpClient ) {} query(): Observable { return this.http.get( 'api.com/data' ); } } HttpClient is provided by HttpClientModule

Slide 110

Slide 110 text

Component Use of HttpClient @Injectable({ providedIn: 'root' }) export class TodosService { constructor( private http: HttpClient ) {} query(): Observable { return this.http.get( 'api.com/data' ); } } Please note that an Observable is returned. You want to avoid subscribe in the service.

Slide 111

Slide 111 text

todos.service.ts todos.component.ts Put everything together @Injectable({ providedIn: 'root' }) export class TodosService { constructor( private http: HttpClient ) {} query(): Observable { return this.http.get( 'api.com/data' ); } } @Component({ /* ... */ }) export class TodosComponent implements OnInit { todos: Todo[] = []; constructor( private service: TodosService) {} ngOnInit(): void { this.service.query().subscribe( todos => this.todos = todos ); } } inject

Slide 112

Slide 112 text

todos.service.ts todos.component.ts Put everything together @Injectable({ providedIn: 'root' }) export class TodosService { constructor( private http: HttpClient ) {} query(): Observable { return this.http.get( 'api.com/data' ); } } @Component({ /* ... */ }) export class TodosComponent implements OnInit { // ... constructor( private service: TodosService) {} ngOnInit(): void { this.service.query().subscribe( todos => this.todos = todos ); } } Subscribe to the Observable.

Slide 113

Slide 113 text

todos.service.ts todos.component.ts Put everything together @Injectable({ providedIn: 'root' }) export class TodosService { constructor( private http: HttpClient ) {} query(): Observable { return this.http.get( 'api.com/data' ); } } @Component({ /* ... */ }) export class TodosComponent implements OnInit { todos: Todo[] = []; // ... ngOnInit(): void { this.service.query().subscribe( todos => this.todos = todos ); } } Assign todos

Slide 114

Slide 114 text

http://bit.do/ngde19-sf-tasks Hands on Load todos from API

Slide 115

Slide 115 text

Observables are part of the RxJS framework.

Slide 116

Slide 116 text

Learn RxJS and you can master every Angular module.

Slide 117

Slide 117 text

Checkout http:/ /bit.do/ngde19-rxjs to learn more about RxJS operators.

Slide 118

Slide 118 text

RxJS’ Impact on Angular

Slide 119

Slide 119 text

Http POST API Demonstration httpClient.post('api.com/data', body) .subscribe(data => /* do something */); A post-Request expects a 2nd parameter containing the body that should be passed to the API.

Slide 120

Slide 120 text

Http Put API Demonstration httpClient.put('api.com/data', body) .subscribe(data => /* do something */); A put-Request expects a 2nd parameter containing the body that should be passed to the API.

Slide 121

Slide 121 text

http://bit.do/ngde19-sf-tasks Hands on Persist new todos

Slide 122

Slide 122 text

… After creating a todo ⚡ ⚠ List is out of sync

Slide 123

Slide 123 text

… After creating a todo Automatically reload todos after todo creation HTTP switch

Slide 124

Slide 124 text

todos.component.ts Hook into the stream createTodo(newTodo: Todo) { this.todosService .create(newTodo) .pipe(/* do something with the resonse */) .subscribe(todos => (this.todos = todos)) }

Slide 125

Slide 125 text

todos.component.ts Switching from one Request to another import { switchMap } from 'rxjs/operators'; createTodo(newTodo: Todo) { this.todosService .create(newTodo) .pipe(switchMap(() => this.todosService.query())) .subscribe(todos => (this.todos = todos)) }

Slide 126

Slide 126 text

todos.component.ts Switching from one Request to another import { switchMap } from 'rxjs/operators'; createTodo(newTodo: Todo) { this.todosService .create(newTodo) .pipe(switchMap(() => this.todosService.query())) .subscribe(todos => (this.todos = todos)) }

Slide 127

Slide 127 text

http://bit.do/ngde19-sf-tasks Hands on Auto refresh todos

Slide 128

Slide 128 text

One last thing about subscribe subscribe next complete An Observable represents a stream. ☝ We are lucky that Http completes the stream automatically.

Slide 129

Slide 129 text

One last thing about subscribe … Lots of streams do not complete automatically. (e.g. WebSockets, Store, ...) ☝ We need to unsubscribe from each stream to avoid Memory Leaks. subscribe complete next

Slide 130

Slide 130 text

Unsubscribe from an Observable todo-checker.component.ts import { Subscription } from 'rxjs'; @Component({ /* ... */}) export class TodosComponent implements OnInit { private createTodoSubscription = new Subscription(); // ... } A save place storing subscriptions.

Slide 131

Slide 131 text

Unsubscribe from an Observable todo-checker.component.ts @Component({ /* ... */}) export class TodosComponent implements OnInit { private createTodoSubscription = new Subscription(); // ... addTodo(newTodo: Todo) { this.createTodoSubscription = this.todosService .create(newTodo) .pipe(switchMap(() => this.todosService.query())) .subscribe(todos => (this.todos = todos)); } } Saving a subscription

Slide 132

Slide 132 text

Unsubscribe from an Observable todo-checker.component.ts @Component({ /* ... */}) export class TodosComponent implements OnInit, OnDestroy { private createTodoSubscription = new Subscription(); // ... ngOnDestroy(): void { this.createTodoSubscription.unsubscribe(); } } Unsubscribe from the stream once component gets destroyed.

Slide 133

Slide 133 text

Unsubscribe from an Observable todo-checker.component.ts @Component({ /* ... */}) export class TodosComponent implements OnInit { private loadTodosSubscription = new Subscription(); private createTodoSubscription = new Subscription(); // ... ngOnInit(): void { this.loadTodosSubscription = ... } addTodo(newTodo: Todo) { this.createTodoSubscription = ... } } Does not scale that well For multiple subscriptions...

Slide 134

Slide 134 text

Unsubscribe from an Observable todo-checker.component.ts @Component({ /* ... */}) export class TodosComponent implements OnInit, OnDestroy { private loadTodosSubscription = new Subscription(); private createTodoSubscription = new Subscription(); // ... ngOnDestroy(): void { this.loadTodosSubscription.unsubscribe(); this.createTodoSubscription.unsubscribe(); } } Multiple code parts need to be touched for a single subscription.

Slide 135

Slide 135 text

Use one Subscription collecting all streams todo-checker.component.ts @Component({ /* ... */}) export class TodosComponent implements OnInit { private sink = new Subscription(); // ... addTodo(newTodo: Todo) { this.sink.add(this.todosService .create(newTodo) .pipe(switchMap(() => this.todosService.query())) .subscribe(todos => (this.todos = todos)); ); } }

Slide 136

Slide 136 text

Unsubscribe from an Observable todo-checker.component.ts @Component({ /* ... */}) export class TodosComponent implements OnInit { private sink = new Subscription(); // ... ngOnInit(): void { this.sink.add(...); } addTodo(newTodo: Todo) { this.sink.add(...); } } A single Subscription can collect multiple subscribtions.

Slide 137

Slide 137 text

Unsubscribe from an Observable todo-checker.component.ts @Component({ /* ... */}) export class TodosComponent implements OnInit, OnDestroy { private sink = new Subscription(); // ... ngOnDestroy(): void { this.sink.unsubscribe(); } } The code looks cleaner now. #cleanCodePolice

Slide 138

Slide 138 text

Alternative: async pipe

Slide 139

Slide 139 text

http://bit.do/ngde19-sf-tasks Hands on Clean up subscriptions

Slide 140

Slide 140 text

Navigate between Views

Slide 141

Slide 141 text

Navigation SPAs need a way to navigate between views: View 1 Link 1 Link 2 Link 3 View 2 Link 1 Link 2 Link 3 View 3 Link 1 Link 2 Link 3 But without refreshing the page!

Slide 142

Slide 142 text

Routing http://localhost/todos Maps URLs to components ‘/todos’ TodosComponent ‘/about’ AboutComponent http://localhost/about Todos About

Slide 143

Slide 143 text

Routing Can replace only parts of a view ‘/todos’ TodosComponent ‘/about’ AboutComponent http://localhost/todos http://localhost/about Navigation Navigation Todos About ‘/’ AppComponent NavigationComponent Child component

Slide 144

Slide 144 text

Use RouterModule app-routing.module.ts import { Routes, RouterModule } from '@angular/router'; const routes: Routes = []; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule { } Routing functionality is provided by RouterModule

Slide 145

Slide 145 text

Use RouterModule app-routing.module.ts import { Routes, RouterModule } from '@angular/router'; const routes: Routes = []; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule { } ☝ Routing is an own aspect of our app → Routing Module

Slide 146

Slide 146 text

Use RouterModule Import RouterModule import { Routes, RouterModule } from '@angular/router'; const routes: Routes = []; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule { } We import RouterModule and configure it with Routes

Slide 147

Slide 147 text

Routing Modules app-routing.module.ts import { Routes, RouterModule } from '@angular/router'; const routes: Routes = []; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule { } You can export whole modules

Slide 148

Slide 148 text

@NgModule({ // ... imports: [AppRoutingModule], // ... }) export class AppModule {} app.module.ts Routing Modules app-routing.module.ts import { Routes, RouterModule } from ... const routes: Routes = []; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule { } You can export whole modules We do this, so we don’t need to import RouterModule in AppModule

Slide 149

Slide 149 text

app-routing.module.ts Routes const routes: Routes = []; @NgModule({ imports: [RouterModule.forRoot(routes)] }) export class AppRoutingModule { } Route mapping goes here

Slide 150

Slide 150 text

app-routing.module.ts Routes const routes: Routes = [ { path: '', redirectTo: 'todos', pathMatch: 'full' }, { path: 'todos', component: TodosComponent }, { path: 'about', component: AboutComponent }, // ... ]; @NgModule({ imports: [RouterModule.forRoot(routes)] }) export class AppRoutingModule { } Default redirect when user enters empty path

Slide 151

Slide 151 text

Show Routed Component app.component.html Finally: Use router-outlet as placeholder for activated views

Slide 152

Slide 152 text

http://bit.do/ngde19-sf-tasks Hands on Set up Routing

Slide 153

Slide 153 text

Dynamic Routes routes const routes: Routes = [ { path: 'todos/all', component: TodosComponent }, { path: 'todos/active', component: TodosComponent }, { path: 'todos/complete, component: TodosComponent } ]; const routes: Routes = [ { path: 'todos/:query', component: TodosComponent } ]; ‘:query’ Will match all sub-paths! Access :query via ActivatedRoute service

Slide 154

Slide 154 text

ActivatedRoute todos.component.ts import { ActivatedRoute } from '@angular/router'; // ... constructor(private route: ActivatedRoute) {} this.route.paramMap .subscribe(paramMap => console.log(paramMap.get('query'))); // routes: [{ path: 'todos/:query', component: TodosComponent }] Will return ‘all’ when todos/all is active

Slide 155

Slide 155 text

Decentralized Route Configuration RouterModules can be loaded from other Modules: AppRoutingModule: .forRoot() Todos .forChild() About .forChild() ... .forChild()

Slide 156

Slide 156 text

Decentralized Route Configuration app-routing.module.ts const routes: Routes = [{ path: '', redirectTo: 'todos/all', pathMatch: 'full' }]; @NgModule({ imports: [RouterModule.forRoot(routes)] }) export class AppRoutingModule { } todos-routing.module.ts const routes: Routes = [{ path: 'todos/:query', component: TodosComponent }]; @NgModule({ imports: [RouterModule.forChild(routes)] }) export class TodosRoutingModule { }

Slide 157

Slide 157 text

Router Link To link to an Angular route use RouterLink: Link 1 navigation Relative to the current route Add CSS class “link-active” when route is active

Slide 158

Slide 158 text

Router Link To link to an Angular route use RouterLink: Link 1 navigation http://localhost/todos/all http://localhost/todos/active Link 1 click

Slide 159

Slide 159 text

http://bit.do/ngde19-sf-tasks Hands on Create Todo Navigation

Slide 160

Slide 160 text

Lazy Loading TodoModule is loaded at Startup. If you have many modules this is not desired! → Lazy Loading for the rescue

Slide 161

Slide 161 text

Lazy Loading Modules are bundled in different JavaScript chunks Chunks are loaded at runtime when needed

Slide 162

Slide 162 text

Routes for Lazy Loaded TodosModule app-routing.module.ts const routes: Routes = [{ path: '', pathMatch: 'full', redirectTo: 'todos' }, { path: 'todos', loadChildren: () => import('./todos/todos.module') .then(m => m.TodosModule) }]; app-routing.module.ts const routes: Routes = [{ path: '', pathMath: 'full', redirectTo: 'todos/all' }]; We load on demand → Remove imported TodosModule from AppModule!

Slide 163

Slide 163 text

Routes for Lazy Loaded TodosModule todos-routing.module.ts app-routing.module.ts const routes: Routes = [{ path: '', pathMatch: 'full', redirectTo: 'todos' }, { path: 'todos', loadChildren: () => import('./todos/todos.module') .then(m => m.TodosModule) }]; const routes: Routes = [{ path: '', redirectTo: 'all' }, { path: ':query', component: TodosComponent }];

Slide 164

Slide 164 text

http://bit.do/ngde19-sf-tasks Hands on Lazy Loading

Slide 165

Slide 165 text

Thank you @GregOnNet _der_markusende ~150 Slides & 20 Hands on lessons