Handling Forms in Angular Apps
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

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

TD : using form data
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

TD : resetting forms
to reset a form
      onSubmit() {
        this.submitted = true;
        ...
        this.user.gender = this.signupForm.value.gender;

        this.signupForm.reset();
      } 
        

Top

Index

n4jvp.com