exhtml
1/25/2019 - 6:21 AM

Angular2 Snippets - Forms (Template driven)

Forms

Template driven approach

In your app.module:
import { FormsModule } from '@angular/forms';
Now when angular detects a <form>will create a javascript object representation of your form.

  • Register your form controls to include in this form object giving them a name and marking them with ngModel:
    <input type="text" ngModel name="username">
  • To manage the form submit, add a local reference (equal to ngForm, that is the javascript object representation of the form created by Angular) to the <form>, and pass it to the function you attach to ngSubmit handler: <form (ngSubmit)="onSubmit(myForm)" #myForm="ngForm">
  • Finally you can:
onSubmit(form: NgForm) {
  console.log(form);
}

If we inspect the object in the consol:

  • value form controls in key/value pairs
  • controls has representations of each input, with its own properties
  • dirty tells if we have changed anything in the form
  • touched if we have interacted
  • invalid / valid

Accesing the form with @ViewChild

  • with @ViewChild we can access an element holding a local reference:
export class AppComponent {

  @ViewChild('myForm') signupForm: NgForm; //access the local reference and store it in a variable of type NgForm

    //this gives you access to the form object without having to submit, 
    //this is specially useful if you need to access the form earlier to the point you submit it

  onSubmit() {
    console.log(this.signupForm);
    // use the form data
    this.user.username = this.signupForm.value.userData.username;
    // reset the form
    this.signupForm.reset(); //will empty all the inputs and also reset the state (valid, touched etc)
  }
}

Adding validation

<input ... required email>

  • required is only treated as a selector to take in account when checking if form is valid

  • email is not an html attribute, is a directive to make sure is a valid email address.

  • valid state is checked at a form level and at input level

  • classes are added to the input depending on the state:
    <input ng-dirty ng-touched ng-invalid>
    So you can:

input.ng-touched.ng-invalid {
  border: 1px solid red
} //to affect only to inputs that have been touched
  • disable the submit button if the form is not valid:
    <button type="submit" ... [disabled]="!myForm.valid"> //put here the name of your local reference

  • output validation messages: put a local reference in the input, and associate this to ngModel, that will expose us information about the control:

<input ... #email="ngModel">
<p class="help-block" *ngIf="!email.valid && email.touched">Please enter a valid email!</p>

Built-in validator directives in Angular for template driven approach: search for "validator" in the official docs: https://angular.io/api?type=directive - everything marked with "D" is a directive and can be added to your template.

If you need HTML5 native validation (disabled by Angular by default) add ngNativeValidateto the desired input

  • we can set a default value in a field using ngModel property binding:

defaultQuestion = 'pet';

<select ... [ngModel]="defaultQuestion">
  <option value="pet">Your first Pet?</option>
  <option value="teacher">Your first teacher?</option>
</select>
  • we also can use two way data binding:

answer = '';

<textarea ... [(ngModel)]="answer">
</textarea>
<p>Your reply: {{ answer }}</p>

Grouping form controls

  • give some visual grouping
  • evaluate validity of individual groups
  • give structure to our output object in our form:
<div ngModelGroup="userData" #userData="ngModelGroup">
  <input ...name="username">
  <input ...name="email">
</div>
<p *ngIf="!userData.valid && userData.touched">User data not valid</p>

Output object:

value: {
  ...
  userData: {
    usename: ""
    email: ""
  }
}

and also in controls:

controls: {
  question: FormControl
  userData: FormGroup
}

Setting and patching form values

Example: a button than when clicked, will populate the 'username' field.

  • We could use two way data binding, but remember we have our form accesed via @ViewChild: @ViewChild('myForm') signupForm: NgForm;

So we can:

  • Set the values of all controls in the form:
this.signupForm.setValue({ //allows us to set the value of the whole form. Here we need to pass a javascript object exactly representing our form
  userData: {
    username: suggestedName,
    email: ''
  },
  secret: 'pet',
});
  • Set the value of specific controls in the form:
this.signupForm.form.patchValue({ //important! you have to access .form property and then patchValue method
  userData: {
    username: suggestedName
  }
});