Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Angular Solid Foundations NG-DE19

Angular Solid Foundations NG-DE19

The slides to the Workshop "Angular Solid Foundations" given at Ng-DE.
- see https://ng-de.org/workshops/angular-solid-foundations/

Gregor Woiwode

August 29, 2019
Tweet

More Decks by Gregor Woiwode

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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
  4. Mission Apps that users ♥ to use Apps that developers

    ♥ to build Community where everyone feels welcome
  5. What is a Component? Toggle All Prepare Workshop Hold the

    Workshop ToDo App Representation & Interaction ⚡
  6. 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'; }
  7. 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'; } <body> <nde-root></nde-root> </body> Look, an Angular Component In the wild.
  8. 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'; }
  9. 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'; }
  10. 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'; }
  11. app.component.ts app.component.html Bind component-values <span> {{ title }} app is

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

    running! </span> /* imports */ @Component(/* … */) export class AppComponent { title = 'Your'; } Bind values of a component with double curly braces to the template.
  13. Beyond AppComponent child of child of child of child of

    … A component can have multiple children.
  14. todo-checker.component.ts todo-checker.component.html Property-Binding {{ todo.text }} <input type="checkbox" [checked]="todo.isDone" />

    @Component(/* … */) export class TodoCheckerComponent { todo = { text: 'Buy ', isDone: true }; }
  15. todo-checker.component.ts todo-checker.component.html Event-Binding {{ todo.text }} <input type="checkbox" [checked]="todo.isDone" (change)="emitToggle()"

    /> @Component(/* … */) export class TodoCheckerComponent { // todo = ... emitToggle() { alert( `${this.todo.text} clicked.` ); } When change is fired emitToggle gets executed.
  16. app.component.html Feed your component with some data AppComponent <nde-todo-checker [todo]="someTodo"

    ></nde-todo-checker> @Input Define your own property binding. TodoCheckerComponent
  17. 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 { // <nde-todo-checker [todo]=""> @Input() todo; // ... {{ todo.text }} <input type="checkbox" [checked]="todo.isDone" (change)="emitToggle()" />
  18. React to events of a child component AppComponent TodoComponent app.component.html

    <nde-todo [todo]="someTodo" (toggle)="doSomething()" ></nde-todo> @Output Create your own event binding.
  19. Emit custom events using @Output todo-checker.component.ts import { Output, EventEmitter

    } from '@angular/core'; @Component(/* … */) export class TodoCheckerComponent { // … // <nde-todo-checker (toggle)=""> @Output() toggle = new EventEmitter(); emitToggle() { this.toggle.emit(this.todo); } }
  20. app.component.ts React to events of a child component AppComponent TodoComponent

    app.component.html <nde-todo [todo]="someTodo" (toggle)="notify($event)" ></nde-todo> @Output notify(todo): void { alert(`${todo.text} clicked.`); } $event transports the todo.
  21. 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 <nde-todo-checker [todo]="todos[0]"> </nde-todo-checker> <nde-todo-checker [todo]="todos[1]"> </nde-todo-checker> Imagine you need to render a collection of data.
  22. todo-checker.component.html Render components with *ngFor <nde-todo-checker [todo]="todo" *ngFor="let todo of

    todos" ></nde-todo-checker> 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.
  23. todo-checker.component.html Render components with *ngFor <nde-todo-checker [todo]="todo" *ngFor="let todo of

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

    todos" ></nde-todo-checker> todo-checker.component.ts @Component({ /* … */ }) export class TodoCheckerComponent { todos = [ { text: 'Buy ', isDone: true }, { text: 'Plant ', isDone: false } ]; }
  25. 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
  26. todo-checker.component.ts Using an Interface import { Todo } from '../models';

    @Component(/* … */) export class TodoCheckerComponent { @Input() todo: Todo; @Output() toggle = new EventEmitter<Todo>(); // ... }
  27. Interact with template elements Template <input type="text" class="fancy-class" placeholder="Type something

    in" /> What if you need to - … read attribute values? - … change attribute values?
  28. Simple HTML-Element Interaction Template <input #myInput /> Define a template

    reference by using the #-sign + a freely chosen name.
  29. Simple HTML-Element Interaction Template <input #myInput (change)="doSomething(myInput)" /> Pass the

    template reference to your component by using your freely chosen name without #.
  30. Simple HTML-Element Interaction Component Template <input #myInput (change)="doSomething(myInput)" /> @Component(/*

    … */) export class MyComponent { doSomething( myInput: HTMLInputElement ) { // ... myInput.value = ''; } }
  31. Simple HTML-Element Interaction Component Template <input #myInput (change)="doSomething(myInput)" /> @Component(/*

    … */) export class MyComponent { doSomething( myInput: HTMLInputElement ) { } } Please don’t miss the type annotation giving you ‍♀ more safety while you are coding,.
  32. 2 Different Types of Modules TypeScript (or JavaScript) Module Abstraction

    level: files Angular Module (NgModule) Abstraction level: app aspects VS
  33. 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
  34. 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
  35. todos.module.ts Module Declaration import { NgModule } from '@angular/core'; @NgModule({

    declarations: [ TodosComponent, TodoCheckerComponent, // ... ] exports: [ TodosComponent ] }) export class TodosModule { } Use @NgModule directive
  36. 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
  37. 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 { }
  38. 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 { }
  39. 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)!
  40. 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
  41. 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!
  42. todos.service.ts Injectable Service Declaration import { Injectable } from '@angular/core';

    @Injectable({ providedIn: 'root' }) export class TodosService { } Define class to be injectable
  43. 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
  44. 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
  45. 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
  46. 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!
  47. 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!
  48. Register of HttpClientModule import { HttpClientModule } from '@angular/common/http'; //

    ... @NgModule({ imports: [ CommonModule, HttpClientModule ], //... }) export class TodosModule {}
  49. 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 */) {} }
  50. 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 */) {} }
  51. HttpClient API of HttpClient class HttpClient { get<T> (/* various

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

    overloads */): Observable<T> {} post<T> (/* various overloads */): Observable<T> {} put<T> (/* various overloads */): Observable<T> {} delete<T> (/* various overloads */): Observable<T> {} } The data structure supporting you handling asynchronous operations.
  53. Http GET API Demonstration httpClient.get<SomeData>('api.com/data') .subscribe(data => /* do something

    */); An Observable provides the subscribe-method. Once the request is complete you can process the loaded data.
  54. Observable The callbacks of subscribe class Observable<T> { subscribe( (value:

    T) => {}, (error: any) => {}, () => {}, ) // ... // see https://rxjs.dev/api/index/class/Observable for more details }
  55. API Demo Understanding subscribe HTTP subscribe( (value: T) => {},

    (error: any) => {}, () => { /* complete */ }, )
  56. API Demo Lifecycle of a successful request subscribe ⏱ next

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

    subscribe( (value: T) => {}, (error: any) => {}, () => {}, )
  58. Component Inject of HttpClient @Injectable({ providedIn: 'root' }) export class

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

    TodosService { constructor( private http: HttpClient ) {} query(): Observable<Todo[]> { return this.http.get<Todo[]>( 'api.com/data' ); } } Please note that an Observable is returned. You want to avoid subscribe in the service.
  60. todos.service.ts todos.component.ts Put everything together @Injectable({ providedIn: 'root' }) export

    class TodosService { constructor( private http: HttpClient ) {} query(): Observable<Todo[]> { return this.http.get<Todo[]>( '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
  61. todos.service.ts todos.component.ts Put everything together @Injectable({ providedIn: 'root' }) export

    class TodosService { constructor( private http: HttpClient ) {} query(): Observable<Todo[]> { return this.http.get<Todo[]>( '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.
  62. todos.service.ts todos.component.ts Put everything together @Injectable({ providedIn: 'root' }) export

    class TodosService { constructor( private http: HttpClient ) {} query(): Observable<Todo[]> { return this.http.get<Todo[]>( 'api.com/data' ); } } @Component({ /* ... */ }) export class TodosComponent implements OnInit { todos: Todo[] = []; // ... ngOnInit(): void { this.service.query().subscribe( todos => this.todos = todos ); } } Assign todos
  63. Http POST API Demonstration httpClient.post<SomeData>('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.
  64. Http Put API Demonstration httpClient.put<SomeData>('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.
  65. 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)) }
  66. 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)) }
  67. 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)) }
  68. One last thing about subscribe subscribe next complete An Observable

    represents a stream. ☝ We are lucky that Http completes the stream automatically.
  69. 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
  70. 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.
  71. 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
  72. 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.
  73. 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...
  74. 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.
  75. 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)); ); } }
  76. 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.
  77. 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
  78. 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!
  79. 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
  80. 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
  81. 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
  82. 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
  83. 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
  84. @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
  85. 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
  86. Show Routed Component <!-- any html before → <router-outlet></router-outlet> <!--

    any html after --> app.component.html Finally: Use router-outlet as placeholder for activated views
  87. 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
  88. 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
  89. Decentralized Route Configuration RouterModules can be loaded from other Modules:

    AppRoutingModule: .forRoot() Todos .forChild() About .forChild() ... .forChild()
  90. 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 { }
  91. Router Link To link to an Angular route use RouterLink:

    <a routerLink="../active” routerLinkActive="link-active"> Link 1 </a> navigation Relative to the current route Add CSS class “link-active” when route is active
  92. Router Link To link to an Angular route use RouterLink:

    <a routerLink="../active” routerLinkActive="link-active"> Link 1 </a> navigation http://localhost/todos/all http://localhost/todos/active Link 1 click
  93. Lazy Loading TodoModule is loaded at Startup. If you have

    many modules this is not desired! → Lazy Loading for the rescue
  94. 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!
  95. 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 }];