Differenzansicht 08-rxjs
im Vergleich zu 07-http

← Zurück zur Ãœbersicht | Demo | Quelltext auf GitHub
src/app/app.module.ts CHANGED
@@ -6,11 +6,13 @@ import { AppRoutingModule } from './app-routing.module';
6
  import { AppComponent } from './app.component';
7
  import { BooksModule } from './books/books.module';
8
  import { HomeComponent } from './home/home.component';
 
9
 
10
  @NgModule({
11
  declarations: [
12
  AppComponent,
13
  HomeComponent,
 
14
  ],
15
  imports: [
16
  BrowserModule,
6
  import { AppComponent } from './app.component';
7
  import { BooksModule } from './books/books.module';
8
  import { HomeComponent } from './home/home.component';
9
+ import { SearchComponent } from './search/search.component';
10
 
11
  @NgModule({
12
  declarations: [
13
  AppComponent,
14
  HomeComponent,
15
+ SearchComponent,
16
  ],
17
  imports: [
18
  BrowserModule,
src/app/books/book-details/book-details.component.html CHANGED
@@ -1,4 +1,4 @@
1
- <div class="details" *ngIf="book">
2
  <h1>{{ book.title }}</h1>
3
  <p role="doc-subtitle" *ngIf="book.subtitle">{{ book.subtitle }}</p>
4
  <div class="header">
1
+ <div class="details" *ngIf="book$ | async as book">
2
  <h1>{{ book.title }}</h1>
3
  <p role="doc-subtitle" *ngIf="book.subtitle">{{ book.subtitle }}</p>
4
  <div class="header">
src/app/books/book-details/book-details.component.ts CHANGED
@@ -1,5 +1,6 @@
1
  import { Component } from '@angular/core';
2
  import { ActivatedRoute, Router } from '@angular/router';
 
3
 
4
  import { BookStoreService } from '../../shared/book-store.service';
5
  import { Book } from '../../shared/book';
@@ -11,16 +12,15 @@ import { Book } from '../../shared/book';
11
  styleUrl: './book-details.component.css'
12
  })
13
  export class BookDetailsComponent {
14
- book?: Book;
 
15
  constructor(
16
  private service: BookStoreService,
17
  private route: ActivatedRoute,
18
  private router: Router
19
  ) {
20
  const isbn = this.route.snapshot.paramMap.get('isbn')!;
21
- this.service.getSingle(isbn).subscribe(book => {
22
- this.book = book;
23
- });
24
  }
25
 
26
  removeBook(isbn: string) {
1
  import { Component } from '@angular/core';
2
  import { ActivatedRoute, Router } from '@angular/router';
3
+ import { Observable } from 'rxjs';
4
 
5
  import { BookStoreService } from '../../shared/book-store.service';
6
  import { Book } from '../../shared/book';
12
  styleUrl: './book-details.component.css'
13
  })
14
  export class BookDetailsComponent {
15
+ book$: Observable<Book>;
16
+
17
  constructor(
18
  private service: BookStoreService,
19
  private route: ActivatedRoute,
20
  private router: Router
21
  ) {
22
  const isbn = this.route.snapshot.paramMap.get('isbn')!;
23
+ this.book$ = this.service.getSingle(isbn);
 
 
24
  }
25
 
26
  removeBook(isbn: string) {
src/app/books/book-list/book-list.component.html CHANGED
@@ -1,5 +1,5 @@
1
  <h1>Books</h1>
2
- <ul class="book-list">
3
  <li *ngFor="let book of books">
4
  <bm-book-list-item [book]="book"></bm-book-list-item>
5
  </li>
1
  <h1>Books</h1>
2
+ <ul class="book-list" *ngIf="books$ | async as books">
3
  <li *ngFor="let book of books">
4
  <bm-book-list-item [book]="book"></bm-book-list-item>
5
  </li>
src/app/books/book-list/book-list.component.ts CHANGED
@@ -1,7 +1,8 @@
1
  import { Component } from '@angular/core';
 
2
 
3
- import { BookStoreService } from '../../shared/book-store.service';
4
  import { Book } from '../../shared/book';
 
5
 
6
  @Component({
7
  selector: 'bm-book-list',
@@ -10,11 +11,9 @@ import { Book } from '../../shared/book';
10
  styleUrl: './book-list.component.css'
11
  })
12
  export class BookListComponent {
13
- books: Book[] = [];
14
 
15
  constructor(private service: BookStoreService) {
16
- this.service.getAll().subscribe(books => {
17
- this.books = books;
18
- });
19
  }
20
  }
1
  import { Component } from '@angular/core';
2
+ import { Observable } from 'rxjs';
3
 
 
4
  import { Book } from '../../shared/book';
5
+ import { BookStoreService } from '../../shared/book-store.service';
6
 
7
  @Component({
8
  selector: 'bm-book-list',
11
  styleUrl: './book-list.component.css'
12
  })
13
  export class BookListComponent {
14
+ books$: Observable<Book[]>;
15
 
16
  constructor(private service: BookStoreService) {
17
+ this.books$ = this.service.getAll();
 
 
18
  }
19
  }
src/app/home/home.component.html CHANGED
@@ -3,3 +3,6 @@
3
  <a routerLink="/books" class="button red">
4
  Show book list
5
  </a>
 
 
 
3
  <a routerLink="/books" class="button red">
4
  Show book list
5
  </a>
6
+
7
+ <h2>Search</h2>
8
+ <bm-search></bm-search>
src/app/search/search.component.html ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <input type="search"
2
+ autocomplete="off"
3
+ aria-label="Search"
4
+ [class.loading]="isLoading"
5
+ #searchInput
6
+ (input)="input$.next(searchInput.value)">
7
+
8
+ <ul class="search-results" *ngIf="results$ | async as results">
9
+ <li *ngFor="let book of results">
10
+ <a [routerLink]="['/books', book.isbn]">
11
+ {{ book.title }}
12
+ <p role="doc-subtitle">{{ book.subtitle }}</p>
13
+ </a>
14
+ </li>
15
+ <li *ngIf="!results.length">No results</li>
16
+ </ul>
src/app/search/search.component.ts ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component } from '@angular/core';
2
+ import { Subject, debounceTime, distinctUntilChanged, filter, switchMap, tap, Observable } from 'rxjs';
3
+
4
+ import { Book } from '../shared/book';
5
+ import { BookStoreService } from '../shared/book-store.service';
6
+
7
+ @Component({
8
+ selector: 'bm-search',
9
+ templateUrl: './search.component.html',
10
+ standalone: false,
11
+ styleUrl: './search.component.css'
12
+ })
13
+ export class SearchComponent {
14
+ input$ = new Subject<string>();
15
+ isLoading = false;
16
+
17
+ results$: Observable<Book[]>;
18
+
19
+ constructor(private service: BookStoreService) {
20
+ this.results$ = this.input$.pipe(
21
+ filter(term => term.length >= 3),
22
+ debounceTime(500),
23
+ distinctUntilChanged(),
24
+ tap(() => this.isLoading = true),
25
+ switchMap(term => this.service.getAllSearch(term)),
26
+ tap(() => this.isLoading = false)
27
+ );
28
+ }
29
+ }
src/app/shared/book-store.service.ts CHANGED
@@ -1,6 +1,6 @@
1
  import { HttpClient } from '@angular/common/http';
2
  import { Injectable } from '@angular/core';
3
- import { Observable } from 'rxjs';
4
 
5
  import { Book } from './book';
6
 
@@ -13,7 +13,12 @@ export class BookStoreService {
13
  constructor(private http: HttpClient) {}
14
 
15
  getAll(): Observable<Book[]> {
16
- return this.http.get<Book[]>(`${this.apiUrl}/books`);
 
 
 
 
 
17
  }
18
 
19
  getSingle(isbn: string): Observable<Book> {
@@ -23,4 +28,13 @@ export class BookStoreService {
23
  remove(isbn: string): Observable<unknown> {
24
  return this.http.delete(`${this.apiUrl}/books/${isbn}`);
25
  }
 
 
 
 
 
 
 
 
 
26
  }
1
  import { HttpClient } from '@angular/common/http';
2
  import { Injectable } from '@angular/core';
3
+ import { Observable, catchError, of } from 'rxjs';
4
 
5
  import { Book } from './book';
6
 
13
  constructor(private http: HttpClient) {}
14
 
15
  getAll(): Observable<Book[]> {
16
+ return this.http.get<Book[]>(`${this.apiUrl}/books`).pipe(
17
+ catchError(err => {
18
+ console.error(err);
19
+ return of([]);
20
+ })
21
+ );
22
  }
23
 
24
  getSingle(isbn: string): Observable<Book> {
28
  remove(isbn: string): Observable<unknown> {
29
  return this.http.delete(`${this.apiUrl}/books/${isbn}`);
30
  }
31
+
32
+ getAllSearch(term: string): Observable<Book[]> {
33
+ return this.http.get<Book[]>(`${this.apiUrl}/books/search/${term}`).pipe(
34
+ catchError(err => {
35
+ console.error(err);
36
+ return of([]);
37
+ })
38
+ );
39
+ }
40
  }