From 3fcf281ee81182497902fe956520a9dd873189c4 Mon Sep 17 00:00:00 2001
From: Alex Sumoski <alexsumoski@gmail.com>
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 @@ <h3>All Courses</h3>
 
         <mat-tab label="Beginners">
 
-          <mat-card *ngFor="let course of beginnerCourses" class="course-card mat-elevation-z10">
+          <mat-card *ngFor="let course of beginnerCourses$ | async" class="course-card mat-elevation-z10">
 
             <mat-card-header>
 
@@ -40,7 +40,7 @@ <h3>All Courses</h3>
 
         <mat-tab label="Advanced">
 
-          <mat-card *ngFor="let course of advancedCourses" class="course-card mat-elevation-z10">
+          <mat-card *ngFor="let course of advancedCourses$ | async" class="course-card mat-elevation-z10">
 
             <mat-card-header>
 
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<Course[]>;
+  advancedCourses$: Observable<Course[]>;
 
-  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<Course[]> {
+        return this.http.get<Course[]>("/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 <alexsumoski@gmail.com>
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<Course[]>("/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 <alexsumoski@gmail.com>
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<CourseDialogComponent>,
+        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 @@
+<mat-card *ngFor="let course of courses"
+          class="course-card mat-elevation-z10">
+
+    <mat-card-header>
+
+        <mat-card-title>{{course.description}}</mat-card-title>
+
+    </mat-card-header>
+
+    <img mat-card-image [src]="course.iconUrl">
+
+    <mat-card-content>
+        <p>{{course.longDescription}}</p>
+    </mat-card-content>
+
+    <mat-card-actions class="course-actions">
+
+        <button mat-button class="mat-raised-button mat-primary" [routerLink]="['/courses', course.id]">
+            VIEW COURSE
+        </button>
+
+        <button mat-button class="mat-raised-button mat-accent"
+                (click)="editCourse(course)">
+            EDIT
+        </button>
+
+    </mat-card-actions>
+
+</mat-card>
\ 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 @@ <h3>All Courses</h3>
 
         <mat-tab label="Beginners">
 
-          <mat-card *ngFor="let course of beginnerCourses$ | async" class="course-card mat-elevation-z10">
-
-            <mat-card-header>
-
-              <mat-card-title>{{course.description}}</mat-card-title>
-
-            </mat-card-header>
-
-            <img mat-card-image [src]="course.iconUrl">
-
-            <mat-card-content>
-              <p>{{course.longDescription}}</p>
-            </mat-card-content>
-
-            <mat-card-actions class="course-actions">
-
-              <button mat-button class="mat-raised-button mat-primary" [routerLink]="['/courses', course.id]">
-                VIEW COURSE
-              </button>
-
-              <button mat-button class="mat-raised-button mat-accent"
-                      (click)="editCourse(course)">
-                EDIT
-              </button>
-
-            </mat-card-actions>
-
-          </mat-card>
+          <courses-card-list [courses]="beginnerCourses$ | async"></courses-card-list>
 
         </mat-tab>
 
         <mat-tab label="Advanced">
 
-          <mat-card *ngFor="let course of advancedCourses$ | async" class="course-card mat-elevation-z10">
-
-            <mat-card-header>
-
-              <mat-card-title>{{course.description}}</mat-card-title>
-
-            </mat-card-header>
-
-            <img mat-card-image [src]="course.iconUrl">
-
-            <mat-card-content>
-              <p>{{course.longDescription}}</p>
-            </mat-card-content>
-
-            <mat-card-actions class="course-actions">
-
-              <button mat-button class="mat-raised-button mat-primary" [routerLink]="['/courses', course.id]">
-                VIEW COURSE
-              </button>
-
-              <button mat-button class="mat-raised-button mat-accent"
-                      (click)="editCourse(course)">
-                EDIT
-              </button>
-
-            </mat-card-actions>
-
-          </mat-card>
+          <courses-card-list [courses]="advancedCourses$ | async"></courses-card-list>
 
         </mat-tab>
 
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<Course>): Observable<any> {
+        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 <alexsumoski@gmail.com>
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 @@ <h3>All Courses</h3>
 
         <mat-tab label="Beginners">
 
-          <courses-card-list [courses]="beginnerCourses$ | async"></courses-card-list>
+          <courses-card-list (coursesChanged)="reloadCourses()" [courses]="beginnerCourses$ | async"></courses-card-list>
 
         </mat-tab>
 
         <mat-tab label="Advanced">
 
-          <courses-card-list [courses]="advancedCourses$ | async"></courses-card-list>
+          <courses-card-list (coursesChanged)="reloadCourses()" [courses]="advancedCourses$ | async"></courses-card-list>
 
         </mat-tab>
 
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 <alexsumoski@gmail.com>
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 @@
 
   </mat-toolbar>
 
+  <loading></loading>
+
   <router-outlet></router-outlet>
 
 </mat-sidenav-container>
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<CourseDialogComponent>,
         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<Course[]>;
   advancedCourses$: Observable<Course[]>;
 
-  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 @@
-
+<div class="spinner-container" *ngIf="loadingService.loading$ | async">
+    <mat-spinner></mat-spinner>
+</div>
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<boolean>(false);
+
+    loading$: Observable<boolean> = this.loadingSubject.asObservable();
+
+    showLoaderUntilCompleted<T>(obs$: Observable<T>): Observable<T> {
+        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