Angular Model-driven Forms Reactive Forms Forms
▌Introduction
We learned how
to use Template
driven Forms to design our form quickly. However if there are dynamic fields
or rows in the form, it is recommended to use Reactive
Forms (Model driven Forms) to implement.
▌Environment
▋Angular 2.4.0
▋Angular CLI
1.0.0-beta.31
▌Implement
▋Import ReactiveFormModule
▋ app.module.ts
import {
ReactiveFormsModule, FormsModule } from '@angular/forms';
@NgModule({
imports: [
//...
FormsModule,
ReactiveFormsModule
],
//...
})
|
▋Create Form Model
In Reactive Forms, we could design our model with
FormControl, FormGroup and FormArray.
l FormGroup:
Aggregates the values of each child FormControl
into one object.
l FormControl:
Tracks the value and validation status of an individual form control.
l FormArray:
Could contains one or more FormGroup
or/and FormControl.
Let’s have a look at the following form,
The sample shows that we could insert a new row with
fields by clicking [New] button.
So the form model should be designed like this,
So the form model should be designed like this,
▋reactive.component.ts
import { AbstractControl,
FormGroup, FormArray, FormControl, FormBuilder, Validators, NgForm } from '@angular/forms';
@Component({
selector: 'reactive-form',
templateUrl: './reactive-form.component.html',
})
export class ReactiveFormComponent implements OnInit {
private form: FormGroup;
constructor(
private fb: FormBuilder) {
//Set the model-driven with default data
this.form = this.fb.group({
user:
this.fb.control('JB',
[Validators.required]),
links: this.fb.array([
this.fb.group({
'title': ['github', [Validators.required, Validators.minLength(30)]],
'photo': ['assets/images/Links/github.png'],
'uri': ['www.github.com', Validators.required],
'target': [null, Validators.required]
})
])
})
}
}
|
▋reactive.component.html
<form [formGroup]="form" novalidate (ngSubmit)="onSubmit(form)">
<span>Current User</span>
<input type="text" class="form-control" formControlName="user" />
<div formArrayName="links">
<table class="table bordered">
<thead>
<tr>
<th>Image</th>
<th>Title/th>
<th>Uri</th>
<th>Window Open</th>
<th></th>
</tr>
</thead>
<tbody>
<tr [formGroupName]="i" *ngFor="let link of form.controls.links.controls; let i
= index">
<td>
<img *ngIf="link.controls.photo.value" style="width:38px;height:19px;" src="{{link.controls.photo.value}}" />
<input type="file" class="form-control" style="max-width: 200px"></td>
</td>
<td>
<input type="text" class="form-control" formControlName="title" />
</td>
<td>
<input type="text" class="form-control" formControlName="uri" />
</td>
<td>
<select class="form-control" formControlName="target">
<option *ngFor="let lt of linkTargets" [ngValue]="lt">{{lt.name}}</option>
</select>
</td>
<td>
<input type="button" class="btn btn-danger" (click)="removeLink(i)" value="刪除" />
</td>
</tr>
</tbody>
</table>
</div>
</form>
|
Notice:
u On the form element, use [formGroup]="form" to bind the FormGroup on it.
u Use formGroupName,
formControlName,
formArrayName
to map our Form Model.
u We can get
the FormControl object in a FormGroup by this way,
[FormGroup object].controls.[FormControl name]
[FormGroup object].controls.[FormControl name]
Result:
▋Initialize Form Model
Now we will use the ajax call to get the data and fill
the Form Model with the them.
▋Initialize Form
constructor(
private fb:
FormBuilder) {
// initialize form
this.form = new FormGroup({
links: new FormArray([])
});
}
|
PS. From now on, we will skip
and remove the FormControl: user, which was used for demo the structure in the
previous sample.
▋Initialize Form Model
private initFormGroup() {
let links: FormArray = <FormArray>this.form.controls['links'];
let
linkObservable$ = this.linkSvc.getAll();
//Subscribe
the observer to linkObservable$
linkObservable$.subscribe((data: Link[]) => {
//Push
FormGroup to FormArray
data.forEach((lk: Link) => {
links.push(this.fb.group({
'title': [lk.title,
[Validators.required, Validators.maxLength(30)]],
'photo': [lk.photo],
'uri': [lk.uri,
[HttpsValidator, Validators.required]],
'target': [lk.target,
Validators.required]
}));
})
});
}
|
Result:
▋Append/Remove FormGroup in FormArray
We will complete the [New] and
[Remove] functions.
u New: Inserting a new inputs row. In
other words, we are going to append a new FormGroup
to the FormArray.
u Remove: Remove a FormGroup from
the FormArray.
▋TS
//Create a link
private insertLink() {
let linkArry: FormArray =
<FormArray>this.form.controls['links'];
linkArry.insert(0, this.fb.group({
'title': ['', [Validators.required,
Validators.minLength(30)]],
'photo': [''],
'uri': ['',
Validators.required],
'target': [null, Validators.required]
}));
}
//Remove a link
private removeLink(index:
number) {
let linkArry: FormArray =
<FormArray>this.form.controls['links'];
linkArry.removeAt(index);
}
|
▋Demo
▋Form Validation
The last st Of course we can add
some Form validations on Reactive Form just like Template-driven Form.
<tr [formGroupName]="i" *ngFor="let link of
form.controls.links.controls; let i = index">
<td [ngClass]="{'has-error':
link.controls.title.invalid}">
<input type="text" class="form-control" formControlName="title" />
<span class="label label-danger" *ngIf="link.controls.uri.dirty &&
link.controls.uri.invalid">Title is
required!</span>
</td>
<!-- ... -->
|
▋Submit
The last step, we are going
to make sure that we can get the Reactive Form’s submit data correctly.
▋HTML
<form [formGroup]="form" novalidate (ngSubmit)="onSubmit(form)">
<!-- ... -->
</form>
|
▋TS
private onSubmit(form:
NgForm) {
if (form &&
form.value) {
console.log(form.value;
}
}
|
Result:
▌Summary
Both Template
driven Forms and Reactive
Forms are useful in Angular. If we want more flexibility on the form, use Reactive
Forms. But for static form, I suggest using Template
driven Forms for quick implement.
▌Reference
Reactive Forms Model Host Bet driven Forms are recommended for dynamic fields or rows, offering better control and flexibility than Template-driven Forms.
回覆刪除