BookMonkey 4 Diff

Files changed (7) hide show
  1. tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/book-form/book-form.component.ts +18 -16
  2. tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/create-book/create-book.component.ts +0 -1
  3. tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/edit-book/edit-book.component.ts +0 -1
  4. tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/form-messages/form-messages.component.ts +3 -3
  5. tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/shared/book-exists-validator.service.ts +25 -0
  6. tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/shared/book-store.service.ts +8 -0
  7. tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/shared/book-validators.ts +31 -0
tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/book-form/book-form.component.ts RENAMED
@@ -1,7 +1,9 @@
1
  import { Component, OnInit, Input, Output, EventEmitter, OnChanges } from '@angular/core';
2
- import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';
3
   
4
  import { Book, Thumbnail } from '../shared/book';
   
   
5
   
6
  @Component({
7
  selector: 'bm-book-form',
@@ -14,28 +16,32 @@
14
   
15
  @Input() book?: Book;
16
   
17
-
18
  @Input() set editing(isEditing: boolean) {
19
  const isbnControl = this.bookForm.get('isbn')!;
20
  if (isEditing) {
   
21
  isbnControl.disable();
22
  } else {
   
23
  isbnControl.enable();
24
  }
25
  };
26
-  
27
  @Output() submitBook = new EventEmitter<Book>();
28
   
29
- constructor(private fb: FormBuilder) {
30
-
   
   
31
  this.bookForm = this.fb.group({
32
  title: ['', Validators.required],
33
  subtitle: [''],
34
- isbn: ['', [
35
- Validators.required,
36
- Validators.minLength(10),
37
- Validators.maxLength(13)
38
- ]],
   
   
39
  description: [''],
40
  authors: this.buildAuthorsArray(['']),
41
  thumbnails: this.buildThumbnailsArray([
@@ -45,11 +51,9 @@
45
  });
46
  }
47
   
48
- ngOnInit(): void {
49
- }
50
   
51
  ngOnChanges() {
52
-  
53
  if (this.book) {
54
  this.setFormValues(this.book);
55
  }
@@ -71,9 +75,8 @@
71
  };
72
  }
73
   
74
-  
75
  private buildAuthorsArray(values: string[]): FormArray {
76
- return this.fb.array(values, Validators.required);
77
  }
78
   
79
  private buildThumbnailsArray(values: Thumbnail[]): FormArray {
@@ -102,7 +105,6 @@
102
   
103
  submitForm() {
104
  const formValue = this.bookForm.value;
105
-
106
  const authors = formValue.authors
107
  .filter((author: string) => author);
108
  const thumbnails = formValue.thumbnails
1
  import { Component, OnInit, Input, Output, EventEmitter, OnChanges } from '@angular/core';
2
+ import { FormBuilder, FormGroup, FormArray, Validators, AbstractControl } from '@angular/forms';
3
   
4
  import { Book, Thumbnail } from '../shared/book';
5
+ import { BookValidators } from '../shared/book-validators';
6
+ import { BookExistsValidatorService } from '../shared/book-exists-validator.service';
7
   
8
  @Component({
9
  selector: 'bm-book-form',
16
   
17
  @Input() book?: Book;
18
   
   
19
  @Input() set editing(isEditing: boolean) {
20
  const isbnControl = this.bookForm.get('isbn')!;
21
  if (isEditing) {
22
+ isbnControl.clearAsyncValidators();
23
  isbnControl.disable();
24
  } else {
25
+ isbnControl.setAsyncValidators(control => this.bookExistsValidator.validate(control));
26
  isbnControl.enable();
27
  }
28
  };
   
29
  @Output() submitBook = new EventEmitter<Book>();
30
   
31
+ constructor(
32
+ private fb: FormBuilder,
33
+ private bookExistsValidator: BookExistsValidatorService
34
+ ) {
35
  this.bookForm = this.fb.group({
36
  title: ['', Validators.required],
37
  subtitle: [''],
38
+ isbn: ['',
39
+ [
40
+ Validators.required,
41
+ BookValidators.isbnFormat
42
+ ],
43
+ (control: AbstractControl) => this.bookExistsValidator.validate(control)
44
+ ],
45
  description: [''],
46
  authors: this.buildAuthorsArray(['']),
47
  thumbnails: this.buildThumbnailsArray([
51
  });
52
  }
53
   
54
+ ngOnInit(): void { }
   
55
   
56
  ngOnChanges() {
   
57
  if (this.book) {
58
  this.setFormValues(this.book);
59
  }
75
  };
76
  }
77
   
   
78
  private buildAuthorsArray(values: string[]): FormArray {
79
+ return this.fb.array(values, BookValidators.atLeastOneAuthor);
80
  }
81
   
82
  private buildThumbnailsArray(values: Thumbnail[]): FormArray {
105
   
106
  submitForm() {
107
  const formValue = this.bookForm.value;
   
108
  const authors = formValue.authors
109
  .filter((author: string) => author);
110
  const thumbnails = formValue.thumbnails
tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/create-book/create-book.component.ts RENAMED
@@ -1,6 +1,5 @@
1
  import { Component, OnInit } from '@angular/core';
2
  import { ActivatedRoute, Router } from '@angular/router';
3
- import { map, switchMap } from 'rxjs/operators';
4
   
5
  import { Book } from '../shared/book';
6
  import { BookStoreService } from '../shared/book-store.service';
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';
tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/edit-book/edit-book.component.ts RENAMED
@@ -21,7 +21,6 @@
21
  ) { }
22
   
23
  ngOnInit(): void {
24
-
25
  this.route.paramMap.pipe(
26
  map(params => params.get('isbn') || ''),
27
  switchMap(isbn => this.bs.getSingle(isbn))
21
  ) { }
22
   
23
  ngOnInit(): void {
   
24
  this.route.paramMap.pipe(
25
  map(params => params.get('isbn') || ''),
26
  switchMap(isbn => this.bs.getSingle(isbn))
tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/form-messages/form-messages.component.ts RENAMED
@@ -17,14 +17,14 @@
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
   
17
  },
18
  isbn: {
19
  required: 'Es muss eine ISBN angegeben werden.',
20
+ isbnFormat: 'Die ISBN muss aus 10 oder 13 Zeichen bestehen.',
21
+ isbnExists: 'Die ISBN existiert bereits.'
22
  },
23
  published: {
24
  required: 'Es muss ein Erscheinungsdatum angegeben werden.'
25
  },
26
  authors: {
27
+ atLeastOneAuthor: 'Es muss ein Autor angegeben werden.'
28
  }
29
  };
30
   
tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/shared/book-exists-validator.service.ts RENAMED
@@ -0,0 +1,25 @@
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
1
+ import { Injectable } from '@angular/core';
2
+ import { AsyncValidator, ValidationErrors, AbstractControl } from '@angular/forms';
3
+ import { Observable, of } from 'rxjs';
4
+ import { map, catchError } from 'rxjs/operators';
5
+  
6
+ import { BookStoreService } from './book-store.service';
7
+  
8
+ @Injectable({
9
+ providedIn: 'root'
10
+ })
11
+ export class BookExistsValidatorService implements AsyncValidator {
12
+  
13
+ constructor(private bs: BookStoreService) { }
14
+  
15
+ validate(
16
+ control: AbstractControl
17
+ ): Observable<ValidationErrors | null> {
18
+ return this.bs.check(control.value).pipe(
19
+ map(exists => (exists === false) ? null : {
20
+ isbnExists: { valid: false }
21
+ }),
22
+ catchError(() => of(null))
23
+ );
24
+ }
25
+ }
tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/shared/book-store.service.ts RENAMED
@@ -77,6 +77,14 @@
77
  );
78
  }
79
   
   
   
   
   
   
   
   
   
80
  private errorHandler(error: HttpErrorResponse): Observable<any> {
81
  console.error('Fehler aufgetreten!');
82
  return throwError(error);
77
  );
78
  }
79
   
80
+ check(isbn: string): Observable<boolean> {
81
+ return this.http.get<boolean>(
82
+ `${this.api}/book/${isbn}/check`
83
+ ).pipe(
84
+ catchError(this.errorHandler)
85
+ );
86
+ }
87
+  
88
  private errorHandler(error: HttpErrorResponse): Observable<any> {
89
  console.error('Fehler aufgetreten!');
90
  return throwError(error);
tmp/src/app/book-monkey/iteration-4/{reactive-forms → custom-validation}/shared/book-validators.ts RENAMED
@@ -0,0 +1,31 @@
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
1
+ import { FormArray, ValidationErrors, AbstractControl } from '@angular/forms';
2
+  
3
+ export class BookValidators {
4
+  
5
+ static isbnFormat(control: AbstractControl): ValidationErrors | null {
6
+ if (!control.value) { return null; }
7
+  
8
+ const numbers = control.value.replace(/-/g, '');
9
+ const isbnPattern = /(^\d{10}$)|(^\d{13}$)/;
10
+  
11
+ if (isbnPattern.test(numbers)) {
12
+ return null;
13
+ } else {
14
+ return {
15
+ isbnFormat: { valid: false }
16
+ };
17
+ }
18
+ }
19
+  
20
+
21
+ static atLeastOneAuthor(controlArray: AbstractControl): ValidationErrors | null {
22
+ if ((controlArray as FormArray).controls.some((el: AbstractControl) => el.value)) {
23
+ return null;
24
+ } else {
25
+ return {
26
+ atLeastOneAuthor: { valid: false }
27
+ };
28
+ }
29
+ }
30
+  
31
+ }