diff --git a/package-lock.json b/package-lock.json index d5a44ed..a9ebf78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@angular/platform-browser": "^16.1.0", "@angular/platform-browser-dynamic": "^16.1.0", "@angular/router": "^16.1.0", + "angular-date-value-accessor": "^3.0.0", "book-monkey5-styles": "^1.0.4", "rxjs": "~7.8.0", "tslib": "^2.3.0", @@ -4562,6 +4563,18 @@ "ajv": "^8.8.2" } }, + "node_modules/angular-date-value-accessor": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/angular-date-value-accessor/-/angular-date-value-accessor-3.0.0.tgz", + "integrity": "sha512-1Oxm2fkKgleIQdZ50BiBSoMz7+qyQLI4rD+qllfJo7gCVIpkXLlWAObFUPVFte+SngdHSycH00bVMb8co8/lhw==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">= 14.0.0", + "@angular/core": ">= 14.0.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", diff --git a/package.json b/package.json index 2cf5e11..09818f5 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@angular/platform-browser": "^16.1.0", "@angular/platform-browser-dynamic": "^16.1.0", "@angular/router": "^16.1.0", + "angular-date-value-accessor": "^3.0.0", "book-monkey5-styles": "^1.0.4", "rxjs": "~7.8.0", "tslib": "^2.3.0", diff --git a/src/app/admin/admin-routing.module.ts b/src/app/admin/admin-routing.module.ts index e49a121..38fdfc3 100644 --- a/src/app/admin/admin-routing.module.ts +++ b/src/app/admin/admin-routing.module.ts @@ -1,10 +1,12 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { BookCreateComponent } from './book-create/book-create.component'; +import { BookEditComponent } from './book-edit/book-edit.component'; const routes: Routes = [ - { path: 'admin', redirectTo: 'admin/create' }, - { path: 'admin/create', component: BookCreateComponent }, + { path: '', redirectTo: 'create', pathMatch: 'full' }, + { path: 'create', component: BookCreateComponent }, + { path: 'edit/:isbn', component: BookEditComponent }, ]; @NgModule({ diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts index 8914a58..a3425d3 100644 --- a/src/app/admin/admin.module.ts +++ b/src/app/admin/admin.module.ts @@ -2,15 +2,20 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AdminRoutingModule } from './admin-routing.module'; -import { FormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; +import { LocalIsoDateValueAccessor } from 'angular-date-value-accessor'; import { BookFormComponent } from './book-form/book-form.component'; import { BookCreateComponent } from './book-create/book-create.component'; +import { BookEditComponent } from './book-edit/book-edit.component'; +import { FormErrorsComponent } from './form-errors/form-errors.component'; @NgModule({ - declarations: [ - BookFormComponent, - BookCreateComponent + declarations: [BookFormComponent, BookCreateComponent, BookEditComponent, FormErrorsComponent], + imports: [ + CommonModule, + AdminRoutingModule, + ReactiveFormsModule, + LocalIsoDateValueAccessor, ], - imports: [CommonModule, AdminRoutingModule, FormsModule], }) export class AdminModule {} diff --git a/src/app/admin/book-edit/book-edit.component.css b/src/app/admin/book-edit/book-edit.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/admin/book-edit/book-edit.component.html b/src/app/admin/book-edit/book-edit.component.html new file mode 100644 index 0000000..b946658 --- /dev/null +++ b/src/app/admin/book-edit/book-edit.component.html @@ -0,0 +1,7 @@ +

Edit Book

+ + diff --git a/src/app/admin/book-edit/book-edit.component.spec.ts b/src/app/admin/book-edit/book-edit.component.spec.ts new file mode 100644 index 0000000..65c7be8 --- /dev/null +++ b/src/app/admin/book-edit/book-edit.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BookEditComponent } from './book-edit.component'; + +describe('BookEditComponent', () => { + let component: BookEditComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [BookEditComponent] + }); + fixture = TestBed.createComponent(BookEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/book-edit/book-edit.component.ts b/src/app/admin/book-edit/book-edit.component.ts new file mode 100644 index 0000000..35f8e6c --- /dev/null +++ b/src/app/admin/book-edit/book-edit.component.ts @@ -0,0 +1,31 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { map, Observable, switchMap } from 'rxjs'; +import { Book } from 'src/shared/book'; +import { BookStoreService } from 'src/app/shared/book-store.service'; + +@Component({ + selector: 'bm-book-edit', + templateUrl: './book-edit.component.html', + styleUrls: ['./book-edit.component.css'], +}) +export class BookEditComponent { + book$: Observable; + + constructor( + private service: BookStoreService, + private route: ActivatedRoute, + private router: Router, + ) { + this.book$ = this.route.paramMap.pipe( + map((params) => params.get('isbn')!), + switchMap((isbn) => this.service.getSingle(isbn)), + ); + } + + update(book: Book) { + this.service.update(book).subscribe((updatedBook) => { + this.router.navigate(['/books', updatedBook.isbn]); + }); + } +} diff --git a/src/app/admin/book-form/book-form.component.html b/src/app/admin/book-form/book-form.component.html index b084601..c6f6c07 100644 --- a/src/app/admin/book-form/book-form.component.html +++ b/src/app/admin/book-form/book-form.component.html @@ -1,19 +1,56 @@ -
+ - + + + + + + + + + + +
+ +
+ + + + + + - - + +
+ + diff --git a/src/app/admin/book-form/book-form.component.ts b/src/app/admin/book-form/book-form.component.ts index 25afafa..c69d558 100644 --- a/src/app/admin/book-form/book-form.component.ts +++ b/src/app/admin/book-form/book-form.component.ts @@ -1,21 +1,94 @@ -import { Component, Output, EventEmitter } from '@angular/core'; +import { + Component, + Output, + EventEmitter, + OnChanges, + Input, + inject, +} from '@angular/core'; import { Book } from 'src/shared/book'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { FormArray } from '@angular/forms'; +import { atLeastOneValue, isbnFormat } from '../shared/validators'; +import { AsyncValidatorsService } from '../shared/async-validators.service'; @Component({ selector: 'bm-book-form', templateUrl: './book-form.component.html', styleUrls: ['./book-form.component.css'], }) -export class BookFormComponent { +export class BookFormComponent implements OnChanges { @Output() submitBook = new EventEmitter(); + @Input() book?: Book; + + form = new FormGroup({ + title: new FormControl('', { + nonNullable: true, + validators: Validators.required, + }), + subtitle: new FormControl('', { nonNullable: true }), + isbn: new FormControl('', { + nonNullable: true, + validators: [Validators.required, isbnFormat], + asyncValidators: inject(AsyncValidatorsService).isbnExists(), + }), + description: new FormControl('', { nonNullable: true }), + published: new FormControl('', { nonNullable: true }), + thumbnailUrl: new FormControl('', { nonNullable: true }), + authors: this.buildAuthorsArray(['']), + }); submitForm() { - this.submitBook.emit(this.book); + const formValue = this.form.getRawValue(); + const authors = formValue.authors.filter((author) => !!author); + const newBook: Book = { + ...formValue, + authors, // TODO:echte Eingabe + }; + this.submitBook.emit(newBook); } - book: Book = { - isbn: '', - title: '', - authors: [''], - }; + // book: Book = { + // isbn: '', + // title: '', + // authors: [''], + // }; + + get authors() { + return this.form.controls.authors; + } + + addAuthorControl() { + this.authors.push(new FormControl('', { nonNullable: true })); + } + + private buildAuthorsArray(authors: string[]) { + return new FormArray( + authors.map((v) => new FormControl(v, { nonNullable: true })), + atLeastOneValue, + ); + } + + ngOnChanges(): void { + if (this.book) { + this.setFormValues(this.book); + this.setEditMode(true); + } else { + this.setEditMode(false); + } + } + + private setEditMode(isEditing: boolean) { + const isbnControle = this.form.controls.isbn; + if (isEditing) { + isbnControle.disable(); + } else { + isbnControle.enable(); + } + } + + private setFormValues(book: Book) { + this.form.patchValue(book); + this.form.setControl('authors', this.buildAuthorsArray(book.authors)); + } } diff --git a/src/app/admin/form-errors/form-errors.component.css b/src/app/admin/form-errors/form-errors.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/admin/form-errors/form-errors.component.html b/src/app/admin/form-errors/form-errors.component.html new file mode 100644 index 0000000..308f3e0 --- /dev/null +++ b/src/app/admin/form-errors/form-errors.component.html @@ -0,0 +1 @@ +

{{ error }}

diff --git a/src/app/admin/form-errors/form-errors.component.spec.ts b/src/app/admin/form-errors/form-errors.component.spec.ts new file mode 100644 index 0000000..c337c3f --- /dev/null +++ b/src/app/admin/form-errors/form-errors.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FormErrorsComponent } from './form-errors.component'; + +describe('FormErrorsComponent', () => { + let component: FormErrorsComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [FormErrorsComponent] + }); + fixture = TestBed.createComponent(FormErrorsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/form-errors/form-errors.component.ts b/src/app/admin/form-errors/form-errors.component.ts new file mode 100644 index 0000000..ba808d0 --- /dev/null +++ b/src/app/admin/form-errors/form-errors.component.ts @@ -0,0 +1,30 @@ +import { Component, Input } from '@angular/core'; +import { FormGroupDirective } from '@angular/forms'; + +@Component({ + selector: 'bm-form-errors', + templateUrl: './form-errors.component.html', + styleUrls: ['./form-errors.component.css'], +}) +export class FormErrorsComponent { + @Input() controlName?: string; + @Input() messages: { [errorCode: string]: string } = {}; + + constructor(private form: FormGroupDirective) {} + + get errors(): string[] { + if (!this.controlName) { + return []; + } + + const control = this.form.control.get(this.controlName); + + if (!control || !control.errors || !control.touched) { + return []; + } + + return Object.keys(control.errors).map((errorCode) => { + return this.messages[errorCode]; + }); + } +} diff --git a/src/app/admin/shared/async-validators.service.spec.ts b/src/app/admin/shared/async-validators.service.spec.ts new file mode 100644 index 0000000..9f280e1 --- /dev/null +++ b/src/app/admin/shared/async-validators.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AsyncValidatorsService } from './async-validators.service'; + +describe('AsyncValidatorsService', () => { + let service: AsyncValidatorsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AsyncValidatorsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/admin/shared/async-validators.service.ts b/src/app/admin/shared/async-validators.service.ts new file mode 100644 index 0000000..60615b1 --- /dev/null +++ b/src/app/admin/shared/async-validators.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import { AsyncValidatorFn } from '@angular/forms'; +import { map } from 'rxjs'; +import { BookStoreService } from 'src/app/shared/book-store.service'; + +@Injectable({ + providedIn: 'root', +}) +export class AsyncValidatorsService { + constructor(private service: BookStoreService) {} + + isbnExists(): AsyncValidatorFn { + return (control) => { + return this.service + .check(control.value) + .pipe(map((exists) => (exists ? { isbnexists: true } : null))); + }; + } +} diff --git a/src/app/admin/shared/validators.ts b/src/app/admin/shared/validators.ts new file mode 100644 index 0000000..08f36b1 --- /dev/null +++ b/src/app/admin/shared/validators.ts @@ -0,0 +1,26 @@ +import { isFormArray, ValidatorFn } from '@angular/forms'; + +export const atLeastOneValue: ValidatorFn = function (control) { + if (!isFormArray(control)) { + return null; + } + if (control.controls.some((el) => !!el.value)) { + return null; + } else { + return { atleastonevalue: true }; + } +}; + +export const isbnFormat: ValidatorFn = function (control) { + if (!control.value || typeof control.value !== 'string') { + return null; + } + const isbnWithoutDashes = control.value.replace(/-/g, ''); + const length = isbnWithoutDashes.length; + + if (length === 10 || length === 13) { + return null; + } else { + return { isbnformat: true }; + } +}; diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 7506485..a673e6f 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,10 +1,22 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HomeComponent } from './home/home.component'; +import { authGuard } from './shared/auth.guard'; const routes: Routes = [ { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'home', component: HomeComponent }, + { + path: 'books', + loadChildren: () => + import('./books/books.module').then((m) => m.BooksModule), + }, + { + path: 'admin', + loadChildren: () => + import('./admin/admin.module').then((m) => m.AdminModule), + canActivate: [authGuard], + }, ]; @NgModule({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 450cbff..c696cf2 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -3,16 +3,14 @@ import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; -import { BooksModule } from './books/books.module'; import { HomeComponent } from './home/home.component'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { SearchComponent } from './search/search.component'; import { AuthInterceptor } from './shared/auth.interceptor'; -import { AdminModule } from './admin/admin.module'; @NgModule({ declarations: [AppComponent, HomeComponent, SearchComponent], - imports: [BrowserModule, AppRoutingModule, BooksModule, HttpClientModule, AdminModule], + imports: [BrowserModule, AppRoutingModule, HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, ], diff --git a/src/app/books/book-details/book-details.component.html b/src/app/books/book-details/book-details.component.html index 33a4ea3..b9ddbda 100644 --- a/src/app/books/book-details/book-details.component.html +++ b/src/app/books/book-details/book-details.component.html @@ -1,6 +1,7 @@

{{ book.title }}

{{ book.subtilte }}

+

Authors

@@ -8,18 +9,30 @@
  • {{ author }}
  • +

    ISBN

    - {{ book.isbn }} + {{ book.isbn | isbn }}

    Published

    - {{ book.published }} + {{ book.published | date: "longDate" }}
    +

    Description

    {{ book.description }}

    Cover Back to list - + + + + Edit book +
    diff --git a/src/app/books/book-details/book-details.component.ts b/src/app/books/book-details/book-details.component.ts index b30955a..e2e6c8e 100644 --- a/src/app/books/book-details/book-details.component.ts +++ b/src/app/books/book-details/book-details.component.ts @@ -21,10 +21,8 @@ export class BookDetailsComponent { this.book$ = this.service.getSingle(isbn); } removeBook(isbn: string) { - if (window.confirm('Remove book?')) { - this.service.remove(isbn).subscribe(() => { - this.router.navigateByUrl('/books'); - }); - } + this.service.remove(isbn).subscribe(() => { + this.router.navigateByUrl('/books'); + }); } } diff --git a/src/app/books/book-list-item/book-list-item.component.html b/src/app/books/book-list-item/book-list-item.component.html index 6fae28b..cd379a5 100644 --- a/src/app/books/book-list-item/book-list-item.component.html +++ b/src/app/books/book-list-item/book-list-item.component.html @@ -9,5 +9,5 @@ {{ author }} -
    ISBN {{ book.isbn }}
    +
    ISBN {{ book.isbn | isbn }}
    diff --git a/src/app/books/books-routing.module.ts b/src/app/books/books-routing.module.ts index feacd0d..f98a14e 100644 --- a/src/app/books/books-routing.module.ts +++ b/src/app/books/books-routing.module.ts @@ -4,8 +4,8 @@ import { BookDetailsComponent } from './book-details/book-details.component'; import { BookListComponent } from './book-list/book-list.component'; const routes: Routes = [ - { path: 'books', component: BookListComponent }, - { path: 'books/:isbn', component: BookDetailsComponent }, + { path: '', component: BookListComponent }, + { path: ':isbn', component: BookDetailsComponent }, ]; @NgModule({ diff --git a/src/app/books/books.module.ts b/src/app/books/books.module.ts index e27a098..71d6711 100644 --- a/src/app/books/books.module.ts +++ b/src/app/books/books.module.ts @@ -5,12 +5,18 @@ import { BooksRoutingModule } from './books-routing.module'; import { BookListComponent } from './book-list/book-list.component'; import { BookListItemComponent } from './book-list-item/book-list-item.component'; import { BookDetailsComponent } from './book-details/book-details.component'; +import { IsbnPipe } from './shared/isbn.pipe'; +import { ConfirmDirective } from './shared/confirm.directive'; +import { LoggedinOnlyDirective } from './shared/loggedin-only.directive'; @NgModule({ declarations: [ BookDetailsComponent, BookListComponent, BookListItemComponent, + IsbnPipe, + ConfirmDirective, + LoggedinOnlyDirective, ], imports: [CommonModule, BooksRoutingModule], exports: [], diff --git a/src/app/books/shared/confirm.directive.spec.ts b/src/app/books/shared/confirm.directive.spec.ts new file mode 100644 index 0000000..665f262 --- /dev/null +++ b/src/app/books/shared/confirm.directive.spec.ts @@ -0,0 +1,8 @@ +import { ConfirmDirective } from './confirm.directive'; + +describe('ConfirmDirective', () => { + it('should create an instance', () => { + const directive = new ConfirmDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/books/shared/confirm.directive.ts b/src/app/books/shared/confirm.directive.ts new file mode 100644 index 0000000..1472e3f --- /dev/null +++ b/src/app/books/shared/confirm.directive.ts @@ -0,0 +1,21 @@ +import { + Directive, + Input, + Output, + EventEmitter, + HostListener, +} from '@angular/core'; + +@Directive({ + selector: '[bmConfirm]', +}) +export class ConfirmDirective { + @Input('bmConform') confirmText?: string; + @Output() confirm = new EventEmitter(); + @HostListener('click') onClick() { + if (window.confirm(this.confirmText)) { + this.confirm.emit(); + } + } + constructor() {} +} diff --git a/src/app/books/shared/isbn.pipe.spec.ts b/src/app/books/shared/isbn.pipe.spec.ts new file mode 100644 index 0000000..54fdf72 --- /dev/null +++ b/src/app/books/shared/isbn.pipe.spec.ts @@ -0,0 +1,8 @@ +import { IsbnPipe } from './isbn.pipe'; + +describe('IsbnPipe', () => { + it('create an instance', () => { + const pipe = new IsbnPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/books/shared/isbn.pipe.ts b/src/app/books/shared/isbn.pipe.ts new file mode 100644 index 0000000..71aa825 --- /dev/null +++ b/src/app/books/shared/isbn.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'isbn', +}) +export class IsbnPipe implements PipeTransform { + transform(value: string): string { + if (!value) { + return ''; + } + return `${value.substring(0, 3)}-${value.substring(3)}`; + } +} diff --git a/src/app/books/shared/loggedin-only.directive.spec.ts b/src/app/books/shared/loggedin-only.directive.spec.ts new file mode 100644 index 0000000..8c444dc --- /dev/null +++ b/src/app/books/shared/loggedin-only.directive.spec.ts @@ -0,0 +1,8 @@ +import { LoggedinOnlyDirective } from './loggedin-only.directive'; + +describe('LoggedinOnlyDirective', () => { + it('should create an instance', () => { + const directive = new LoggedinOnlyDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/books/shared/loggedin-only.directive.ts b/src/app/books/shared/loggedin-only.directive.ts new file mode 100644 index 0000000..88ea715 --- /dev/null +++ b/src/app/books/shared/loggedin-only.directive.ts @@ -0,0 +1,34 @@ +import { + Directive, + OnDestroy, + ViewContainerRef, + TemplateRef, +} from '@angular/core'; +import { Subject, takeUntil } from 'rxjs'; +import { AuthService } from 'src/app/shared/auth.service'; + +@Directive({ + selector: '[bmLoggedinOnly]', +}) +export class LoggedinOnlyDirective implements OnDestroy { + private destroy$ = new Subject(); + + constructor( + private template: TemplateRef, + private viewContainer: ViewContainerRef, + private authService: AuthService, + ) { + this.authService.isAuthenticated$ + .pipe(takeUntil(this.destroy$)) + .subscribe((isAuthenticated) => { + if (isAuthenticated) { + this.viewContainer.createEmbeddedView(this.template); + } else { + this.viewContainer.clear(); + } + }); + } + ngOnDestroy(): void { + this.destroy$.next(); + } +} diff --git a/src/app/shared/auth.guard.spec.ts b/src/app/shared/auth.guard.spec.ts new file mode 100644 index 0000000..4ae275e --- /dev/null +++ b/src/app/shared/auth.guard.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { CanActivateFn } from '@angular/router'; + +import { authGuard } from './auth.guard'; + +describe('authGuard', () => { + const executeGuard: CanActivateFn = (...guardParameters) => + TestBed.runInInjectionContext(() => authGuard(...guardParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(executeGuard).toBeTruthy(); + }); +}); diff --git a/src/app/shared/auth.guard.ts b/src/app/shared/auth.guard.ts new file mode 100644 index 0000000..87cebb7 --- /dev/null +++ b/src/app/shared/auth.guard.ts @@ -0,0 +1,21 @@ +import { Inject, inject } from '@angular/core'; +import { CanActivateFn, Router } from '@angular/router'; +import { AuthService } from './auth.service'; +import { map, take } from 'rxjs'; + +export const authGuard: CanActivateFn = (route, state) => { + const authService = inject(AuthService); + const router = inject(Router); + + return authService.isAuthenticated$.pipe( + take(1), + map((isAuthenticated) => { + if (authService.isAuthenticated) { + return true; + } else { + window.alert('Not logged in!'); + return router.parseUrl('home'); + } + }), + ); +}; diff --git a/src/app/shared/book-store.service.ts b/src/app/shared/book-store.service.ts index f42e72c..da89a8c 100644 --- a/src/app/shared/book-store.service.ts +++ b/src/app/shared/book-store.service.ts @@ -40,4 +40,12 @@ export class BookStoreService { create(book: Book): Observable { return this.http.post(`${this.apiUrl}/books`, book); } + + update(book: Book): Observable { + return this.http.put(`${this.apiUrl}/book/${book.isbn}`, book); + } + + check(isbn: string): Observable { + return this.http.get(`${this.apiUrl}/books/${isbn}/check`); + } }