BookMonkey 3 Diff

Files changed (11) hide show
  1. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/app-routing.module.ts +10 -0
  2. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/app.component.html +1 -0
  3. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/app.module.ts +12 -2
  4. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/book-form/book-form.component.html +83 -0
  5. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/book-form/book-form.component.ts +29 -0
  6. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/create-book/create-book.component.html +3 -0
  7. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/create-book/create-book.component.ts +29 -0
  8. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/form-messages/form-messages.component.html +4 -0
  9. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/form-messages/form-messages.component.ts +50 -0
  10. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/shared/book-factory.ts +16 -0
  11. tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/shared/book-store.service.ts +10 -0
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/app-routing.module.ts RENAMED
@@ -4,6 +4,7 @@
4
import { HomeComponent } from './home/home.component';
5
import { BookListComponent } from './book-list/book-list.component';
6
import { BookDetailsComponent } from './book-details/book-details.component';
7
8
export const routes: Routes = [
9
{
@@ -22,6 +23,15 @@
22
{
23
path: 'books/:isbn',
24
component: BookDetailsComponent
25
}
26
];
27
4
import { HomeComponent } from './home/home.component';
5
import { BookListComponent } from './book-list/book-list.component';
6
import { BookDetailsComponent } from './book-details/book-details.component';
7
+ import { CreateBookComponent } from './create-book/create-book.component';
8
9
export const routes: Routes = [
10
{
23
{
24
path: 'books/:isbn',
25
component: BookDetailsComponent
26
+ },
27
+ {
28
+ path: 'admin',
29
+ redirectTo: 'admin/create',
30
+ pathMatch: 'full'
31
+ },
32
+ {
33
+ path: 'admin/create',
34
+ component: CreateBookComponent
35
}
36
];
37
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/app.component.html RENAMED
@@ -1,5 +1,6 @@
1
<div class="ui menu">
2
<a routerLink="home" routerLinkActive="active" class="item">Home</a>
3
<a routerLink="books" routerLinkActive="active" class="item">Bücher</a>
4
</div>
5
<router-outlet></router-outlet>
1
<div class="ui menu">
2
<a routerLink="home" routerLinkActive="active" class="item">Home</a>
3
<a routerLink="books" routerLinkActive="active" class="item">Bücher</a>
4
+ <a routerLink="admin" routerLinkActive="active" class="item">Administration</a>
5
</div>
6
<router-outlet></router-outlet>
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/app.module.ts RENAMED
@@ -1,6 +1,8 @@
1
import { CommonModule } from '@angular/common';
2
import { NgModule } from '@angular/core';
3
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
4
5
import { AppRoutingModule } from './app-routing.module.one-app';
6
import { AppComponent } from './app.component';
@@ -10,6 +12,9 @@
10
import { BookDetailsComponent } from './book-details/book-details.component';
11
import { SearchComponent } from './search/search.component';
12
import { TokenInterceptor } from './shared/token-interceptor';
13
14
@NgModule({
15
declarations: [
@@ -18,12 +23,17 @@
18
BookListComponent,
19
BookListItemComponent,
20
BookDetailsComponent,
21
- SearchComponent
22
],
23
imports: [
24
CommonModule,
25
HttpClientModule,
26
- AppRoutingModule
27
],
28
providers: [
29
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
1
import { CommonModule } from '@angular/common';
2
import { NgModule } from '@angular/core';
3
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
4
+ import { FormsModule } from '@angular/forms';
5
+ import { DateValueAccessorModule } from 'angular-date-value-accessor';
6
7
import { AppRoutingModule } from './app-routing.module.one-app';
8
import { AppComponent } from './app.component';
12
import { BookDetailsComponent } from './book-details/book-details.component';
13
import { SearchComponent } from './search/search.component';
14
import { TokenInterceptor } from './shared/token-interceptor';
15
+ import { BookFormComponent } from './book-form/book-form.component';
16
+ import { CreateBookComponent } from './create-book/create-book.component';
17
+ import { FormMessagesComponent } from './form-messages/form-messages.component';
18
19
@NgModule({
20
declarations: [
23
BookListComponent,
24
BookListItemComponent,
25
BookDetailsComponent,
26
+ SearchComponent,
27
+ BookFormComponent,
28
+ CreateBookComponent,
29
+ FormMessagesComponent
30
],
31
imports: [
32
CommonModule,
33
HttpClientModule,
34
+ AppRoutingModule,
35
+ FormsModule,
36
+ DateValueAccessorModule
37
],
38
providers: [
39
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/book-form/book-form.component.html RENAMED
@@ -0,0 +1,83 @@
1
+ <form class="ui form"
2
+ (ngSubmit)="submitForm()"
3
+ #bookForm="ngForm">
4
+
5
+ <label>Buchtitel</label>
6
+ <input
7
+ name="title"
8
+ [(ngModel)]="book.title"
9
+ required
10
+ #titleInput="ngModel">
11
+ <bm-form-messages
12
+ [control]="titleInput"
13
+ controlName="title">
14
+ </bm-form-messages>
15
+
16
+ <label>Untertitel</label>
17
+ <input
18
+ name="subtitle"
19
+ [(ngModel)]="book.subtitle">
20
+
21
+ <label>ISBN</label>
22
+ <input
23
+ name="isbn"
24
+ [(ngModel)]="book.isbn"
25
+ required
26
+ minlength="10"
27
+ maxlength="13"
28
+ #isbnInput="ngModel">
29
+ <bm-form-messages
30
+ [control]="isbnInput"
31
+ controlName="isbn">
32
+ </bm-form-messages>
33
+
34
+ <label>Erscheinungsdatum</label>
35
+ <input
36
+ type="date"
37
+ name="published"
38
+ [(ngModel)]="book.published"
39
+ useValueAsDate
40
+ required
41
+ #dateInput="ngModel">
42
+ <bm-form-messages
43
+ [control]="dateInput"
44
+ controlName="published">
45
+ </bm-form-messages>
46
+
47
+ <label>Autor</label>
48
+ <input
49
+ name="authors"
50
+ [(ngModel)]="book.authors[0]"
51
+ required
52
+ #authorInput="ngModel">
53
+ <bm-form-messages
54
+ [control]="authorInput"
55
+ controlName="authors">
56
+ </bm-form-messages>
57
+
58
+ <label>Beschreibung</label>
59
+ <textarea
60
+ name="description"
61
+ [(ngModel)]="book.description"></textarea>
62
+
63
+ <label>Bild</label>
64
+ <div class="two fields">
65
+ <div class="field">
66
+ <input
67
+ name="url"
68
+ [(ngModel)]="book.thumbnails[0].url"
69
+ placeholder="URL">
70
+ </div>
71
+ <div class="field">
72
+ <input
73
+ name="title"
74
+ [(ngModel)]="book.thumbnails[0].title"
75
+ placeholder="Titel">
76
+ </div>
77
+ </div>
78
+
79
+ <button class="ui button" type="submit"
80
+ [disabled]="bookForm.invalid">
81
+ Speichern
82
+ </button>
83
+ </form>
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/book-form/book-form.component.ts RENAMED
@@ -0,0 +1,29 @@
1
+ import { Component, ViewChild, OnInit, Output, EventEmitter } from '@angular/core';
2
+ import { NgForm } from '@angular/forms';
3
+
4
+ import { Book } from '../shared/book';
5
+ import { BookFactory } from '../shared/book-factory';
6
+
7
+ @Component({
8
+ selector: 'bm-book-form',
9
+ templateUrl: './book-form.component.html',
10
+ styleUrls: ['./book-form.component.css']
11
+ })
12
+ export class BookFormComponent implements OnInit {
13
+
14
+ book = BookFactory.empty();
15
+
16
+ @Output() submitBook = new EventEmitter<Book>();
17
+ @ViewChild('bookForm', { static: true }) bookForm: NgForm;
18
+
19
+
20
+ submitForm() {
21
+ this.submitBook.emit(this.book);
22
+
23
+ this.book = BookFactory.empty();
24
+ this.bookForm.reset();
25
+ }
26
+
27
+ ngOnInit() {
28
+ }
29
+ }
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/create-book/create-book.component.html RENAMED
@@ -0,0 +1,3 @@
1
+ <h1>Buch hinzufügen</h1>
2
+ <bm-book-form (submitBook)="createBook($event)">
3
+ </bm-book-form>
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/create-book/create-book.component.ts RENAMED
@@ -0,0 +1,29 @@
1
+ import { Component, OnInit } from '@angular/core';
2
+ import { ActivatedRoute, Router } from '@angular/router';
3
+
4
+ import { Book } from '../shared/book';
5
+ import { BookStoreService } from '../shared/book-store.service';
6
+
7
+ @Component({
8
+ selector: 'bm-create-book',
9
+ templateUrl: './create-book.component.html',
10
+ styleUrls: ['./create-book.component.css']
11
+ })
12
+ export class CreateBookComponent implements OnInit {
13
+
14
+ constructor(
15
+ private bs: BookStoreService,
16
+ private route: ActivatedRoute,
17
+ private router: Router
18
+ ) { }
19
+
20
+ ngOnInit() {
21
+ }
22
+
23
+ createBook(book: Book) {
24
+ this.bs.create(book).subscribe(() => {
25
+ this.router.navigate(['../..', 'books'], { relativeTo: this.route });
26
+ });
27
+ }
28
+
29
+ }
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/form-messages/form-messages.component.html RENAMED
@@ -0,0 +1,4 @@
1
+ <div class="ui negative message"
2
+ *ngFor="let msg of errorsForControl()">
3
+ {{ msg }}
4
+ </div>
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/form-messages/form-messages.component.ts RENAMED
@@ -0,0 +1,50 @@
1
+ import { Component, OnInit, Input } from '@angular/core';
2
+ import { AbstractControl } from '@angular/forms';
3
+
4
+ @Component({
5
+ selector: 'bm-form-messages',
6
+ templateUrl: './form-messages.component.html',
7
+ styleUrls: ['./form-messages.component.css']
8
+ })
9
+ export class FormMessagesComponent implements OnInit {
10
+
11
+ @Input() control: AbstractControl;
12
+ @Input() controlName: string;
13
+
14
+ private allMessages = {
15
+ title: {
16
+ required: 'Ein Buchtitel muss angegeben werden.'
17
+ },
18
+ isbn: {
19
+ required: 'Es muss eine ISBN angegeben werden.',
20
+ minlength: 'Die ISBN muss mindestens 10 Zeichen haben.',
21
+ maxlength: 'Die ISBN darf höchstens 13 Zeichen haben.'
22
+ },
23
+ published: {
24
+ required: 'Es muss ein Erscheinungsdatum angegeben werden.'
25
+ },
26
+ authors: {
27
+ required: 'Es muss ein Autor angegeben werden.'
28
+ }
29
+ };
30
+
31
+ constructor() { }
32
+
33
+ ngOnInit() {
34
+ }
35
+
36
+ errorsForControl(): string[] {
37
+ const messages = this.allMessages[this.controlName];
38
+
39
+ if (
40
+ !this.control ||
41
+ !this.control.errors ||
42
+ !messages ||
43
+ !this.control.dirty
44
+ ) { return null; }
45
+
46
+ return Object.keys(this.control.errors)
47
+ .map(err => messages[err]);
48
+ }
49
+
50
+ }
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/shared/book-factory.ts RENAMED
@@ -2,6 +2,22 @@
2
import { BookRaw } from './book-raw';
3
4
export class BookFactory {
5
static fromRaw(b: BookRaw): Book {
6
return {
7
...b,
2
import { BookRaw } from './book-raw';
3
4
export class BookFactory {
5
+
6
+ static empty(): Book {
7
+ return {
8
+ isbn: '',
9
+ title: '',
10
+ authors: [''],
11
+ published: new Date(),
12
+ subtitle: '',
13
+ rating: 0,
14
+ thumbnails: [
15
+ { url: '', title: '' }
16
+ ],
17
+ description: ''
18
+ };
19
+ }
20
+
21
static fromRaw(b: BookRaw): Book {
22
return {
23
...b,
tmp/src/app/book-monkey/{iteration-3/interceptors → iteration-4/template-driven-forms}/shared/book-store.service.ts RENAMED
@@ -36,6 +36,16 @@
36
);
37
}
38
39
remove(isbn: string): Observable<any> {
40
return this.http.delete(
41
`${this.api}/book/${isbn}`,
36
);
37
}
38
39
+ create(book: Book): Observable<any> {
40
+ return this.http.post(
41
+ `${this.api}/book`,
42
+ book,
43
+ { responseType: 'text' }
44
+ ).pipe(
45
+ catchError(this.errorHandler)
46
+ );
47
+ }
48
+
49
remove(isbn: string): Observable<any> {
50
return this.http.delete(
51
`${this.api}/book/${isbn}`,