Differenzansicht 09-resource
im Vergleich zu 08-http

← Zurück zur Ãœbersicht | Demo | Quelltext auf GitHub
src/app/books-portal/book-details/book-details.ng.html CHANGED
@@ -1,4 +1,4 @@
1
- @if (book(); as b) {
2
  <article>
3
  <header>
4
  <h1>{{ b.title }}</h1>
@@ -28,6 +28,7 @@
28
  <img [src]="b.imageUrl" alt="Cover" />
29
  <footer>
30
  <a routerLink="/books">Back to list</a>
 
31
  <button type="button" (click)="deleteBook(b.isbn)">
32
  Delete book
33
  </button>
 
1
+ @if (book.value(); as b) {
2
  <article>
3
  <header>
4
  <h1>{{ b.title }}</h1>
 
28
  <img [src]="b.imageUrl" alt="Cover" />
29
  <footer>
30
  <a routerLink="/books">Back to list</a>
31
+ <a routerLink="/books/details/9783864909467">Angular Book</a>
32
  <button type="button" (click)="deleteBook(b.isbn)">
33
  Delete book
34
  </button>
src/app/books-portal/book-details/book-details.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { Component, effect, inject, input, signal } from '@angular/core';
2
  import { Router, RouterLink } from '@angular/router';
3
 
4
- import { Book } from '../../shared/book';
5
  import { BookStore } from '../../shared/book-store';
6
 
7
  @Component({
@@ -15,15 +14,7 @@ export class BookDetails {
15
  #router = inject(Router);
16
 
17
  readonly isbn = input.required<string>();
18
- readonly book = signal<Book | undefined>(undefined);
19
-
20
- constructor() {
21
- effect(() => {
22
- this.#store.getOneBook(this.isbn()).subscribe(book => {
23
- this.book.set(book);
24
- });
25
- });
26
- }
27
 
28
  deleteBook(isbn: string) {
29
  if (window.confirm('Delete book?')) {
 
1
+ import { Component, inject, input } from '@angular/core';
2
  import { Router, RouterLink } from '@angular/router';
3
 
 
4
  import { BookStore } from '../../shared/book-store';
5
 
6
  @Component({
 
14
  #router = inject(Router);
15
 
16
  readonly isbn = input.required<string>();
17
+ readonly book = this.#store.getOneBook(this.isbn);
 
 
 
 
 
 
 
 
18
 
19
  deleteBook(isbn: string) {
20
  if (window.confirm('Delete book?')) {
src/app/books-portal/book-list/book-list.ng.html CHANGED
@@ -12,6 +12,10 @@
12
 
13
  <section>
14
  <h1>Books</h1>
 
 
 
 
15
  <div>
16
  <input
17
  type="search"
 
12
 
13
  <section>
14
  <h1>Books</h1>
15
+ <button type="button" (click)="books.reload()" [attr.aria-busy]="books.isLoading()">
16
+ Reload
17
+ </button>
18
+
19
  <div>
20
  <input
21
  type="search"
src/app/books-portal/book-list/book-list.ts CHANGED
@@ -15,24 +15,18 @@ export class BookList {
15
 
16
  readonly searchTerm = signal('');
17
 
18
- readonly books = signal<Book[]>([]);
19
  readonly likedBooks = signal<Book[]>([]);
20
 
21
  readonly filteredBooks = computed(() => {
22
  if (!this.searchTerm()) {
23
- return this.books();
24
  }
25
 
26
  const term = this.searchTerm().toLowerCase();
27
- return this.books().filter((b) => b.title.toLowerCase().includes(term));
28
  });
29
 
30
- constructor() {
31
- this.#store.getBookList().subscribe(books => {
32
- this.books.set(books);
33
- });
34
- }
35
-
36
  addLikedBook(newLikedBook: Book) {
37
  const foundBook = this.likedBooks().find(
38
  (b) => b.isbn === newLikedBook.isbn
 
15
 
16
  readonly searchTerm = signal('');
17
 
18
+ readonly books = this.#store.getBookList();
19
  readonly likedBooks = signal<Book[]>([]);
20
 
21
  readonly filteredBooks = computed(() => {
22
  if (!this.searchTerm()) {
23
+ return this.books.value();
24
  }
25
 
26
  const term = this.searchTerm().toLowerCase();
27
+ return this.books.value().filter((b) => b.title.toLowerCase().includes(term));
28
  });
29
 
 
 
 
 
 
 
30
  addLikedBook(newLikedBook: Book) {
31
  const foundBook = this.likedBooks().find(
32
  (b) => b.isbn === newLikedBook.isbn
src/app/shared/book-store.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { inject, Injectable } from '@angular/core';
2
- import { HttpClient } from '@angular/common/http';
3
  import { Observable } from 'rxjs';
4
 
5
  import { Book } from './book';
@@ -8,16 +8,20 @@ import { Book } from './book';
8
  providedIn: 'root'
9
  })
10
  export class BookStore {
11
-
12
  #http = inject(HttpClient);
13
  #apiUrl = 'https://api6.angular-buch.com';
14
 
15
- getBookList(): Observable<Book[]> {
16
- return this.#http.get<Book[]>(`${this.#apiUrl}/books`);
 
 
 
17
  }
18
 
19
- getOneBook(isbn: string): Observable<Book> {
20
- return this.#http.get<Book>(`${this.#apiUrl}/books/${isbn}`);
 
 
21
  }
22
 
23
  deleteBook(isbn: string): Observable<unknown> {
 
1
+ import { inject, Injectable, Signal } from '@angular/core';
2
+ import { HttpClient, httpResource, HttpResourceRef } from '@angular/common/http';
3
  import { Observable } from 'rxjs';
4
 
5
  import { Book } from './book';
 
8
  providedIn: 'root'
9
  })
10
  export class BookStore {
 
11
  #http = inject(HttpClient);
12
  #apiUrl = 'https://api6.angular-buch.com';
13
 
14
+ getBookList(): HttpResourceRef<Book[]> {
15
+ return httpResource<Book[]>(
16
+ () => `${this.#apiUrl}/books`,
17
+ { defaultValue: [] }
18
+ );
19
  }
20
 
21
+ getOneBook(isbn: Signal<string>): HttpResourceRef<Book | undefined> {
22
+ return httpResource<Book>(
23
+ () => `${this.#apiUrl}/books/${isbn()}`
24
+ );
25
  }
26
 
27
  deleteBook(isbn: string): Observable<unknown> {