Differenzansicht 05-filter
im Vergleich zu 04-services

Zurück zur Übersicht | ← Vorherige | Nächste → | Demo | Quelltext auf GitHub
src/app/books-portal/books-overview-page/books-overview-page.html CHANGED
@@ -13,7 +13,15 @@
13
  <section>
14
  <h1>Books</h1>
15
  <div>
16
- @for (b of books(); track b.isbn) {
 
 
 
 
 
 
 
 
17
  <app-book-card [book]="b" (like)="addLikedBook($event)" />
18
  }
19
  </div>
 
13
  <section>
14
  <h1>Books</h1>
15
  <div>
16
+ <input
17
+ type="search"
18
+ [value]="searchTerm()"
19
+ (input)="searchTerm.set($event.target.value)"
20
+ placeholder="Search"
21
+ aria-label="Search"
22
+ />
23
+
24
+ @for (b of filteredBooks(); track b.isbn) {
25
  <app-book-card [book]="b" (like)="addLikedBook($event)" />
26
  }
27
  </div>
src/app/books-portal/books-overview-page/books-overview-page.spec.ts CHANGED
@@ -51,4 +51,34 @@ describe('BooksOverviewPage', () => {
51
  expect(bookCardEls[0].textContent).toContain('Tierisch gut kochen');
52
  expect(bookCardEls[1].textContent).toContain('Backen mit Affen');
53
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  });
 
51
  expect(bookCardEls[0].textContent).toContain('Tierisch gut kochen');
52
  expect(bookCardEls[1].textContent).toContain('Backen mit Affen');
53
  });
54
+
55
+ it('should display all books if the search term is empty', () => {
56
+ component['searchTerm'].set('');
57
+
58
+ const books = component['filteredBooks']();
59
+ expect(books.length).toBe(2);
60
+ });
61
+
62
+ it('should filter books based on the search term', () => {
63
+ component['searchTerm'].set('Affe');
64
+
65
+ const books = component['filteredBooks']();
66
+ expect(books.length).toBe(1);
67
+ expect(books[0].title).toBe('Backen mit Affen');
68
+ });
69
+
70
+ it('should filter books ignoring case sensitivity', () => {
71
+ component['searchTerm'].set('AFFEN');
72
+
73
+ const books = component['filteredBooks']();
74
+ expect(books.length).toBe(1);
75
+ expect(books[0].title).toBe('Backen mit Affen');
76
+ });
77
+
78
+ it('should return an empty array if no book matches the search term', () => {
79
+ component['searchTerm'].set('unbekannter Titel');
80
+
81
+ const books = component['filteredBooks']();
82
+ expect(books.length).toBe(0);
83
+ });
84
  });
src/app/books-portal/books-overview-page/books-overview-page.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Component, inject, signal } from '@angular/core';
2
 
3
  import { Book } from '../../shared/book';
4
  import { BookCard } from '../book-card/book-card';
@@ -13,9 +13,20 @@ import { BookStore } from '../../shared/book-store';
13
  export class BooksOverviewPage {
14
  #bookStore = inject(BookStore);
15
 
 
 
16
  protected books = signal<Book[]>([]);
17
  protected likedBooks = signal<Book[]>([]);
18
 
 
 
 
 
 
 
 
 
 
19
  constructor() {
20
  this.books.set(this.#bookStore.getAll());
21
  }
 
1
+ import { Component, computed, inject, signal } from '@angular/core';
2
 
3
  import { Book } from '../../shared/book';
4
  import { BookCard } from '../book-card/book-card';
 
13
  export class BooksOverviewPage {
14
  #bookStore = inject(BookStore);
15
 
16
+ protected searchTerm = signal('');
17
+
18
  protected books = signal<Book[]>([]);
19
  protected likedBooks = signal<Book[]>([]);
20
 
21
+ protected 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.books.set(this.#bookStore.getAll());
32
  }