Differenzansicht 02-inputs
im Vergleich zu 01-components

Zurück zur Übersicht | ← Vorherige | Nächste → | Demo | Quelltext auf GitHub
src/app/books-portal/book-card/book-card.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @let b = book();
2
+
3
+ <article class="book-card">
4
+ <img [src]="b.imageUrl" alt="Cover" />
5
+ <header>
6
+ <h2>{{ b.title }}</h2>
7
+ </header>
8
+ <div>
9
+ @if (b.subtitle) {
10
+ <p role="doc-subtitle">{{ b.subtitle }}</p>
11
+ }
12
+ ISBN: {{ b.isbn }}
13
+ </div>
14
+ </article>
src/app/books-portal/book-card/book-card.spec.ts ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { inputBinding, signal } from '@angular/core';
2
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
3
+
4
+ import { Book } from '../../shared/book';
5
+ import { BookCard } from './book-card';
6
+
7
+ describe('BookCard', () => {
8
+ let component: BookCard;
9
+ let fixture: ComponentFixture<BookCard>;
10
+ const testBook = signal<Book>({
11
+ isbn: '1234567890123',
12
+ title: 'Test Book',
13
+ authors: ['Test Author'],
14
+ description: '',
15
+ imageUrl: 'https://example.com/test.png',
16
+ createdAt: '2026-01-01'
17
+ });
18
+
19
+ beforeEach(async () => {
20
+ await TestBed.configureTestingModule({
21
+ imports: [BookCard]
22
+ })
23
+ .compileComponents();
24
+
25
+ fixture = TestBed.createComponent(BookCard, {
26
+ bindings: [inputBinding('book', testBook)]
27
+ });
28
+ component = fixture.componentInstance;
29
+ await fixture.whenStable();
30
+ });
31
+
32
+ it('should create', () => {
33
+ expect(component).toBeTruthy();
34
+ });
35
+
36
+ it('should render book title and isbn', () => {
37
+ const compiledElement: HTMLElement = fixture.nativeElement;
38
+ expect(compiledElement.textContent).toContain(testBook().isbn);
39
+ expect(compiledElement.textContent).toContain(testBook().title);
40
+ });
41
+
42
+ it('should display the correct image', () => {
43
+ const compiledElement: HTMLElement = fixture.nativeElement;
44
+ const imageEl = compiledElement.querySelector('img');
45
+ expect(imageEl).toBeTruthy();
46
+ expect(imageEl?.src).toBe(testBook().imageUrl);
47
+ });
48
+ });
src/app/books-portal/book-card/book-card.ts ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component, input } from '@angular/core';
2
+
3
+ import { Book } from '../../shared/book';
4
+
5
+ @Component({
6
+ selector: 'app-book-card',
7
+ imports: [],
8
+ templateUrl: './book-card.html',
9
+ styleUrl: './book-card.css'
10
+ })
11
+ export class BookCard {
12
+ readonly book = input.required<Book>();
13
+ }
src/app/books-portal/books-overview-page/books-overview-page.html CHANGED
@@ -2,17 +2,7 @@
2
  <h1>Books</h1>
3
  <div>
4
  @for (b of books(); track b.isbn) {
5
- <article class="book-card">
6
- <header>
7
- <h2>{{ b.title }}</h2>
8
- </header>
9
- <div>
10
- @if (b.subtitle) {
11
- <p role="doc-subtitle">{{ b.subtitle }}</p>
12
- }
13
- ISBN: {{ b.isbn }}
14
- </div>
15
- </article>
16
  }
17
  </div>
18
  </section>
 
2
  <h1>Books</h1>
3
  <div>
4
  @for (b of books(); track b.isbn) {
5
+ <app-book-card [book]="b" />
 
 
 
 
 
 
 
 
 
 
6
  }
7
  </div>
8
  </section>
src/app/books-portal/books-overview-page/books-overview-page.spec.ts CHANGED
@@ -37,4 +37,18 @@ describe('BooksOverviewPage', () => {
37
  expect(articleEls[0].textContent).toContain('Tierisch gut kochen');
38
  expect(articleEls[1].textContent).toContain('Backen mit Affen');
39
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  });
 
37
  expect(articleEls[0].textContent).toContain('Tierisch gut kochen');
38
  expect(articleEls[1].textContent).toContain('Backen mit Affen');
39
  });
40
+
41
+ it('should render a BookCard component for each book', () => {
42
+ const compiledElement: HTMLElement = fixture.nativeElement;
43
+ const bookCardEls = compiledElement.querySelectorAll('app-book-card');
44
+ expect(bookCardEls.length).toBe(2);
45
+ });
46
+
47
+ it('should correctly pass book data to BookCard components', () => {
48
+ const compiledElement: HTMLElement = fixture.nativeElement;
49
+ const bookCardEls = compiledElement.querySelectorAll('app-book-card');
50
+
51
+ expect(bookCardEls[0].textContent).toContain('Tierisch gut kochen');
52
+ expect(bookCardEls[1].textContent).toContain('Backen mit Affen');
53
+ });
54
  });
src/app/books-portal/books-overview-page/books-overview-page.ts CHANGED
@@ -1,10 +1,11 @@
1
  import { Component, signal } from '@angular/core';
2
 
3
  import { Book } from '../../shared/book';
 
4
 
5
  @Component({
6
  selector: 'app-books-overview-page',
7
- imports: [],
8
  templateUrl: './books-overview-page.html',
9
  styleUrl: './books-overview-page.css',
10
  })
 
1
  import { Component, signal } from '@angular/core';
2
 
3
  import { Book } from '../../shared/book';
4
+ import { BookCard } from '../book-card/book-card';
5
 
6
  @Component({
7
  selector: 'app-books-overview-page',
8
+ imports: [BookCard],
9
  templateUrl: './books-overview-page.html',
10
  styleUrl: './books-overview-page.css',
11
  })