template-driven (TD) vs. reactive approach
template-driven - Angular infers the form object from the DOM
reactive approach - form is created programmatically and synchronized with the DOM
Top
Index
TD : submitting and using the form
event handler takes NgForm object as arg
import { Component, ElementRef } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
...
})
export class AppComponent {
...
onSubmit(form: NgForm) {
console.log(form);
}
}
bind ngSubmit to event handler and provide alias
set container with class
form-group
in the onSubmit argument the data object is form.value
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
<div id="user-data">
<div class="form-group">
<label for="username">Username</label>
<input
type="text"
id="username"
class="form-control"
ngModel
name="username">
</div>
...
<button class="btn btn-primary" type="submit">Submit</button>
</form>
Top
Index
TD : accessing the form with @ViewChild
unlike the markup above the snippet below uses no argument with the onSubmit event
handler
<form (ngSubmit)="onSubmit()" #f="ngForm">
...
</form>
in app.component import ViewChild add property of type NgForm and decorate with
ViewChild method
the arg for ViewChild should be the local reference declared in the markup
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
...
})
export class AppComponent {
@ViewChild('f') signupForm: NgForm;
...
onSubmit() {
console.log(this.signupForm);
}
}
Top
Index
built-in validators to check user input
note required directive to the input along with the email directive to the email
input (built-in validator)
the email validator is not very robust, it finds a@a to be a valid email address
when submitted check the valid property of the form and its controls
<form (ngSubmit)="onSubmit()" #f="ngForm">
<div id="user-data">
...
<div class="form-group">
<label for="email">Mail</label>
<input type="email" id="email" class="form-control" ngModel name="email" required email>
</div>
</div>
...
<button class="btn btn-primary" type="submit">Submit</button>
</form>
Top
Index
TD : using the form state
add the disabled property to button in app.component markup and set the property
using the local reference f
<form (ngSubmit)="onSubmit()" #f="ngForm">
...
<button class="btn btn-primary" type="submit" [disabled]="!f.valid">Submit</button>
</form>
add a CSS class to put a red border around the input and select controls when the
control is not valid but has been touched
input.ng-invalid.ng-touched, select.ng-invalid.ng-touched {
border: 1px solid red;
}
Top
Index
TD : outputting validation state errors
add local references to the input and select elements using ngModel
add spans containing validation messages using ngIf directives
<form (ngSubmit)="onSubmit()" #f="ngForm">
<div id="user-data">
...
<div class="form-group">
<label for="email">Mail</label>
<input type="email" id="email" class="form-control" ngModel name="email" required email #email="ngModel">
<span class="help-block" *ngIf="!email.valid && email.touched">Please enter a valid email.</span>
</div>
</div>
...
<button class="btn btn-primary" type="submit" [disabled]="!f.valid">Submit</button>
</form>
Top
Index
TD : set default values with ngModel property binding
set a property to the desired default
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
...
})
export class AppComponent {
@ViewChild('f') signupForm: NgForm;
defaultQuestion = 'pet';
...
}
in the markup assign the property to the control's value property
<form (ngSubmit)="onSubmit()" #f="ngForm">
...
<div class="form-group">
<label for="secret">Secret Questions</label>
<select id="secret" class="form-control" [ngModel]="defaultQuestion" name="secret" required #secret="ngModel">
<option value="pet">Your first Pet?</option>
<option value="teacher">Your first teacher?</option>
</select>
<span class="help-block" *ngIf="!secret.valid && secret.touched">Please select a secret question.</span>
</div>
<button class="btn btn-primary" type="submit" [disabled]="!f.valid">Submit</button>
</form>
Top
Index
TD : using ngModel with two-way binding
add answer property
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
...
})
export class AppComponent {
@ViewChild('f') signupForm: NgForm;
defaultQuestion = 'pet';
answer = '';
...
}
use two-way binding with ngModel to bind the new property to the input control
use string interpolation of the new property to display the property's value
<div class="form-group">
<textarea name="questionAnser" rows='3' [(ngModel)]='answer' class='form-control'></textarea>
<p>Your reply : {{ answer }}</p>
</div>
ngModel can be used with
- no binding, used to inform Angular the input is a control
- one-way binding to preload controls
- two-way binding as shown above
Top
Index
TD : grouping form controls
group the data returned in the form object
ngModelGroup directive and a local reference in the user-data div containing the
username and email divs
add a conditional error message at the bottom of the div
<form (ngSubmit)="onSubmit()" #f="ngForm">
<div id="user-data" ngModelGroup="userData" #userData="ngModelGroup">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" class="form-control" ngModel name="username" required #username="ngModel">
<span class="help-block" *ngIf="!username.valid && username.touched">Please enter a username.</span>
</div>
<button class="btn btn-default" type="button">Suggest an Username</button>
<div class="form-group">
<label for="email">Mail</label>
<input type="email" id="email" class="form-control" ngModel name="email" required email #email="ngModel">
<span class="help-block" *ngIf="!email.valid && email.touched">Please enter a valid email.</span>
</div>
<p *ngIf="!userData.valid && userData.touched">User data is invalid.</p>
</div>
...
</form>
Top
Index
TD : handling radio buttons
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
...
})
export class AppComponent {
...
genders = ['male', 'female'];
answer = '';
...
}
ngFor used to present the list
<form (ngSubmit)="onSubmit()" #f="ngForm">
...;
<div class="form-group">
...
<div class="radio" *ngFor="let gender of genders">
<label>
<input type="radio" name="gender" ngModel [value]="gender" required>
{{ gender }}
</label>
</div>
</div>
<button class="btn btn-primary" type="submit" [disabled]="!f.valid">Submit</button>
</form>
Top
Index
add a property as a JSON object containing the same values as the form
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
...
})
export class AppComponent {
...
user = {
username: '',
mail: '',
secretQuestion: '',
answer: '',
gender: ''
}
submitted = false;
...
onSubmit() {
this.submitted = true;
this.user.username = this.signupForm.value.userData.username;
this.user.mail = this.signupForm.value.userData.email;
this.user.secretQuestion = this.signupForm.value.secret;
this.user.answer = this.signupForm.value.questionAnswer;
this.user.gender = this.signupForm.value.gender;
}
}
Top
Index
to reset a form
onSubmit() {
this.submitted = true;
...
this.user.gender = this.signupForm.value.gender;
this.signupForm.reset();
}
Top
Index