BookMonkey 4 Diff

Files changed (7) hide show
  1. tmp/src/app/book-monkey/iteration-3/{http → rxjs}/app.module.ts +3 -1
  2. tmp/src/app/book-monkey/iteration-3/{http → rxjs}/home/home.component.html +3 -0
  3. tmp/src/app/book-monkey/iteration-3/{http → rxjs}/search/search.component.html +18 -0
  4. tmp/src/app/book-monkey/iteration-3/{http → rxjs}/search/search.component.ts +32 -0
  5. tmp/src/app/book-monkey/iteration-3/{http → rxjs}/shared/book-factory.ts +11 -0
  6. tmp/src/app/book-monkey/iteration-3/{http → rxjs}/shared/book-raw.ts +15 -0
  7. tmp/src/app/book-monkey/iteration-3/{http → rxjs}/shared/book-store.service.ts +37 -4
tmp/src/app/book-monkey/iteration-3/{http → rxjs}/app.module.ts RENAMED
@@ -8,6 +8,7 @@
8
  import { BookListComponent } from './book-list/book-list.component';
9
  import { BookListItemComponent } from './book-list-item/book-list-item.component';
10
  import { BookDetailsComponent } from './book-details/book-details.component';
   
11
   
12
  @NgModule({
13
  declarations: [
@@ -15,7 +16,8 @@
15
  HomeComponent,
16
  BookListComponent,
17
  BookListItemComponent,
18
- BookDetailsComponent
   
19
  ],
20
  imports: [
21
  CommonModule,
8
  import { BookListComponent } from './book-list/book-list.component';
9
  import { BookListItemComponent } from './book-list-item/book-list-item.component';
10
  import { BookDetailsComponent } from './book-details/book-details.component';
11
+ import { SearchComponent } from './search/search.component';
12
   
13
  @NgModule({
14
  declarations: [
16
  HomeComponent,
17
  BookListComponent,
18
  BookListItemComponent,
19
+ BookDetailsComponent,
20
+ SearchComponent
21
  ],
22
  imports: [
23
  CommonModule,
tmp/src/app/book-monkey/iteration-3/{http → rxjs}/home/home.component.html RENAMED
@@ -4,3 +4,6 @@
4
  Buchliste ansehen
5
  <i class="right arrow icon"></i>
6
  </a>
   
   
   
4
  Buchliste ansehen
5
  <i class="right arrow icon"></i>
6
  </a>
7
+  
8
+ <h2>Suche</h2>
9
+ <bm-search></bm-search>
tmp/src/app/book-monkey/iteration-3/{http → rxjs}/search/search.component.html RENAMED
@@ -0,0 +1,18 @@
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
1
+ <div class="ui search" [class.loading]="isLoading">
2
+ <div class="ui icon input">
3
+
4
+ <input type="text" class="prompt" #input
5
+ (keyup)="keyUp$.next(input.value)">
6
+ <i class="search icon"></i>
7
+ </div>
8
+  
9
+ <div class="results transition visible"
10
+ *ngIf="foundBooks.length">
11
+ <a class="result"
12
+ *ngFor="let book of foundBooks"
13
+ [routerLink]="['..', 'books', book.isbn]">
14
+ {{ book.title }}
15
+ <p class="description">{{ book.subtitle }}</p>
16
+ </a>
17
+ </div>
18
+ </div>
tmp/src/app/book-monkey/iteration-3/{http → rxjs}/search/search.component.ts RENAMED
@@ -0,0 +1,32 @@
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
1
+ import { Component, OnInit } from '@angular/core';
2
+ import { Subject } from 'rxjs';
3
+ import { debounceTime, distinctUntilChanged, tap, switchMap, filter } from 'rxjs/operators';
4
+  
5
+ import { Book } from '../shared/book';
6
+ import { BookStoreService } from '../shared/book-store.service';
7
+  
8
+ @Component({
9
+ selector: 'bm-search',
10
+ templateUrl: './search.component.html',
11
+ styleUrls: ['./search.component.css']
12
+ })
13
+ export class SearchComponent implements OnInit {
14
+  
15
+ keyUp$ = new Subject<string>();
16
+ isLoading = false;
17
+ foundBooks: Book[] = [];
18
+  
19
+ constructor(private bs: BookStoreService) { }
20
+  
21
+ ngOnInit(): void {
22
+ this.keyUp$.pipe(
23
+ filter(term => term.length >= 3),
24
+ debounceTime(500),
25
+ distinctUntilChanged(),
26
+ tap(() => this.isLoading = true),
27
+ switchMap(searchTerm => this.bs.getAllSearch(searchTerm)),
28
+ tap(() => this.isLoading = false)
29
+ )
30
+ .subscribe(books => this.foundBooks = books);
31
+ }
32
+ }
tmp/src/app/book-monkey/iteration-3/{http → rxjs}/shared/book-factory.ts RENAMED
@@ -0,0 +1,11 @@
   
   
   
   
   
   
   
   
   
   
   
1
+ import { Book } from './book';
2
+ import { BookRaw } from './book-raw';
3
+  
4
+ export class BookFactory {
5
+ static fromRaw(b: BookRaw): Book {
6
+ return {
7
+ ...b,
8
+ published: new Date(b.published)
9
+ };
10
+ }
11
+ }
tmp/src/app/book-monkey/iteration-3/{http → rxjs}/shared/book-raw.ts RENAMED
@@ -0,0 +1,15 @@
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
1
+ export interface BookRaw {
2
+ isbn: string;
3
+ title: string;
4
+ authors: string[];
5
+ published: string;
6
+ subtitle?: string;
7
+ rating?: number;
8
+ thumbnails?: ThumbnailRaw[];
9
+ description?: string;
10
+ }
11
+  
12
+ export interface ThumbnailRaw {
13
+ url: string;
14
+ title?: string;
15
+ }
tmp/src/app/book-monkey/iteration-3/{http → rxjs}/shared/book-store.service.ts RENAMED
@@ -1,8 +1,11 @@
1
  import { Injectable } from '@angular/core';
2
- import { HttpClient } from '@angular/common/http';
3
- import { Observable } from 'rxjs';
   
4
   
5
  import { Book } from './book';
   
   
6
   
7
  @Injectable({
8
  providedIn: 'root'
@@ -13,12 +16,23 @@
13
  constructor(private http: HttpClient) {}
14
   
15
  getAll(): Observable<Book[]> {
16
- return this.http.get<any[]>(`${this.api}/books`);
   
   
   
   
   
   
   
17
  }
18
   
19
  getSingle(isbn: string): Observable<Book> {
20
- return this.http.get<any>(
21
  `${this.api}/book/${isbn}`
   
   
   
   
22
  );
23
  }
24
   
@@ -26,6 +40,25 @@
26
  return this.http.delete(
27
  `${this.api}/book/${isbn}`,
28
  { responseType: 'text' }
   
   
29
  );
30
  }
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
31
  }
1
  import { Injectable } from '@angular/core';
2
+ import { HttpClient, HttpErrorResponse } from '@angular/common/http';
3
+ import { throwError, Observable } from 'rxjs';
4
+ import { retry, map, catchError } from 'rxjs/operators';
5
   
6
  import { Book } from './book';
7
+ import { BookRaw } from './book-raw';
8
+ import { BookFactory } from './book-factory';
9
   
10
  @Injectable({
11
  providedIn: 'root'
16
  constructor(private http: HttpClient) {}
17
   
18
  getAll(): Observable<Book[]> {
19
+ return this.http.get<BookRaw[]>(`${this.api}/books`)
20
+ .pipe(
21
+ retry(3),
22
+ map(booksRaw =>
23
+ booksRaw.map(b => BookFactory.fromRaw(b)),
24
+ ),
25
+ catchError(this.errorHandler)
26
+ );
27
  }
28
   
29
  getSingle(isbn: string): Observable<Book> {
30
+ return this.http.get<BookRaw>(
31
  `${this.api}/book/${isbn}`
32
+ ).pipe(
33
+ retry(3),
34
+ map(b => BookFactory.fromRaw(b)),
35
+ catchError(this.errorHandler)
36
  );
37
  }
38
   
40
  return this.http.delete(
41
  `${this.api}/book/${isbn}`,
42
  { responseType: 'text' }
43
+ ).pipe(
44
+ catchError(this.errorHandler)
45
  );
46
  }
47
+  
48
+ getAllSearch(searchTerm: string): Observable<Book[]> {
49
+ return this.http.get<BookRaw[]>(
50
+ `${this.api}/books/search/${searchTerm}`
51
+ ).pipe(
52
+ retry(3),
53
+ map(booksRaw =>
54
+ booksRaw.map(b => BookFactory.fromRaw(b)),
55
+ ),
56
+ catchError(this.errorHandler)
57
+ );
58
+ }
59
+  
60
+ private errorHandler(error: HttpErrorResponse): Observable<any> {
61
+ console.error('Fehler aufgetreten!');
62
+ return throwError(error);
63
+ }
64
  }