|
@@ -13,7 +13,15 @@
|
|
| 13 |
<section>
|
| 14 |
<h1>Books</h1>
|
| 15 |
<div>
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)="this.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>
|
|
@@ -1,31 +1,47 @@
|
|
| 1 |
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
| 2 |
-
|
| 3 |
import { BooksOverviewPage } from './books-overview-page';
|
| 4 |
|
| 5 |
describe('BooksOverviewPage', () => {
|
|
|
|
| 6 |
let fixture: ComponentFixture<BooksOverviewPage>;
|
| 7 |
|
| 8 |
beforeEach(async () => {
|
| 9 |
await TestBed.configureTestingModule({
|
| 10 |
imports: [BooksOverviewPage]
|
| 11 |
-
})
|
| 12 |
-
.compileComponents();
|
| 13 |
|
| 14 |
fixture = TestBed.createComponent(BooksOverviewPage);
|
|
|
|
| 15 |
fixture.detectChanges();
|
| 16 |
});
|
| 17 |
|
| 18 |
-
it('should
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
});
|
| 23 |
|
| 24 |
-
it('should
|
| 25 |
-
|
| 26 |
-
const bookCardEls = compiledElement.querySelectorAll('app-book-card');
|
| 27 |
|
| 28 |
-
|
| 29 |
-
expect(
|
| 30 |
});
|
| 31 |
});
|
|
|
|
| 1 |
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
|
|
| 2 |
import { BooksOverviewPage } from './books-overview-page';
|
| 3 |
|
| 4 |
describe('BooksOverviewPage', () => {
|
| 5 |
+
let component: BooksOverviewPage;
|
| 6 |
let fixture: ComponentFixture<BooksOverviewPage>;
|
| 7 |
|
| 8 |
beforeEach(async () => {
|
| 9 |
await TestBed.configureTestingModule({
|
| 10 |
imports: [BooksOverviewPage]
|
| 11 |
+
}).compileComponents();
|
|
|
|
| 12 |
|
| 13 |
fixture = TestBed.createComponent(BooksOverviewPage);
|
| 14 |
+
component = fixture.componentInstance;
|
| 15 |
fixture.detectChanges();
|
| 16 |
});
|
| 17 |
|
| 18 |
+
it('should display all books if the search term is empty', () => {
|
| 19 |
+
component['searchTerm'].set('');
|
| 20 |
+
|
| 21 |
+
const books = component['filteredBooks']();
|
| 22 |
+
expect(books.length).toBe(2);
|
| 23 |
+
});
|
| 24 |
+
|
| 25 |
+
it('should filter books based on the search term', () => {
|
| 26 |
+
component['searchTerm'].set('Affe');
|
| 27 |
+
|
| 28 |
+
const books = component['filteredBooks']();
|
| 29 |
+
expect(books.length).toBe(1);
|
| 30 |
+
expect(books[0].title).toBe('Backen mit Affen');
|
| 31 |
+
});
|
| 32 |
+
|
| 33 |
+
it('should filter books ignoring case sensitivity', () => {
|
| 34 |
+
component['searchTerm'].set('AFFEN');
|
| 35 |
+
|
| 36 |
+
const books = component['filteredBooks']();
|
| 37 |
+
expect(books.length).toBe(1);
|
| 38 |
+
expect(books[0].title).toBe('Backen mit Affen');
|
| 39 |
});
|
| 40 |
|
| 41 |
+
it('should return an empty array if no book matches the search term', () => {
|
| 42 |
+
component['searchTerm'].set('unbekannter Titel');
|
|
|
|
| 43 |
|
| 44 |
+
const books = component['filteredBooks']();
|
| 45 |
+
expect(books.length).toBe(0);
|
| 46 |
});
|
| 47 |
});
|
|
@@ -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 |
}
|
|
@@ -1,24 +0,0 @@
|
|
| 1 |
-
import { BookStore } from './book-store';
|
| 2 |
-
import { Book } from './book';
|
| 3 |
-
|
| 4 |
-
describe('BookStore', () => {
|
| 5 |
-
let service: BookStore;
|
| 6 |
-
|
| 7 |
-
beforeEach(() => {
|
| 8 |
-
service = new BookStore();
|
| 9 |
-
});
|
| 10 |
-
|
| 11 |
-
it('should be created', () => {
|
| 12 |
-
expect(service).toBeTruthy();
|
| 13 |
-
});
|
| 14 |
-
|
| 15 |
-
it('should return a list of books', () => {
|
| 16 |
-
const books = service.getAll();
|
| 17 |
-
|
| 18 |
-
// Optional: Wir können die Bücher auch noch intensiver prüfen
|
| 19 |
-
// Zum Beispiel:
|
| 20 |
-
// expect(books[0].isbn).toBe('12345');
|
| 21 |
-
// expect(books[1].title).toBe('Backen mit Affen');
|
| 22 |
-
expect(books.length).toBeGreaterThan(0);
|
| 23 |
-
});
|
| 24 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { TestBed } from '@angular/core/testing';
|
| 2 |
+
|
| 3 |
+
import { BookStore } from './book-store';
|
| 4 |
+
|
| 5 |
+
describe('BookStore', () => {
|
| 6 |
+
let service: BookStore;
|
| 7 |
+
|
| 8 |
+
beforeEach(() => {
|
| 9 |
+
TestBed.configureTestingModule({});
|
| 10 |
+
service = TestBed.inject(BookStore);
|
| 11 |
+
});
|
| 12 |
+
|
| 13 |
+
it('should be created', () => {
|
| 14 |
+
expect(service).toBeTruthy();
|
| 15 |
+
});
|
| 16 |
+
});
|
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
import { TestBed } from '@angular/core/testing';
|
| 2 |
-
import { BookStore } from './book-store';
|
| 3 |
-
|
| 4 |
-
describe('BookStore', () => {
|
| 5 |
-
let service: BookStore;
|
| 6 |
-
|
| 7 |
-
beforeEach(() => {
|
| 8 |
-
TestBed.configureTestingModule({
|
| 9 |
-
providers: [BookStore]
|
| 10 |
-
});
|
| 11 |
-
service = TestBed.inject(BookStore);
|
| 12 |
-
});
|
| 13 |
-
|
| 14 |
-
it('should be created', () => {
|
| 15 |
-
expect(service).toBeTruthy();
|
| 16 |
-
});
|
| 17 |
-
|
| 18 |
-
it('should return a list of books', () => {
|
| 19 |
-
const books = service.getAll();
|
| 20 |
-
expect(books.length).toBeGreaterThan(0);
|
| 21 |
-
|
| 22 |
-
// Optional: Wir können die Bücher auch noch intensiver prüfen
|
| 23 |
-
// Zum Beispiel:
|
| 24 |
-
// expect(books[0].isbn).toBe('12345');
|
| 25 |
-
// expect(books[1].title).toBe('Backen mit Affen');
|
| 26 |
-
});
|
| 27 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|