20230728-angular-Erwieterung-observAsyncPipe
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { HomeComponent } from './home/home.component';
|
||||||
|
|
||||||
const routes: Routes = [];
|
const routes: Routes = [
|
||||||
|
{ path: '', redirectTo: 'home', pathMatch: 'full' },
|
||||||
|
{ path: 'home', component: HomeComponent },
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes)],
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
<nav>
|
||||||
|
<a routerLink="/home" routerLinkActive="active" ariaCurrentWhenActive="page"
|
||||||
|
>Home</a
|
||||||
|
>
|
||||||
|
<a routerLink="/books" routerLinkActive="active" ariaCurrentWhenActive="page"
|
||||||
|
>Books</a
|
||||||
|
>
|
||||||
|
</nav>
|
||||||
<main>
|
<main>
|
||||||
<bm-book-list *ngIf="!book" (selectBook)="showDetails($event)"></bm-book-list>
|
<router-outlet></router-outlet>
|
||||||
<bm-book-details
|
|
||||||
*ngIf="book"
|
|
||||||
(leave)="showList()"
|
|
||||||
[book]="book"
|
|
||||||
></bm-book-details>
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import { BrowserModule } from '@angular/platform-browser';
|
|||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { BooksModule } from './books/books.module';
|
import { BooksModule } from './books/books.module';
|
||||||
|
import { HomeComponent } from './home/home.component';
|
||||||
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AppComponent],
|
declarations: [AppComponent, HomeComponent],
|
||||||
imports: [BrowserModule, AppRoutingModule, BooksModule],
|
imports: [BrowserModule, AppRoutingModule, BooksModule, HttpClientModule],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<div class="details" *ngIf="book">
|
<div class="details" *ngIf="book$ | async as book">
|
||||||
<h1>{{ book.tilte }}</h1>
|
<h1>{{ book.title }}</h1>
|
||||||
<p role="doc-subtitle" *ngIf="book.subtilte">{{ book.subtilte }}</p>
|
<p role="doc-subtitle" *ngIf="book.subtilte">{{ book.subtilte }}</p>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div>
|
<div>
|
||||||
@@ -20,5 +20,6 @@
|
|||||||
<h2>Description</h2>
|
<h2>Description</h2>
|
||||||
<p>{{ book.description }}</p>
|
<p>{{ book.description }}</p>
|
||||||
<img *ngIf="book.thumbnailUrl" [src]="book.thumbnailUrl" alt="Cover" />
|
<img *ngIf="book.thumbnailUrl" [src]="book.thumbnailUrl" alt="Cover" />
|
||||||
<button class="arrow-left" (click)="doLeave()">Back to list</button>
|
<a class="button" routerLink="..">Back to list</a>
|
||||||
|
<button class="red" (click)="removeBook(book.isbn)">Remove book</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Book } from 'src/shared/book';
|
import { Book } from 'src/shared/book';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { BookStoreService } from 'src/app/shared/book-store.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'bm-book-details',
|
selector: 'bm-book-details',
|
||||||
@@ -7,10 +10,21 @@ import { Book } from 'src/shared/book';
|
|||||||
styleUrls: ['./book-details.component.css'],
|
styleUrls: ['./book-details.component.css'],
|
||||||
})
|
})
|
||||||
export class BookDetailsComponent {
|
export class BookDetailsComponent {
|
||||||
@Input() book?: Book;
|
book$: Observable<Book>;
|
||||||
@Output() leave = new EventEmitter<void>();
|
|
||||||
|
|
||||||
doLeave() {
|
constructor(
|
||||||
this.leave.emit();
|
private service: BookStoreService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
) {
|
||||||
|
const isbn = this.route.snapshot.paramMap.get('isbn')!;
|
||||||
|
this.book$ = this.service.getSingle(isbn);
|
||||||
|
}
|
||||||
|
removeBook(isbn: string) {
|
||||||
|
if (window.confirm('Remove book?')) {
|
||||||
|
this.service.remove(isbn).subscribe(() => {
|
||||||
|
this.router.navigateByUrl('/books');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<div *ngIf="book" class="list-item">
|
<a *ngIf="book" [routerLink]="book.isbn" class="list-item">
|
||||||
<img *ngIf="book.thumbnailUrl" [src]="book.thumbnailUrl" alt="Cover" />
|
<img *ngIf="book.thumbnailUrl" [src]="book.thumbnailUrl" alt="Cover" />
|
||||||
<h2>{{ book.tilte }}</h2>
|
<h2>{{ book.title }}</h2>
|
||||||
<p role="doc-subtitle" *ngIf="book.subtilte">
|
<p role="doc-subtitle" *ngIf="book.subtilte">
|
||||||
{{ book.tilte }}
|
{{ book.title }}
|
||||||
</p>
|
</p>
|
||||||
<ul class="comma-list">
|
<ul class="comma-list">
|
||||||
<li *ngFor="let author of book.authors">
|
<li *ngFor="let author of book.authors">
|
||||||
@@ -10,4 +10,4 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div>ISBN {{ book.isbn }}</div>
|
<div>ISBN {{ book.isbn }}</div>
|
||||||
</div>
|
</a>
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
<h1>Books</h1>
|
<h1>Books</h1>
|
||||||
<ul class="book-list">
|
<ul class="book-list" *ngIf="books$ | async as books">
|
||||||
<li *ngFor="let book of books">
|
<li *ngFor="let b of books$ | async">
|
||||||
<bm-book-list-item
|
<bm-book-list-item [book]="b"></bm-book-list-item>
|
||||||
[book]="book"
|
|
||||||
(click)="doSelect(book)"
|
|
||||||
></bm-book-list-item>
|
|
||||||
</li>
|
</li>
|
||||||
<li *ngIf="!books.length">No books available.</li>
|
<li *ngIf="!(books$ | async)?.length">No books available.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Component, EventEmitter, Output } from '@angular/core';
|
import { Component, EventEmitter, Output } from '@angular/core';
|
||||||
import { Book } from 'src/shared/book';
|
import { Book } from 'src/shared/book';
|
||||||
|
import { BookStoreService } from 'src/app/shared/book-store.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'bm-book-list',
|
selector: 'bm-book-list',
|
||||||
@@ -7,30 +9,13 @@ import { Book } from 'src/shared/book';
|
|||||||
styleUrls: ['./book-list.component.css'],
|
styleUrls: ['./book-list.component.css'],
|
||||||
})
|
})
|
||||||
export class BookListComponent {
|
export class BookListComponent {
|
||||||
books: Book[] = [];
|
books$: Observable<Book[]>;
|
||||||
@Output() selectBook = new EventEmitter<Book>();
|
@Output() selectBook = new EventEmitter<Book>();
|
||||||
constructor() {
|
|
||||||
this.books = [
|
constructor(private service: BookStoreService) {
|
||||||
{
|
this.books$ = this.service.getAll();
|
||||||
isbn: '12345',
|
|
||||||
tilte: 'Tierisch gut kochen',
|
|
||||||
authors: ['Mrs Chimp', 'Mr Gorilla'],
|
|
||||||
published: '2022-06-20',
|
|
||||||
subtilte: 'Rezepte von Affe bis Zebre',
|
|
||||||
thumbnailUrl: 'https://cdn.ng-buch.de/kochen.png',
|
|
||||||
description: 'Immer lecker und gut',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isbn: '67890',
|
|
||||||
tilte: 'Backen mit Affen',
|
|
||||||
authors: ['Orang Utan'],
|
|
||||||
published: '2022-07-15',
|
|
||||||
subtilte: 'Bananenbrot und mehr',
|
|
||||||
thumbnailUrl: 'https://cdn.ng-buch.de/backen.png',
|
|
||||||
description: 'Tolle Backtipps für Menschen und Tier',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
doSelect(book: Book) {
|
doSelect(book: Book) {
|
||||||
this.selectBook.emit(book);
|
this.selectBook.emit(book);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { BookDetailsComponent } from './book-details/book-details.component';
|
||||||
|
import { BookListComponent } from './book-list/book-list.component';
|
||||||
|
|
||||||
const routes: Routes = [];
|
const routes: Routes = [
|
||||||
|
{ path: 'books', component: BookListComponent },
|
||||||
|
{ path: 'books/:isbn', component: BookDetailsComponent },
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class BooksRoutingModule {}
|
export class BooksRoutingModule {}
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ import { BookDetailsComponent } from './book-details/book-details.component';
|
|||||||
BookListItemComponent,
|
BookListItemComponent,
|
||||||
],
|
],
|
||||||
imports: [CommonModule, BooksRoutingModule],
|
imports: [CommonModule, BooksRoutingModule],
|
||||||
exports: [BookListComponent, BookDetailsComponent],
|
exports: [],
|
||||||
})
|
})
|
||||||
export class BooksModule {}
|
export class BooksModule {}
|
||||||
|
|||||||
0
src/app/home/home.component.css
Normal file
0
src/app/home/home.component.css
Normal file
3
src/app/home/home.component.html
Normal file
3
src/app/home/home.component.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<h1>Home</h1>
|
||||||
|
|
||||||
|
<a routerLink="/books" class="button red">Show book list</a>
|
||||||
21
src/app/home/home.component.spec.ts
Normal file
21
src/app/home/home.component.spec.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { HomeComponent } from './home.component';
|
||||||
|
|
||||||
|
describe('HomeComponent', () => {
|
||||||
|
let component: HomeComponent;
|
||||||
|
let fixture: ComponentFixture<HomeComponent>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [HomeComponent]
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(HomeComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
10
src/app/home/home.component.ts
Normal file
10
src/app/home/home.component.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'bm-home',
|
||||||
|
templateUrl: './home.component.html',
|
||||||
|
styleUrls: ['./home.component.css']
|
||||||
|
})
|
||||||
|
export class HomeComponent {
|
||||||
|
|
||||||
|
}
|
||||||
16
src/app/shared/book-store.service.spec.ts
Normal file
16
src/app/shared/book-store.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { BookStoreService } from './book-store.service';
|
||||||
|
|
||||||
|
describe('BookStoreService', () => {
|
||||||
|
let service: BookStoreService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(BookStoreService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
25
src/app/shared/book-store.service.ts
Normal file
25
src/app/shared/book-store.service.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Book } from 'src/shared/book';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class BookStoreService {
|
||||||
|
private apiUrl = 'https://api5.angular-buch.com';
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
getAll(): Observable<Book[]> {
|
||||||
|
return this.http.get<Book[]>(`${this.apiUrl}/books`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSingle(isbn: string): Observable<Book> {
|
||||||
|
return this.http.get<Book>(`${this.apiUrl}/books/${isbn}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(isbn: string): Observable<unknown> {
|
||||||
|
return this.http.delete(`${this.apiUrl}/books/${isbn}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export interface Book {
|
export interface Book {
|
||||||
isbn: string;
|
isbn: string;
|
||||||
tilte: string;
|
title: string;
|
||||||
authors: string[];
|
authors: string[];
|
||||||
published?: string;
|
published?: string;
|
||||||
subtilte?: string;
|
subtilte?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user