BookMonkey 3 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 +17 -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,17 @@
1
+ <div class="ui search" [class.loading]="isLoading">
2
+ <div class="ui icon input">
3
+ <input type="text" class="prompt"
4
+ (keyup)="keyUp$.next($event.target.value)">
5
+ <i class="search icon"></i>
6
+ </div>
7
+
8
+ <div class="results transition visible"
9
+ *ngIf="foundBooks.length">
10
+ <a class="result"
11
+ *ngFor="let book of foundBooks"
12
+ [routerLink]="['..', 'books', book.isbn]">
13
+ {{ book.title }}
14
+ <p class="description">{{ book.subtitle }}</p>
15
+ </a>
16
+ </div>
17
+ </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() {
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
}