Differenzansicht 06-routing
im Vergleich zu 05-filter

Zurück zur Übersicht | ← Vorherige | Nächste → | Demo | Quelltext auf GitHub
src/app/app.html CHANGED
@@ -1,3 +1,15 @@
1
  <main>
2
- <app-books-overview-page />
 
 
 
 
 
 
 
 
 
 
 
 
3
  </main>
 
1
  <main>
2
+ <nav>
3
+ <ul>
4
+ <li>
5
+ <a routerLink="/home" routerLinkActive="active"
6
+ ariaCurrentWhenActive="page">Home</a>
7
+ </li>
8
+ <li>
9
+ <a routerLink="/books" routerLinkActive="active"
10
+ ariaCurrentWhenActive="page">Books</a>
11
+ </li>
12
+ </ul>
13
+ </nav>
14
+ <router-outlet />
15
  </main>
src/app/app.routes.ts CHANGED
@@ -1,3 +1,10 @@
1
  import { Routes } from '@angular/router';
2
 
3
- export const routes: Routes = [];
 
 
 
 
 
 
 
 
1
  import { Routes } from '@angular/router';
2
 
3
+ import { HomePage } from './home-page/home-page';
4
+ import { booksPortalRoutes } from './books-portal/books-portal.routes';
5
+
6
+ export const routes: Routes = [
7
+ { path: '', redirectTo: 'home', pathMatch: 'full' },
8
+ { path: 'home', component: HomePage, title: 'BookManager' },
9
+ ...booksPortalRoutes
10
+ ];
src/app/app.spec.ts CHANGED
@@ -1,10 +1,12 @@
1
  import { TestBed } from '@angular/core/testing';
 
2
  import { App } from './app';
3
 
4
  describe('App', () => {
5
  beforeEach(async () => {
6
  await TestBed.configureTestingModule({
7
  imports: [App],
 
8
  }).compileComponents();
9
  });
10
 
 
1
  import { TestBed } from '@angular/core/testing';
2
+ import { provideRouter } from '@angular/router';
3
  import { App } from './app';
4
 
5
  describe('App', () => {
6
  beforeEach(async () => {
7
  await TestBed.configureTestingModule({
8
  imports: [App],
9
+ providers: [provideRouter([])]
10
  }).compileComponents();
11
  });
12
 
src/app/app.ts CHANGED
@@ -1,10 +1,9 @@
1
  import { Component } from '@angular/core';
2
-
3
- import { BooksOverviewPage } from './books-portal/books-overview-page/books-overview-page';
4
 
5
  @Component({
6
  selector: 'app-root',
7
- imports: [BooksOverviewPage],
8
  templateUrl: './app.html',
9
  styleUrl: './app.css'
10
  })
 
1
  import { Component } from '@angular/core';
2
+ import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
 
3
 
4
  @Component({
5
  selector: 'app-root',
6
+ imports: [RouterOutlet, RouterLink, RouterLinkActive],
7
  templateUrl: './app.html',
8
  styleUrl: './app.css'
9
  })
src/app/books-portal/book-card/book-card.html CHANGED
@@ -12,6 +12,7 @@
12
  ISBN: {{ b.isbn }}
13
  </div>
14
  <footer>
 
15
  <button type="button" class="secondary" (click)="likeBook()">Like</button>
16
  </footer>
17
  </article>
 
12
  ISBN: {{ b.isbn }}
13
  </div>
14
  <footer>
15
+ <a [routerLink]="['details', b.isbn]">Details</a>
16
  <button type="button" class="secondary" (click)="likeBook()">Like</button>
17
  </footer>
18
  </article>
src/app/books-portal/book-card/book-card.spec.ts CHANGED
@@ -1,5 +1,6 @@
1
  import { inputBinding, outputBinding, signal } from '@angular/core';
2
  import { ComponentFixture, TestBed } from '@angular/core/testing';
 
3
  import { Mock } from 'vitest';
4
 
5
  import { Book } from '../../shared/book';
@@ -22,7 +23,8 @@ describe('BookCard', () => {
22
  likeMock = vi.fn();
23
 
24
  await TestBed.configureTestingModule({
25
- imports: [BookCard]
 
26
  })
27
  .compileComponents();
28
 
 
1
  import { inputBinding, outputBinding, signal } from '@angular/core';
2
  import { ComponentFixture, TestBed } from '@angular/core/testing';
3
+ import { provideRouter } from '@angular/router';
4
  import { Mock } from 'vitest';
5
 
6
  import { Book } from '../../shared/book';
 
23
  likeMock = vi.fn();
24
 
25
  await TestBed.configureTestingModule({
26
+ imports: [BookCard],
27
+ providers: [provideRouter([])],
28
  })
29
  .compileComponents();
30
 
src/app/books-portal/book-card/book-card.ts CHANGED
@@ -1,10 +1,11 @@
1
  import { Component, input, output } 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
  })
 
1
  import { Component, input, output } from '@angular/core';
2
+ import { RouterLink } from '@angular/router';
3
 
4
  import { Book } from '../../shared/book';
5
 
6
  @Component({
7
  selector: 'app-book-card',
8
+ imports: [RouterLink],
9
  templateUrl: './book-card.html',
10
  styleUrl: './book-card.css'
11
  })
src/app/books-portal/book-details-page/book-details-page.html ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @if (book(); as b) {
2
+ <article>
3
+ <header>
4
+ <h1>{{ b.title }}</h1>
5
+ @if (b.subtitle) {
6
+ <p role="doc-subtitle">{{ b.subtitle }}</p>
7
+ }
8
+ <div class="grid">
9
+ <div>
10
+ <h2>Authors</h2>
11
+ <ul>
12
+ @for (author of b.authors; track $index) {
13
+ <li>{{ author }}</li>
14
+ }
15
+ </ul>
16
+ </div>
17
+ <div>
18
+ <h2>ISBN</h2>
19
+ {{ b.isbn }}
20
+ </div>
21
+ <div>
22
+ <h2>Created at</h2>
23
+ {{ b.createdAt }}
24
+ </div>
25
+ </div>
26
+ </header>
27
+ <p>{{ b.description }}</p>
28
+ <img [src]="b.imageUrl" alt="Cover" />
29
+ <footer>
30
+ <a routerLink="/books">Back to list</a>
31
+ </footer>
32
+ </article>
33
+ }
src/app/books-portal/book-details-page/book-details-page.spec.ts ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { RouterTestingHarness } from '@angular/router/testing';
3
+ import { provideRouter, Router } from '@angular/router';
4
+ import { provideLocationMocks } from '@angular/common/testing';
5
+ import { Location } from '@angular/common';
6
+
7
+ import { BookDetailsPage } from './book-details-page';
8
+ import { booksPortalRoutes } from '../books-portal.routes';
9
+ import { BookStore } from '../../shared/book-store';
10
+
11
+ describe('BookDetailsPage', () => {
12
+ let component: BookDetailsPage;
13
+ let fixture: ComponentFixture<BookDetailsPage>;
14
+ let bookStore: BookStore;
15
+
16
+ beforeEach(async () => {
17
+ await TestBed.configureTestingModule({
18
+ imports: [BookDetailsPage],
19
+ providers: [
20
+ provideRouter(booksPortalRoutes),
21
+ provideLocationMocks(),
22
+ ]
23
+ })
24
+ .compileComponents();
25
+
26
+ fixture = TestBed.createComponent(BookDetailsPage);
27
+ component = fixture.componentInstance;
28
+ bookStore = TestBed.inject(BookStore);
29
+ await fixture.whenStable();
30
+ });
31
+
32
+ it('should create', () => {
33
+ expect(component).toBeTruthy();
34
+ });
35
+
36
+ it('should load the correct book by ISBN', async () => {
37
+ const harness = await RouterTestingHarness.create();
38
+ const component = await harness.navigateByUrl('/books/details/12345', BookDetailsPage);
39
+
40
+ const expectedBook = bookStore.getSingle('12345');
41
+
42
+ expect(component['book']()).toEqual(expectedBook);
43
+ expect(document.title).toBe('Book Details');
44
+ });
45
+
46
+ it('should navigate to the details page', async () => {
47
+ const location = TestBed.inject(Location);
48
+ const router = TestBed.inject(Router);
49
+
50
+ await router.navigate(['/books/details/12345']);
51
+
52
+ expect(location.path()).toBe('/books/details/12345');
53
+ });
54
+ });
src/app/books-portal/book-details-page/book-details-page.ts ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component, inject, signal } from '@angular/core';
2
+ import { ActivatedRoute, RouterLink } from '@angular/router';
3
+
4
+ import { Book } from '../../shared/book';
5
+ import { BookStore } from '../../shared/book-store';
6
+
7
+ @Component({
8
+ selector: 'app-book-details-page',
9
+ imports: [RouterLink],
10
+ templateUrl: './book-details-page.html',
11
+ styleUrl: './book-details-page.css'
12
+ })
13
+ export class BookDetailsPage {
14
+ #bookStore = inject(BookStore);
15
+ #route = inject(ActivatedRoute);
16
+
17
+ protected book = signal<Book | undefined>(undefined);
18
+
19
+ constructor() {
20
+ const isbn = this.#route.snapshot.paramMap.get('isbn');
21
+ if (isbn) {
22
+ this.book.set(this.#bookStore.getSingle(isbn));
23
+ }
24
+ }
25
+ }
src/app/books-portal/books-overview-page/books-overview-page.spec.ts CHANGED
@@ -1,6 +1,9 @@
1
  import { ComponentFixture, TestBed } from '@angular/core/testing';
 
 
2
 
3
  import { BooksOverviewPage } from './books-overview-page';
 
4
 
5
  describe('BooksOverviewPage', () => {
6
  let component: BooksOverviewPage;
@@ -8,7 +11,8 @@ describe('BooksOverviewPage', () => {
8
 
9
  beforeEach(async () => {
10
  await TestBed.configureTestingModule({
11
- imports: [BooksOverviewPage]
 
12
  })
13
  .compileComponents();
14
 
@@ -81,4 +85,12 @@ describe('BooksOverviewPage', () => {
81
  const books = component['filteredBooks']();
82
  expect(books.length).toBe(0);
83
  });
 
 
 
 
 
 
 
 
84
  });
 
1
  import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { provideRouter } from '@angular/router';
3
+ import { RouterTestingHarness } from '@angular/router/testing';
4
 
5
  import { BooksOverviewPage } from './books-overview-page';
6
+ import { booksPortalRoutes } from '../books-portal.routes';
7
 
8
  describe('BooksOverviewPage', () => {
9
  let component: BooksOverviewPage;
 
11
 
12
  beforeEach(async () => {
13
  await TestBed.configureTestingModule({
14
+ imports: [BooksOverviewPage],
15
+ providers: [provideRouter(booksPortalRoutes)]
16
  })
17
  .compileComponents();
18
 
 
85
  const books = component['filteredBooks']();
86
  expect(books.length).toBe(0);
87
  });
88
+
89
+ it('should load the BooksOverviewPage for /books', async () => {
90
+ const harness = await RouterTestingHarness.create();
91
+ const component = await harness.navigateByUrl('/books', BooksOverviewPage);
92
+
93
+ expect(component).toBeTruthy();
94
+ expect(document.title).toBe('Books');
95
+ });
96
  });
src/app/books-portal/books-portal.routes.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import { Routes } from '@angular/router';
2
+
3
+ import { BooksOverviewPage } from './books-overview-page/books-overview-page';
4
+ import { BookDetailsPage } from './book-details-page/book-details-page';
5
+
6
+ export const booksPortalRoutes: Routes = [
7
+ { path: 'books', component: BooksOverviewPage, title: 'Books' },
8
+ { path: 'books/details/:isbn', component: BookDetailsPage, title: 'Book Details' },
9
+ ];
src/app/home-page/home-page.html ADDED
@@ -0,0 +1 @@
 
 
1
+ <h1>Welcome to the BookManager!</h1>
src/app/home-page/home-page.spec.ts ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { provideRouter } from '@angular/router';
3
+ import { RouterTestingHarness } from '@angular/router/testing';
4
+
5
+ import { HomePage } from './home-page';
6
+ import { routes } from '../app.routes';
7
+
8
+ describe('HomePage', () => {
9
+ let component: HomePage;
10
+ let fixture: ComponentFixture<HomePage>;
11
+
12
+ beforeEach(async () => {
13
+ await TestBed.configureTestingModule({
14
+ imports: [HomePage],
15
+ providers: [provideRouter(routes)]
16
+ })
17
+ .compileComponents();
18
+
19
+ fixture = TestBed.createComponent(HomePage);
20
+ component = fixture.componentInstance;
21
+ await fixture.whenStable();
22
+ });
23
+
24
+ it('should create', () => {
25
+ expect(component).toBeTruthy();
26
+ });
27
+
28
+ it('should load the HomePage component for /home', async () => {
29
+ const harness = await RouterTestingHarness.create();
30
+ const component = await harness.navigateByUrl('/home', HomePage);
31
+
32
+ expect(component).toBeTruthy();
33
+ expect(document.title).toBe('BookManager');
34
+ });
35
+ });
src/app/home-page/home-page.ts ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'app-home-page',
5
+ imports: [],
6
+ templateUrl: './home-page.html',
7
+ styleUrl: './home-page.css'
8
+ })
9
+ export class HomePage {
10
+
11
+ }
src/app/shared/book-store.ts CHANGED
@@ -31,4 +31,8 @@ export class BookStore {
31
  getAll(): Book[] {
32
  return this.#books;
33
  }
 
 
 
 
34
  }
 
31
  getAll(): Book[] {
32
  return this.#books;
33
  }
34
+
35
+ getSingle(isbn: string): Book | undefined {
36
+ return this.#books.find(book => book.isbn === isbn);
37
+ }
38
  }