Skip to content
  • https://chat.deepseek.com/a/chat/s/75474839-971c-4605-b025-6c4fbc1996b3

Template driven Form

1. intro

  • form - data-entry experience
  • two-way data binding, change tracking, validation, and error handling.
  • Angular gives JS representation of Form-Object
  • with form-data in K,V format
  • other metadata
    • for validation
    • for add styling
  • TD, where we define the form structure and validation rules directly in the template 👈
  • Angular infers the form-object from template

2. Create

  • import FormModule
  • it will detect the <form>
  • create JS-rep of form automatically.
  • register form to ngForm directive 👈
  • register controls using ngModel directive 👈
    • FormGroup-1
    • control-1
    • control-2
    • ...
    • FormGroup-2
    • ...
  • Get access to JS-rep using below steps.
    • @viewChild('formRef') form1 : NgForm
    • form1.path( { })
    • form1.patch( { })
    • form1.reset()

3. Form validation

  • HTML5 - validation - skip
  • put directive : email, min, max, etc
  • custom validator
  • directive
  • provide: NG_VALIDATORS
class Validators {
  static min(min: number): ValidatorFn;
  static max(max: number): ValidatorFn;
  static required(control: AbstractControl<any, any>): ValidationErrors | null;
  static requiredTrue(control: AbstractControl<any, any>): ValidationErrors | null;
  static email(control: AbstractControl<any, any>): ValidationErrors | null;
  static minLength(minLength: number): ValidatorFn;
  static maxLength(maxLength: number): ValidatorFn;
  static pattern(pattern: string | RegExp): ValidatorFn;
  static nullValidator(control: AbstractControl<any, any>): ValidationErrors | null;
  static compose(validators: null): null;
  static compose(validators: (ValidatorFn | null | undefined)[]): ValidatorFn | null;
  static composeAsync(validators: (AsyncValidatorFn | null)[]): AsyncValidatorFn | null;
}
  • Angular maintains the state
  • form : valid, dirty, touched, etc
  • formControl level. valid, dirty, touched, etc
  • also add css classes


4. Example

Example-1 (old)

  <form #formRef="ngForm" (ngSubmit) = "f1(formRef) > 
    <!-- automatically create FormControl instances -->
    <div id="group-key-1" #group1Ref= ngModelgroup [(ngModelgroup)]="group-1-data">
      <input name="key1" [(ngModel)]="feild1" ngNativeValidate>
    </div>

    <div id="group-key-1" #group1Ref= ngModelgroup [(ngModelgroup)]="group-1-data">
      <input name="key2" #feild2="ngModel" [(ngModel)]="feild2">
      <span class="error-style-1" *ngIf="!feild2.valid && feild2.touched">Please enter a valid email!</span>
    </div>

    <button type=submit > sunmit </button>
  </form> 
  <!--Since SPA  - Dont add - action (GET,POST,etc)-->
  f1(formRef : ElementRef) => { 
    console.log(formRef)
  }
error-style-1 {
  border: 1px solid red;
}


Example-2 :yellow_circle:

user-form.component.html

<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
  <div class="form-group">
    <label for="name">Name</label>
    <input type="text" id="name" name="name" [(ngModel)]="formData.name" required>
    <div *ngIf="myForm.controls['name']?.errors?.required && myForm.controls['name']?.touched">
      Name is required
    </div>
  </div>

  <div class="form-group">
    <label for="email">Email</label>
    <input type="email" id="email" name="email" [(ngModel)]="formData.email" required email>
    <div *ngIf="myForm.controls['email']?.errors && myForm.controls['email']?.touched">
      <div *ngIf="myForm.controls['email']?.errors?.required">Email is required</div>
      <div *ngIf="myForm.controls['email']?.errors?.email">Invalid email format</div>
    </div>
  </div>

  <!--ngModelGroup-->
  <div class="form-group" ngModelGroup="address" #address="ngModelGroup">
    <div class="form-group">
      <label for="street">Street</label>
      <input type="text" id="street" name="street" [(ngModel)]="formData.address.street" required>
    </div>

    <div class="form-group">
      <label for="city">City</label>
      <input type="text" id="city" name="city" [(ngModel)]="formData.address.city" required>
    </div>

    <div *ngIf="address.invalid && address.touched" class="error">
      Address information is incomplete
    </div>
  </div>

  <button type="submit" [disabled]="myForm.invalid">Submit</button>
</form>

class UserFormComponent

import { Component } from '@angular/core';

@Component({  selector: 'app-user-form',  templateUrl: './user-form.component.html'})
export class UserFormComponent
 {
  // Default form values
  formData = {
    name: 'John Doe',
    email: '',
    address: {
      street: '123 Main St',
      city: 'New York'
    }
  };

  onSubmit(form: ngForm) {
    if (form.valid) { }
  }

  // Reset form to default values
  resetForm(form: ngForm) {
    form.reset();
    this.formData = {
      name: 'John Doe',
      email: '',
      address: {
        street: '123 Main St',
        city: 'New York'
      }
    };
  }

  // Patch form with partial data
  patchForm() {
    this.formData = {
      ...this.formData,
      name: 'Jane Smith',
      address: {
        ...this.formData.address,
        city: 'Los Angeles'
      }
    };
  }
}

Custom Validator Directive

validate(control: AbstractControl): 👈

import { Directive } from '@angular/core';
import { NG_VALIDATORS, Validator, AbstractControl } from '@angular/forms';

@Directive({
  selector: '[appPasswordValidator]',
  providers: [{
    provide: NG_VALIDATORS,
    useExisting: PasswordValidatorDirective,
    multi: true
  }]
})
export class PasswordValidatorDirective implements Validator {
  validate(control: AbstractControl): { [key: string]: any } | null {
    const value = control.value;
    const hasNumber = /\d/.test(value);
    const hasUpper = /[A-Z]/.test(value);
    const hasLower = /[a-z]/.test(value);
    const valid = hasNumber && hasUpper && hasLower;

    if (!valid) {
      return { passwordStrength: true };
    }
    return null;
  }
}