From 3fcf281ee81182497902fe956520a9dd873189c4 Mon Sep 17 00:00:00 2001 From: Alex Sumoski Date: Tue, 23 Aug 2022 23:24:34 -0400 Subject: [PATCH 1/5] observables, async pipe --- package-lock.json | 3 ++ src/app/home/home.component.html | 4 +-- src/app/home/home.component.ts | 44 ++++++++++++++--------------- src/app/services/courses.service.ts | 21 ++++++++++++++ 4 files changed, 47 insertions(+), 25 deletions(-) create mode 100644 src/app/services/courses.service.ts diff --git a/package-lock.json b/package-lock.json index 24615e43..336e5e16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,9 @@ "ts-node": "~3.2.0", "tslint": "~6.1.0", "typescript": "4.7.3" + }, + "engines": { + "node": "16" } }, "node_modules/@ampproject/remapping": { diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index c84039e4..793894f8 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -7,7 +7,7 @@

All Courses

- + @@ -40,7 +40,7 @@

All Courses

- + diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index f66828d7..e391df21 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -1,10 +1,11 @@ import {Component, OnInit} from '@angular/core'; import {Course, sortCoursesBySeqNo} from '../model/course'; -import {interval, noop, Observable, of, throwError, timer} from 'rxjs'; -import {catchError, delay, delayWhen, filter, finalize, map, retryWhen, shareReplay, tap} from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; import {HttpClient} from '@angular/common/http'; import {MatDialog, MatDialogConfig} from '@angular/material/dialog'; import {CourseDialogComponent} from '../course-dialog/course-dialog.component'; +import { CoursesService } from '../services/courses.service'; @Component({ @@ -14,35 +15,32 @@ import {CourseDialogComponent} from '../course-dialog/course-dialog.component'; }) export class HomeComponent implements OnInit { - beginnerCourses: Course[]; + beginnerCourses$: Observable; + advancedCourses$: Observable; - advancedCourses: Course[]; - - - constructor(private http: HttpClient, private dialog: MatDialog) { - - } + constructor(private http: HttpClient, private dialog: MatDialog, private coursesService: CoursesService) {} ngOnInit() { - - this.http.get('/api/courses') - .subscribe( - res => { - - const courses: Course[] = res["payload"].sort(sortCoursesBySeqNo); - - this.beginnerCourses = courses.filter(course => course.category == "BEGINNER"); - - this.advancedCourses = courses.filter(course => course.category == "ADVANCED"); - - }); - + const courses$ = this.coursesService.loadAllCourses() + .pipe( + map(courses => courses.sort(sortCoursesBySeqNo)) + ) + + this.beginnerCourses$ = courses$ + .pipe( + map(courses => courses.filter(course => course.category === "BEGINNER")) + ) + + this.advancedCourses$ = courses$ + .pipe( + map(courses => courses.filter(course => course.category === "ADVANCED")) + ) } editCourse(course: Course) { const dialogConfig = new MatDialogConfig(); - + dialogConfig.disableClose = true; dialogConfig.autoFocus = true; dialogConfig.width = "400px"; diff --git a/src/app/services/courses.service.ts b/src/app/services/courses.service.ts new file mode 100644 index 00000000..8674a2f1 --- /dev/null +++ b/src/app/services/courses.service.ts @@ -0,0 +1,21 @@ +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { Observable } from "rxjs"; +import { map, tap } from "rxjs/operators"; +import { Course } from "../model/course"; + +@Injectable({ + providedIn: 'root' +}) + +export class CoursesService { + constructor(private http: HttpClient) {} + + loadAllCourses(): Observable { + return this.http.get("/api/courses") + .pipe( + map(res => res["payload"]), + tap(res => console.log(res["payload"])) + ) + } +} \ No newline at end of file From e565884a8b8bdbeec4b60f02446426eb993c9bdc Mon Sep 17 00:00:00 2001 From: Alex Sumoski Date: Wed, 24 Aug 2022 23:05:21 -0400 Subject: [PATCH 2/5] no duplicate subscribes --- src/app/services/courses.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/services/courses.service.ts b/src/app/services/courses.service.ts index 8674a2f1..5b20f645 100644 --- a/src/app/services/courses.service.ts +++ b/src/app/services/courses.service.ts @@ -1,7 +1,7 @@ import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable } from "rxjs"; -import { map, tap } from "rxjs/operators"; +import { map, shareReplay, tap } from "rxjs/operators"; import { Course } from "../model/course"; @Injectable({ @@ -15,7 +15,7 @@ export class CoursesService { return this.http.get("/api/courses") .pipe( map(res => res["payload"]), - tap(res => console.log(res["payload"])) + shareReplay() ) } } \ No newline at end of file From a322111988f10fcddcd7bfb34ad1e79e33b2add6 Mon Sep 17 00:00:00 2001 From: Alex Sumoski Date: Sat, 27 Aug 2022 22:38:35 -0400 Subject: [PATCH 3/5] card list component --- src/app/app.module.ts | 4 +- .../course-dialog/course-dialog.component.ts | 9 +++ .../courses-card-list.component.css | 0 .../courses-card-list.component.html | 29 ++++++++++ .../courses-card-list.component.ts | 52 +++++++++++++++++ src/app/home/home.component.html | 58 +------------------ src/app/services/courses.service.ts | 7 +++ 7 files changed, 102 insertions(+), 57 deletions(-) create mode 100644 src/app/courses-card-list/courses-card-list.component.css create mode 100644 src/app/courses-card-list/courses-card-list.component.html create mode 100644 src/app/courses-card-list/courses-card-list.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index badc903f..a38723e5 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -33,6 +33,7 @@ import {SafeUrlPipe} from './common/safe-url.pipe'; import {MessagesComponent} from './messages/messages.component'; import {SearchLessonsComponent} from './search-lessons/search-lessons.component'; import { LoadingComponent } from './loading/loading.component'; +import { CoursesCardListComponent } from './courses-card-list/courses-card-list.component'; @NgModule({ declarations: [ @@ -46,7 +47,8 @@ import { LoadingComponent } from './loading/loading.component'; SafeUrlPipe, MessagesComponent, SearchLessonsComponent, - LoadingComponent + LoadingComponent, + CoursesCardListComponent ], imports: [ diff --git a/src/app/course-dialog/course-dialog.component.ts b/src/app/course-dialog/course-dialog.component.ts index 6bcaa85d..10a11417 100644 --- a/src/app/course-dialog/course-dialog.component.ts +++ b/src/app/course-dialog/course-dialog.component.ts @@ -5,6 +5,7 @@ import {FormBuilder, Validators, FormGroup} from "@angular/forms"; import * as moment from 'moment'; import {catchError} from 'rxjs/operators'; import {throwError} from 'rxjs'; +import { CoursesService } from '../services/courses.service'; @Component({ selector: 'course-dialog', @@ -20,6 +21,7 @@ export class CourseDialogComponent implements AfterViewInit { constructor( private fb: FormBuilder, private dialogRef: MatDialogRef, + private courseService: CoursesService, @Inject(MAT_DIALOG_DATA) course:Course) { this.course = course; @@ -41,6 +43,13 @@ export class CourseDialogComponent implements AfterViewInit { const changes = this.form.value; + this.courseService.saveCourse(this.course.id, changes) + .subscribe( + (val) => { + this.dialogRef.close(val); + } + ); + } close() { diff --git a/src/app/courses-card-list/courses-card-list.component.css b/src/app/courses-card-list/courses-card-list.component.css new file mode 100644 index 00000000..e69de29b diff --git a/src/app/courses-card-list/courses-card-list.component.html b/src/app/courses-card-list/courses-card-list.component.html new file mode 100644 index 00000000..7e910e0e --- /dev/null +++ b/src/app/courses-card-list/courses-card-list.component.html @@ -0,0 +1,29 @@ + + + + + {{course.description}} + + + + + + +

{{course.longDescription}}

+
+ + + + + + + + + +
\ No newline at end of file diff --git a/src/app/courses-card-list/courses-card-list.component.ts b/src/app/courses-card-list/courses-card-list.component.ts new file mode 100644 index 00000000..5fbd4219 --- /dev/null +++ b/src/app/courses-card-list/courses-card-list.component.ts @@ -0,0 +1,52 @@ +import {ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {Course} from '../model/course'; +import {MatDialog, MatDialogConfig} from '@angular/material/dialog'; +import {CourseDialogComponent} from '../course-dialog/course-dialog.component'; +import {filter, tap} from 'rxjs/operators'; + +@Component({ + selector: 'courses-card-list', + templateUrl: './courses-card-list.component.html', + styleUrls: ['./courses-card-list.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class CoursesCardListComponent implements OnInit { + + @Input() + courses: Course[] = []; + + @Output() + private coursesChanged = new EventEmitter(); + + constructor(private dialog: MatDialog) { + + } + + ngOnInit() { + + } + + editCourse(course: Course) { + + const dialogConfig = new MatDialogConfig(); + + dialogConfig.disableClose = true; + dialogConfig.autoFocus = true; + dialogConfig.width = "400px"; + + dialogConfig.data = course; + + const dialogRef = this.dialog.open(CourseDialogComponent, dialogConfig); + + dialogRef.afterClosed() + .pipe( + filter(val => !!val), + tap(() => this.coursesChanged.emit()) + + ) + .subscribe(); + + + } + +} \ No newline at end of file diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 793894f8..1ea3da6d 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -7,67 +7,13 @@

All Courses

- - - - - {{course.description}} - - - - - - -

{{course.longDescription}}

-
- - - - - - - - - -
+
- - - - - {{course.description}} - - - - - - -

{{course.longDescription}}

-
- - - - - - - - - -
+
diff --git a/src/app/services/courses.service.ts b/src/app/services/courses.service.ts index 5b20f645..dfc00d10 100644 --- a/src/app/services/courses.service.ts +++ b/src/app/services/courses.service.ts @@ -18,4 +18,11 @@ export class CoursesService { shareReplay() ) } + + saveCourse(couresId: string, changes: Partial): Observable { + return this.http.put(`/api/courses/${couresId}`, changes) + .pipe( + shareReplay() + ) + } } \ No newline at end of file From 56ed85025a4866d88ed028499f59c5a0de066fef Mon Sep 17 00:00:00 2001 From: Alex Sumoski Date: Sat, 27 Aug 2022 22:50:11 -0400 Subject: [PATCH 4/5] reload courses on changes --- src/app/courses-card-list/courses-card-list.component.css | 8 ++++++++ src/app/home/home.component.html | 4 ++-- src/app/home/home.component.ts | 4 ++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/app/courses-card-list/courses-card-list.component.css b/src/app/courses-card-list/courses-card-list.component.css index e69de29b..61b0a93c 100644 --- a/src/app/courses-card-list/courses-card-list.component.css +++ b/src/app/courses-card-list/courses-card-list.component.css @@ -0,0 +1,8 @@ + +.course-card { + margin: 20px 10px; +} + +.course-actions { + text-align: center; +} \ No newline at end of file diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 1ea3da6d..95c1f992 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -7,13 +7,13 @@

All Courses

- + - + diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index e391df21..e34d447d 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -21,6 +21,10 @@ export class HomeComponent implements OnInit { constructor(private http: HttpClient, private dialog: MatDialog, private coursesService: CoursesService) {} ngOnInit() { + this.reloadCourses(); + } + + reloadCourses() { const courses$ = this.coursesService.loadAllCourses() .pipe( map(courses => courses.sort(sortCoursesBySeqNo)) From c60925bf7039292896b125ec7b7364d70792b6b5 Mon Sep 17 00:00:00 2001 From: Alex Sumoski Date: Tue, 30 Aug 2022 23:57:01 -0400 Subject: [PATCH 5/5] loading service finish --- server/get-courses.route.ts | 2 +- src/app/app.component.html | 2 ++ src/app/app.component.ts | 4 ++- src/app/app.module.ts | 1 - .../course-dialog/course-dialog.component.ts | 6 ++-- src/app/home/home.component.ts | 11 ++++++-- src/app/loading/loading.component.html | 4 ++- src/app/loading/loading.component.ts | 3 +- src/app/services/loading.service.ts | 28 +++++++++++++++++++ 9 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 src/app/services/loading.service.ts diff --git a/server/get-courses.route.ts b/server/get-courses.route.ts index 2e66ca1a..1ff40632 100644 --- a/server/get-courses.route.ts +++ b/server/get-courses.route.ts @@ -17,7 +17,7 @@ export function getAllCourses(req: Request, res: Response) { res.status(200).json({payload:Object.values(COURSES)}); - }, 200); + }, 2000); } diff --git a/src/app/app.component.html b/src/app/app.component.html index a7482dda..5f04bc25 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -51,6 +51,8 @@ + + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c94ed83c..21196d80 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,11 +1,13 @@ import {Component, OnInit} from '@angular/core'; +import { LoadingService } from './services/loading.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', - styleUrls: ['./app.component.css'] + styleUrls: ['./app.component.css'], + providers: [LoadingService] }) export class AppComponent implements OnInit { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index a38723e5..5a2829d0 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -49,7 +49,6 @@ import { CoursesCardListComponent } from './courses-card-list/courses-card-list. SearchLessonsComponent, LoadingComponent, CoursesCardListComponent - ], imports: [ BrowserModule, diff --git a/src/app/course-dialog/course-dialog.component.ts b/src/app/course-dialog/course-dialog.component.ts index 10a11417..c8160fb4 100644 --- a/src/app/course-dialog/course-dialog.component.ts +++ b/src/app/course-dialog/course-dialog.component.ts @@ -1,11 +1,10 @@ -import {AfterViewInit, Component, ElementRef, Inject, OnInit, ViewChild, ViewEncapsulation} from '@angular/core'; +import {AfterViewInit, Component, Inject} from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; import {Course} from "../model/course"; import {FormBuilder, Validators, FormGroup} from "@angular/forms"; import * as moment from 'moment'; -import {catchError} from 'rxjs/operators'; -import {throwError} from 'rxjs'; import { CoursesService } from '../services/courses.service'; +import { LoadingService } from '../services/loading.service'; @Component({ selector: 'course-dialog', @@ -22,6 +21,7 @@ export class CourseDialogComponent implements AfterViewInit { private fb: FormBuilder, private dialogRef: MatDialogRef, private courseService: CoursesService, + private loadingService: LoadingService, @Inject(MAT_DIALOG_DATA) course:Course) { this.course = course; diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index e34d447d..ce260578 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -1,11 +1,12 @@ import {Component, OnInit} from '@angular/core'; import {Course, sortCoursesBySeqNo} from '../model/course'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { finalize, map } from 'rxjs/operators'; import {HttpClient} from '@angular/common/http'; import {MatDialog, MatDialogConfig} from '@angular/material/dialog'; import {CourseDialogComponent} from '../course-dialog/course-dialog.component'; import { CoursesService } from '../services/courses.service'; +import { LoadingService } from '../services/loading.service'; @Component({ @@ -18,16 +19,20 @@ export class HomeComponent implements OnInit { beginnerCourses$: Observable; advancedCourses$: Observable; - constructor(private http: HttpClient, private dialog: MatDialog, private coursesService: CoursesService) {} + constructor(private http: HttpClient, private dialog: MatDialog, private coursesService: CoursesService, private loadingService: LoadingService) {} ngOnInit() { this.reloadCourses(); } reloadCourses() { + + this.loadingService.loadingOn(); + const courses$ = this.coursesService.loadAllCourses() .pipe( - map(courses => courses.sort(sortCoursesBySeqNo)) + map(courses => courses.sort(sortCoursesBySeqNo)), + finalize(() => this.loadingService.loadingOff()) ) this.beginnerCourses$ = courses$ diff --git a/src/app/loading/loading.component.html b/src/app/loading/loading.component.html index 8b137891..c9c0845b 100644 --- a/src/app/loading/loading.component.html +++ b/src/app/loading/loading.component.html @@ -1 +1,3 @@ - +
+ +
diff --git a/src/app/loading/loading.component.ts b/src/app/loading/loading.component.ts index 5ee65165..042de434 100644 --- a/src/app/loading/loading.component.ts +++ b/src/app/loading/loading.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import {Observable} from 'rxjs'; +import { LoadingService } from '../services/loading.service'; @Component({ selector: 'loading', @@ -9,7 +10,7 @@ import {Observable} from 'rxjs'; export class LoadingComponent implements OnInit { - constructor() { + constructor(public loadingService: LoadingService) { } diff --git a/src/app/services/loading.service.ts b/src/app/services/loading.service.ts new file mode 100644 index 00000000..0b90ff8b --- /dev/null +++ b/src/app/services/loading.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from "@angular/core"; +import {BehaviorSubject, Observable, of} from 'rxjs'; +import {concatMap, finalize, tap} from 'rxjs/operators'; + +@Injectable() +export class LoadingService { + private loadingSubject = new BehaviorSubject(false); + + loading$: Observable = this.loadingSubject.asObservable(); + + showLoaderUntilCompleted(obs$: Observable): Observable { + return of(null) + .pipe( + tap(() => this.loadingOn()), + concatMap(() => obs$), + finalize(() => this.loadingOff()) + ); + } + + loadingOn() { + this.loadingSubject.next(true); + } + + loadingOff() { + this.loadingSubject.next(false); + + } +} \ No newline at end of file