Skip to content

Commit fc3c019

Browse files
committed
Upgrade to Angular 18
2 parents cca8cc4 + c35783b commit fc3c019

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+548
-461
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
This repository contains the code of the [Modern Angular With Signals Course](https://angular-university.io/course/angular-signals-course).
55

6-
This course repository is updated to Angular 17:
6+
This course repository is updated to Angular 18:
77

88
![Modern Angular With Signals](https://d3vigmphadbn9b.cloudfront.net/course-images/large-images/angular-signals-course.jpg)
99

server/db-data.ts

+72-1
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,78 @@ export const LESSONS : any = {
882882
'seqNo': 9,
883883
courseId: 17,
884884
videoId: '8m5RrAtqlyw'
885-
}
885+
},
886+
887+
90: {
888+
id: 90,
889+
'description': 'Setting Up Your Development Environment',
890+
'duration': '09:15',
891+
'seqNo': 1,
892+
courseId: 18
893+
},
894+
91: {
895+
id: 91,
896+
'description': 'Writing Your First Angular Signal',
897+
'duration': '07:41',
898+
'seqNo': 1,
899+
courseId: 18
900+
},
901+
92: {
902+
id: 92,
903+
'description': 'Why Angular Signals? Main Benefits',
904+
'duration': '04:55',
905+
'seqNo': 1,
906+
courseId: 18
907+
},
908+
93: {
909+
id: 93,
910+
'description': 'The Current State of Signal-Based Change Detection',
911+
'duration': '08:08',
912+
'seqNo': 1,
913+
courseId: 18
914+
},
915+
94: {
916+
id: 94,
917+
'description': 'The Angular signals update API and Read-Only Signals',
918+
'duration': '09:08',
919+
'seqNo': 1,
920+
courseId: 18
921+
},
922+
95: {
923+
id: 95,
924+
'description': 'Angular Signals and Immutability - Objects',
925+
'duration': '05:55',
926+
'seqNo': 1,
927+
courseId: 18
928+
},
929+
96: {
930+
id: 96,
931+
'description': 'Angular Signals and Immutability - Arrays',
932+
'duration': '05:59',
933+
'seqNo': 1,
934+
courseId: 18
935+
},
936+
97: {
937+
id: 97,
938+
'description': 'Angular Computed Signals - Everything You Need To Know',
939+
'duration': '08:07',
940+
'seqNo': 1,
941+
courseId: 18
942+
},
943+
98: {
944+
id: 98,
945+
'description': 'Angular Signal Effects - Everything You Need To Know',
946+
'duration': '06:36',
947+
'seqNo': 1,
948+
courseId: 18
949+
},
950+
99: {
951+
id: 99,
952+
'description': 'Angular Signal Effects - Setting The Injection Context',
953+
'duration': '09:30',
954+
'seqNo': 1,
955+
courseId: 18
956+
},
886957

887958
};
888959

server/get-courses.route.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ export function getAllCourses(req: Request, res: Response) {
1111
return;
1212
*/
1313

14-
14+
console.log(`Called GET /api/courses`);
1515

1616
setTimeout(() => {
1717

18-
res.status(200).json({courses:Object.values(COURSES)});
18+
console.log(`Returning GET /api/courses`);
1919

20-
}, 1500);
20+
res.status(200).json({courses:Object.values(COURSES)});
2121

22+
}, 1000);
2223

2324
}
2425

25-
2626
export function getCourseById(req: Request, res: Response) {
2727

2828
setTimeout(() => {
@@ -33,7 +33,6 @@ export function getCourseById(req: Request, res: Response) {
3333
const course = courses.find(course => course.id == courseId);
3434

3535
res.status(200).json(course);
36-
37-
}, 1500);
36+
})
3837

3938
}

server/search-lessons.route.ts

+22-7
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,40 @@
1-
21
import {Request, Response} from 'express';
32
import {LESSONS} from "./db-data";
43
import {setTimeout} from "timers";
54

65

7-
86
export function searchLessons(req: Request, res: Response) {
97

10-
const query = req.query["query"] as string;
8+
const query = req.query["query"] as string,
9+
courseId = req.query["courseId"] as string;
1110

1211
const allLessons: any[] = Object.values(LESSONS);
1312

14-
console.log(allLessons)
13+
if (!query && !courseId) {
14+
res.status(200).json({lessons: []});
15+
return;
16+
}
17+
18+
let filtered: any[] = allLessons;
19+
console.log(`Filtering total lessons ${filtered?.length}`, allLessons)
20+
21+
if (courseId) {
22+
console.log(`Filtering by courseId ${parseInt(courseId)}`)
23+
filtered = filtered.filter(lesson => lesson.courseId == parseInt(courseId));
24+
console.log(`Filtered ${filtered?.length} results`)
25+
}
1526

16-
const filtered = allLessons.filter(
17-
lesson => lesson?.description?.trim()?.toLowerCase()?.search(query?.toLowerCase()) >= 0);
27+
if (query) {
28+
console.log(`Filtering by query ${query}`)
29+
filtered = allLessons.filter(
30+
lesson => lesson?.description?.trim()?.toLowerCase()?.search(query?.toLowerCase()) >= 0);
31+
console.log(`Filtered ${filtered?.length} results`)
32+
}
1833

1934
const lessons = filtered.slice(0, 10);
2035

2136
setTimeout(() => {
2237
res.status(200).json({lessons});
23-
},1000);
38+
}, 1000);
2439

2540
}

src/app/app.component.html

+9-10
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,17 @@
1414
<span>Lessons</span>
1515
</a>
1616

17-
@if (!isLoggedIn()) {
17+
@if(isLoggedIn()) {
18+
<a mat-list-item (click)="onLogout()">
19+
<mat-icon>exit_to_app</mat-icon>
20+
<span>Logout</span>
21+
</a>
22+
}
23+
@else {
1824
<a mat-list-item routerLink="login">
1925
<mat-icon>account_circle</mat-icon>
2026
<span>Login</span>
2127
</a>
22-
} @else {
23-
<a mat-list-item (click)="logout()">
24-
<mat-icon>exit_to_app</mat-icon>
25-
<span>Logout</span>
26-
</a>
2728
}
2829

2930
</mat-nav-list>
@@ -42,13 +43,11 @@
4243

4344
</div>
4445

45-
4646
</mat-toolbar>
4747

48+
<messages />
4849

49-
<loading/>
50-
51-
<messages/>
50+
<loading />
5251

5352
<router-outlet/>
5453

src/app/app.component.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ import {LoadingIndicatorComponent} from "./loading/loading.component";
99
import {MessagesComponent} from "./messages/messages.component";
1010
import {AuthService} from "./services/auth.service";
1111

12+
1213
@Component({
1314
selector: 'app-root',
1415
standalone: true,
15-
imports: [RouterOutlet, MatSidenavContainer, MatSidenav, MatNavList, MatListItem, MatIcon, RouterLink, MatToolbar, MatIconButton, LoadingIndicatorComponent, MessagesComponent],
16+
imports: [
17+
RouterOutlet, MatSidenavContainer, MatSidenav, MatNavList, MatListItem, MatIcon, RouterLink, MatToolbar,
18+
MatIconButton, LoadingIndicatorComponent, MessagesComponent],
1619
templateUrl: './app.component.html',
1720
styleUrl: './app.component.scss'
1821
})
@@ -22,8 +25,7 @@ export class AppComponent {
2225

2326
isLoggedIn = this.authService.isLoggedIn;
2427

25-
logout() {
28+
onLogout() {
2629
this.authService.logout();
2730
}
28-
2931
}

src/app/app.config.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import {provideRouter} from '@angular/router';
44
import {routes} from './app.routes';
55
import {provideAnimationsAsync} from '@angular/platform-browser/animations/async';
66
import { provideHttpClient, withFetch, withInterceptors } from "@angular/common/http";
7-
import {loadingInterceptor} from "./loading/loading.interceptor";
7+
import {loadingInterceptor} from "./services/loading.interceptor";
88

99
export const appConfig: ApplicationConfig = {
1010
providers: [
1111
provideRouter(routes),
1212
provideAnimationsAsync(),
1313
provideHttpClient(
1414
withFetch(),
15-
withInterceptors([loadingInterceptor])
15+
withInterceptors([
16+
loadingInterceptor
17+
])
1618
)
1719
]
1820
};

src/app/app.routes.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {Routes} from '@angular/router';
22
import {HomeComponent} from "./home/home.component";
33
import {LoginComponent} from "./login/login.component";
4-
import {isUserAuthenticated} from "./guards/auth.guard";
54
import {LessonsComponent} from "./lessons/lessons.component";
5+
import {isUserAuthenticated} from "./guards/auth.guard";
66
import {CourseComponent} from "./course/course.component";
77
import {courseResolver} from "./course/course.resolver";
88
import {courseLessonsResolver} from "./course/course-lessons.resolver";
@@ -14,7 +14,7 @@ export const routes: Routes = [
1414
canActivate: [isUserAuthenticated]
1515
},
1616
{
17-
path: "courses/:courseId",
17+
'path': 'courses/:courseId',
1818
component: CourseComponent,
1919
canActivate: [isUserAuthenticated],
2020
resolve: {

src/app/course-category-combobox/course-category-combobox.component.html

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11

22
<div class="form-control">
33

4-
<label>{{ label() }}:</label>
4+
<label>{{label()}}</label>
55

6-
<select [value]="value()" (change)="onCategoryChanged(category.value)" #category>
6+
<select [value]="value()" #category
7+
(change)="onCategoryChanged(category.value)">
78
<option value="BEGINNER">Beginners</option>
89
<option value="INTERMEDIATE">Intermediate</option>
910
<option value="ADVANCED">Advanced</option>

src/app/course-category-combobox/course-category-combobox.component.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ export class CourseCategoryComboboxComponent {
1717
onCategoryChanged(category: string) {
1818
this.value.set(category as CourseCategory);
1919
}
20+
2021
}

src/app/course/course-lessons.resolver.ts

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
import {LessonsService} from "../services/lessons.service";
1+
import {ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot} from "@angular/router";
2+
import {Lesson} from "../models/lesson.model";
23
import {inject} from "@angular/core";
3-
import {ActivatedRouteSnapshot, RouterStateSnapshot} from "@angular/router";
4+
import {LessonsService} from "../services/lessons.service";
45

56

6-
export const courseLessonsResolver =
7-
async (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
8-
const courseId = route.paramMap.get('courseId');
9-
if (!courseId) {
10-
return [];
7+
export const courseLessonsResolver: ResolveFn<Lesson[]> =
8+
async (route: ActivatedRouteSnapshot,
9+
state: RouterStateSnapshot) => {
10+
const courseId = route.paramMap.get("courseId");
11+
if (!courseId) {
12+
return [];
13+
}
14+
const lessonsService = inject(LessonsService);
15+
return lessonsService.loadLessons({courseId});
1116
}
12-
const lessonsService = inject(LessonsService);
13-
return lessonsService.loadLessons({courseId})
14-
}

src/app/course/course.component.html

+3-15
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,20 @@
1-
21
<div class="course-container">
3-
42
<div class="course-header">
5-
63
<h1>{{ course()?.title }}</h1>
7-
84
<img class="course-image" [src]="course()?.iconUrl">
9-
105
</div>
11-
126
<div class="table-container">
13-
147
<table class="results-table">
15-
16-
@for (lesson of lessons(); track lesson.id) {
8+
@for(lesson of lessons(); track lesson.id) {
179
<tr>
1810
<td>
19-
{{ lesson.description }}
11+
{{lesson.description}}
2012
</td>
2113
<td>
22-
{{ lesson.duration }}
14+
{{lesson.duration}}
2315
</td>
2416
</tr>
2517
}
26-
2718
</table>
28-
2919
</div>
30-
3120
</div>
32-

src/app/course/course.component.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {Component, inject, OnInit, signal} from '@angular/core';
2-
import {ActivatedRoute, ActivatedRouteSnapshot} from "@angular/router";
32
import {Course} from "../models/course.model";
43
import {Lesson} from "../models/lesson.model";
4+
import {ActivatedRoute} from "@angular/router";
55

66
@Component({
77
selector: 'course',
@@ -22,5 +22,4 @@ export class CourseComponent implements OnInit {
2222
this.course.set(this.route.snapshot.data["course"]);
2323
this.lessons.set(this.route.snapshot.data["lessons"]);
2424
}
25-
2625
}

src/app/course/course.resolver.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import {ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot} from "@angular/router";
2+
import {Course} from "../models/course.model";
23
import {CoursesService} from "../services/courses.service";
34
import {inject} from "@angular/core";
4-
import {Course} from "../models/course.model";
55

66

77
export const courseResolver: ResolveFn<Course | null> =
8-
async (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
9-
const courseId = route.paramMap.get('courseId');
10-
if (!courseId) {
11-
return null;
12-
}
13-
const coursesService = inject(CoursesService);
14-
return coursesService.loadCourseById(courseId);
8+
async (route: ActivatedRouteSnapshot,
9+
state: RouterStateSnapshot) => {
10+
const courseId = route.paramMap.get("courseId");
11+
if (!courseId) {
12+
return null;
13+
}
14+
const coursesService = inject(CoursesService);
15+
return coursesService.getCourseById(courseId);
1516
}

0 commit comments

Comments
 (0)