Jerry's Blog

Recording what I learned everyday

View on GitHub


29 June 2019

Angular (16) -- Template Driven Forms

by Jerry Zhang

Day 24: Template Driven Forms

Register the Form controls

To make Angular take over a form element, e.g. an Input, we need to add ngModel and a name for this input. Then, this part of the form is under control of Angular.

<input
  type="text"
  id="username"
  class="form-control"
  ngModel
  name="username"
>

Make the form submittable

Use the (ngSubmit) directive on the form tag, instead of some buttons.

<form (ngSubmit)="onSubmit()">

To get the content in the form that we submit, we need to tag this form as ngForm and pass it to the onSubmit() method.

<form (ngSubmit)="onSubmit(f)" #f="ngForm">
onSubmit(form: NgForm) {
    console.log(form);
  }

Then, if we open the console, we can see that there are many properties in the submitted NgForm Object, including the value.

Accessing the Form with @ViewChild

We do not have to pass the f in the method. Instead, we can use ViewChild.

<form (ngSubmit)="onSubmit()" #f="ngForm">
export class AppComponent {
  @ViewChild('f', { static: false }) signUpForm: NgForm;

  // onSubmit(form: NgForm) {
  //   console.log(form);
  // }

  onSubmit() {
    console.log(this.signUpForm);
  }
}

This gives the access to the very same form without passing it to onSubmit. This is useful if you need to access the form before submitting it.

Input Validation

<input
  type="text"
  id="username"
  class="form-control"
  ngModel
  name="username"
  required
>

Then when we submit it, we can see in the console that the valid is true only this field is not empty. It tracks this on a per-control level, as well as the form level.

<input
  type="email"
  id="email"
  class="form-control"
  ngModel
  name="email"
  required
  email
  #email="ngModel"
>

Additionally, you might also want to enable HTML5 validation (by default, Angular disables it). You can do so by adding the ngNativeValidate to a control in your template.

<button
  class="btn btn-primary"
  type="submit"
  [disabled]="!f.valid">Submit</button>
input.ng-invalid.ng-touched {
  border: 1px solid red;
}
<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>

Here, we get a reference of the email input.

Default Values with ngModel

Change ngModel to a property binding, and set value for it.

<select
id="secret"
class="form-control"
[ngModel]="defaultQuestion"
name="secret"
>
<option value="pet">Your first Pet?</option>
<option value="teacher">Your first teacher?</option>
</select>
defaultQuestion = 'pet';

Two-way-binding ngModel

<div class="form-group">
  <textarea
    name="questionAnswer"
    rows="3"
    class="form-control"
    [(ngModel)]="answer"
  ></textarea>
</div>
<p>Your reply: </p>

Grouping Form Controls with ngModelGroup

We add a wrapping div on the elements that we want to group together, and add ngModelGroup directive.

ngModelGroup needs to be set up as a string, for example, “userData”. This will be the key name for this group.

<div
  id="user-data"
  ngModelGroup="userData"
  #userData="ngModelGroup">

We have the whole control of this group.

Radio Buttons

<div class="radio" *ngFor="let gender of genders">
  <label>
    <input
      type="radio"
      name="gender"
      ngModel
      [value]="gender"
      required
    >
    
  </label>
</div>

Setting and Patching Form Values with viewChild

Use the setValue() method of the viewChild property we set up before. Here we need to pass a JavaScript object representing our form.

@ViewChild('f', { static: false }) signUpForm: NgForm;

suggestUserName() {
    const suggestedName = 'Superuser';
    this.signUpForm.setValue({
      userData: {
        username: suggestedName,
        email: ''
      },
      secret: 'pet',
      questionAnswer: '',
      gender: 'male'
    });
  }

Then we can bind a button with this method.

However, this approach does have one downside. If we already had some value in the form, clicking this button will overwrite all the values.

We can actually patch one single value via the form attribute inside the signUpForm object.

suggestUserName() {
    const suggestedName = 'Superuser';

    this.signUpForm.form.patchValue({
      userData: {
        username: suggestedName
      }
    });
  }

Resetting Forms

this.signUpForm.reset();

It’s like the whole form was loaded again. Very helpful.

tags: Angular