Differenzansicht 11-reactive-forms
im Vergleich zu 10-template-driven-forms

← Zurück zur Ãœbersicht | Demo | Quelltext auf GitHub
src/app/admin/admin-routing.module.ts CHANGED
@@ -1,6 +1,7 @@
1
  import { NgModule } from '@angular/core';
2
  import { RouterModule, Routes } from '@angular/router';
3
  import { BookCreateComponent } from './book-create/book-create.component';
 
4
 
5
  const routes: Routes = [
6
  {
@@ -10,6 +11,10 @@ const routes: Routes = [
10
  {
11
  path: 'admin/create',
12
  component: BookCreateComponent,
 
 
 
 
13
  }
14
  ];
15
 
1
  import { NgModule } from '@angular/core';
2
  import { RouterModule, Routes } from '@angular/router';
3
  import { BookCreateComponent } from './book-create/book-create.component';
4
+ import { BookEditComponent } from './book-edit/book-edit.component';
5
 
6
  const routes: Routes = [
7
  {
11
  {
12
  path: 'admin/create',
13
  component: BookCreateComponent,
14
+ },
15
+ {
16
+ path: 'admin/edit/:isbn',
17
+ component: BookEditComponent,
18
  }
19
  ];
20
 
src/app/admin/admin.module.ts CHANGED
@@ -1,20 +1,24 @@
1
  import { NgModule } from '@angular/core';
2
  import { CommonModule } from '@angular/common';
3
- import { FormsModule } from '@angular/forms';
 
4
 
5
  import { AdminRoutingModule } from './admin-routing.module';
6
  import { BookFormComponent } from './book-form/book-form.component';
7
  import { BookCreateComponent } from './book-create/book-create.component';
 
8
 
9
  @NgModule({
10
  declarations: [
11
  BookFormComponent,
12
- BookCreateComponent
 
13
  ],
14
  imports: [
15
  CommonModule,
16
  AdminRoutingModule,
17
- FormsModule
 
18
  ],
19
  })
20
  export class AdminModule { }
1
  import { NgModule } from '@angular/core';
2
  import { CommonModule } from '@angular/common';
3
+ import { ReactiveFormsModule } from '@angular/forms';
4
+ import { LocalIsoDateValueAccessor } from 'angular-date-value-accessor';
5
 
6
  import { AdminRoutingModule } from './admin-routing.module';
7
  import { BookFormComponent } from './book-form/book-form.component';
8
  import { BookCreateComponent } from './book-create/book-create.component';
9
+ import { BookEditComponent } from './book-edit/book-edit.component';
10
 
11
  @NgModule({
12
  declarations: [
13
  BookFormComponent,
14
+ BookCreateComponent,
15
+ BookEditComponent
16
  ],
17
  imports: [
18
  CommonModule,
19
  AdminRoutingModule,
20
+ ReactiveFormsModule,
21
+ LocalIsoDateValueAccessor
22
  ],
23
  })
24
  export class AdminModule { }
src/app/admin/book-edit/book-edit.component.html ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <h1>Edit Book</h1>
2
+
3
+ <bm-book-form
4
+ *ngIf="book$ | async as book"
5
+ [book]="book"
6
+ (submitBook)="update($event)"></bm-book-form>
src/app/admin/book-edit/book-edit.component.spec.ts ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { BookEditComponent } from './book-edit.component';
4
+
5
+ describe('BookEditComponent', () => {
6
+ let component: BookEditComponent;
7
+ let fixture: ComponentFixture<BookEditComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ declarations: [ BookEditComponent ]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(BookEditComponent);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
src/app/admin/book-edit/book-edit.component.ts ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component } from '@angular/core';
2
+ import { ActivatedRoute, Router } from '@angular/router';
3
+ import { map, Observable, switchMap } from 'rxjs';
4
+
5
+ import { Book } from '../../shared/book';
6
+ import { BookStoreService } from '../../shared/book-store.service';
7
+
8
+ @Component({
9
+ selector: 'bm-book-edit',
10
+ templateUrl: './book-edit.component.html',
11
+ styleUrls: ['./book-edit.component.css']
12
+ })
13
+ export class BookEditComponent {
14
+ book$: Observable<Book>;
15
+
16
+ constructor(
17
+ private service: BookStoreService,
18
+ private route: ActivatedRoute,
19
+ private router: Router
20
+ ) {
21
+ this.book$ = this.route.paramMap.pipe(
22
+ map(params => params.get('isbn')!),
23
+ switchMap(isbn => this.service.getSingle(isbn))
24
+ );
25
+ }
26
+
27
+ update(book: Book) {
28
+ this.service.update(book).subscribe(updatedBook => {
29
+ this.router.navigate(['/books', updatedBook.isbn]);
30
+ });
31
+ }
32
+ }
src/app/admin/book-form/book-form.component.html CHANGED
@@ -1,26 +1,33 @@
1
- <form (ngSubmit)="submitForm()" #form="ngForm">
2
  <label for="title">Title</label>
3
- <input
4
- name="title"
5
- id="title"
6
- [(ngModel)]="book.title"
7
- required>
8
 
9
  <label for="isbn">ISBN</label>
10
- <input
11
- name="isbn"
12
- id="isbn"
13
- [(ngModel)]="book.isbn"
14
- required
15
- minlength="10"
16
- maxlength="13">
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
- <label for="author">Author</label>
19
- <input
20
- name="author"
21
- id="author"
22
- [(ngModel)]="book.authors[0]"
23
- required>
24
 
25
  <button type="submit" [disabled]="form.invalid">
26
  Save
1
+ <form [formGroup]="form" (ngSubmit)="submitForm()">
2
  <label for="title">Title</label>
3
+ <input id="title" formControlName="title">
4
+
5
+ <label for="subtitle">Subtitle</label>
6
+ <input id="subtitle" formControlName="subtitle">
 
7
 
8
  <label for="isbn">ISBN</label>
9
+ <input id="isbn" formControlName="isbn">
10
+
11
+ <label>Authors</label>
12
+ <button type="button" class="add"
13
+ (click)="addAuthorControl()">
14
+ + Author
15
+ </button>
16
+ <fieldset formArrayName="authors">
17
+ <input
18
+ *ngFor="let a of authors.controls; index as i"
19
+ [attr.aria-label]="'Author ' + i"
20
+ [formControlName]="i">
21
+ </fieldset>
22
+
23
+ <label for="description">Description</label>
24
+ <textarea id="description" formControlName="description"></textarea>
25
+
26
+ <label for="published">Published</label>
27
+ <input type="date" useValueAsLocalIso id="published" formControlName="published">
28
 
29
+ <label for="thumbnailUrl">Thumbnail URL</label>
30
+ <input type="url" id="thumbnailUrl" formControlName="thumbnailUrl">
 
 
 
 
31
 
32
  <button type="submit" [disabled]="form.invalid">
33
  Save
src/app/admin/book-form/book-form.component.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Component, Output, EventEmitter } from '@angular/core';
 
2
 
3
  import { Book } from '../../shared/book';
4
 
@@ -7,16 +8,81 @@ import { Book } from '../../shared/book';
7
  templateUrl: './book-form.component.html',
8
  styleUrls: ['./book-form.component.css']
9
  })
10
- export class BookFormComponent {
11
- book: Book = {
12
- isbn: '',
13
- title: '',
14
- authors: ['']
15
- };
16
-
17
  @Output() submitBook = new EventEmitter<Book>();
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  submitForm() {
20
- this.submitBook.emit(this.book);
 
 
 
 
 
 
 
 
21
  }
22
  }
1
+ import { Component, Output, EventEmitter, Input, OnChanges } from '@angular/core';
2
+ import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
3
 
4
  import { Book } from '../../shared/book';
5
 
8
  templateUrl: './book-form.component.html',
9
  styleUrls: ['./book-form.component.css']
10
  })
11
+ export class BookFormComponent implements OnChanges {
12
+ @Input() book?: Book;
 
 
 
 
 
13
  @Output() submitBook = new EventEmitter<Book>();
14
 
15
+ form = new FormGroup({
16
+ title: new FormControl('', {
17
+ nonNullable: true,
18
+ validators: Validators.required,
19
+ }),
20
+ subtitle: new FormControl('', { nonNullable: true }),
21
+ isbn: new FormControl('', {
22
+ nonNullable: true,
23
+ validators: [
24
+ Validators.required,
25
+ Validators.minLength(10),
26
+ Validators.maxLength(13),
27
+ ]
28
+ }),
29
+ description: new FormControl('', { nonNullable: true }),
30
+ published: new FormControl('', { nonNullable: true }),
31
+ authors: this.buildAuthorsArray(['']),
32
+ thumbnailUrl: new FormControl('', { nonNullable: true })
33
+ });
34
+
35
+ ngOnChanges(): void {
36
+ if (this.book) {
37
+ this.setFormValues(this.book);
38
+ this.setEditMode(true);
39
+ } else {
40
+ this.setEditMode(false);
41
+ }
42
+ }
43
+
44
+ private setFormValues(book: Book) {
45
+ this.form.patchValue(book);
46
+
47
+ this.form.setControl(
48
+ 'authors',
49
+ this.buildAuthorsArray(book.authors)
50
+ );
51
+ }
52
+
53
+ private setEditMode(isEditing: boolean) {
54
+ const isbnControl = this.form.controls.isbn;
55
+
56
+ if (isEditing) {
57
+ isbnControl.disable();
58
+ } else {
59
+ isbnControl.enable();
60
+ }
61
+ }
62
+
63
+ private buildAuthorsArray(authors: string[]) {
64
+ return new FormArray(
65
+ authors.map(v => new FormControl(v, { nonNullable: true }))
66
+ );
67
+ }
68
+
69
+ get authors() {
70
+ return this.form.controls.authors;
71
+ }
72
+
73
+ addAuthorControl() {
74
+ this.authors.push(new FormControl('', { nonNullable: true }));
75
+ }
76
+
77
  submitForm() {
78
+ const formValue = this.form.getRawValue();
79
+ const authors = formValue.authors.filter(author => !!author);
80
+
81
+ const newBook: Book = {
82
+ ...formValue,
83
+ authors
84
+ };
85
+
86
+ this.submitBook.emit(newBook);
87
  }
88
  }
src/app/books/book-details/book-details.component.html CHANGED
@@ -26,4 +26,8 @@
26
  <button class="red" (click)="removeBook(book.isbn)">
27
  Remove book
28
  </button>
 
 
 
 
29
  </div>
26
  <button class="red" (click)="removeBook(book.isbn)">
27
  Remove book
28
  </button>
29
+ <a class="button"
30
+ [routerLink]="['/admin/edit', book.isbn]">
31
+ Edit book
32
+ </a>
33
  </div>
src/app/shared/book-store.service.ts CHANGED
@@ -41,4 +41,11 @@ export class BookStoreService {
41
  create(book: Book): Observable<Book> {
42
  return this.http.post<Book>(`${this.apiUrl}/books`, book);
43
  }
 
 
 
 
 
 
 
44
  }
41
  create(book: Book): Observable<Book> {
42
  return this.http.post<Book>(`${this.apiUrl}/books`, book);
43
  }
44
+
45
+ update(book: Book): Observable<Book> {
46
+ return this.http.put<Book>(
47
+ `${this.apiUrl}/books/${book.isbn}`,
48
+ book
49
+ );
50
+ }
51
  }