Template-Driven Forms

Read one Angular interview question at a time, then flip for the complete explanation, example, and code.

0/68 Known filtered set
Level
Read Full Guide Question: 1 of 68

Angular Interview Questions · Template-Driven Forms Companion Article

Template-Driven Forms

Template-driven forms are Angular's simpler approach to building forms. Most of the form structure and validation rules are declared directly inside the HTML template using directives such as ngModel, ngForm, required, minlength, and maxlength.

Angular automatically creates and manages the underlying form controls based on the directives used in the template. This makes template-driven forms convenient for login forms, contact forms, feedback forms, simple registration pages, search filters, and other forms that do not require highly complex state management.

For large forms with dynamic controls or complicated validation logic, Reactive Forms usually provide more explicit control. However, template-driven forms remain useful when the form structure is relatively straightforward.

FormsModule

Template-driven form features are provided by Angular's FormsModule.

Without importing FormsModule, directives such as ngModel and ngForm cannot be used.

In a standalone component, import it directly into the component:

TypeScript
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-contact-form',
  standalone: true,
  imports: [FormsModule],
  templateUrl: './contact-form.component.html'
})
export class ContactFormComponent {
}

In applications that still use NgModules, FormsModule can be imported inside the required module.

TypeScript
import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [
    FormsModule
  ]
})
export class AppModule {
}

Once FormsModule is available, Angular can automatically manage template-driven form controls.

Basic Template-Driven Form Example

Consider a simple user registration form.

TypeScript
export class RegisterComponent {
  user = {
    name: '',
    email: ''
  };

  submitForm(): void {
    console.log(this.user);
  }
}

Template:

HTML
<form #registerForm="ngForm" (ngSubmit)="submitForm()">

  <label for="name">Name</label>
  <input
    id="name"
    type="text"
    name="name"
    [(ngModel)]="user.name">

  <label for="email">Email</label>
  <input
    id="email"
    type="email"
    name="email"
    [(ngModel)]="user.email">

  <button type="submit">
    Register
  </button>

</form>

Angular handles the synchronization between the input elements and the component object.

ngModel

ngModel is one of the most important directives used in template-driven forms.

It connects an HTML form control with a property in the component.

For example:

HTML
<input
  type="text"
  name="username"
  [(ngModel)]="username">

Component:

TypeScript
export class UserComponent {
  username = '';
}

Whenever the user types into the input field, the username property is updated.

If the component changes the value of username, the input field is updated as well.

One-Way Binding with ngModel

ngModel can also be used with one-way binding.

HTML
<input
  type="text"
  name="username"
  [ngModel]="username">

This sends the value from the component to the form control.

It does not automatically update the component when the user modifies the field.

Listening to ngModelChange

Angular exposes the ngModelChange event.

HTML
<input
  type="text"
  name="username"
  [ngModel]="username"
  (ngModelChange)="onUsernameChange($event)">

Component:

TypeScript
username = '';

onUsernameChange(value: string): void {
  this.username = value;
  console.log(value);
}

This approach is useful when additional logic needs to run whenever an input value changes.

Two-Way Form Binding

The most common syntax in template-driven forms is:

HTML
[(ngModel)]="property"

This combines property binding and event binding.

Conceptually:

Text
[ngModel] + (ngModelChange)

Example:

HTML
<input
  type="text"
  name="city"
  [(ngModel)]="city">

Component:

TypeScript
city = 'Pune';

Initially, the input contains:

Text
Pune

If the user changes the input to:

Text
Mumbai

the component property automatically becomes:

TypeScript
city = 'Mumbai';

This synchronization is commonly called two-way data binding.

Why the name Attribute Is Important

Controls registered with an ngForm normally need a name attribute.

Correct:

HTML
<input
  type="text"
  name="firstName"
  [(ngModel)]="user.firstName">

Problematic:

HTML
<input
  type="text"
  [(ngModel)]="user.firstName">

The name identifies the control inside Angular's form model.

For example, with:

HTML
<input
  name="email"
  [(ngModel)]="user.email">

Angular may represent the control inside the form as:

Text
email

Each registered control should therefore have a unique name within the form.

ngForm

When FormsModule is imported, Angular automatically attaches an NgForm directive to standard <form> elements.

You can access this Angular form object using a template reference variable.

HTML
<form #userForm="ngForm">

Here:

Text
userForm

references Angular's NgForm instance.

It provides useful information such as:

  • current form values
  • validity
  • invalidity
  • touched state
  • untouched state
  • dirty state
  • pristine state
  • submitted state
  • registered controls

Accessing Form Values

Example:

HTML
<form #userForm="ngForm">

  <input
    type="text"
    name="username"
    ngModel>

  <input
    type="email"
    name="email"
    ngModel>

</form>

<pre>{{ userForm.value | json }}</pre>

If the user enters:

Text
username: Rahul
email: rahul@example.com

the form value can look like:

JSON
{
  "username": "Rahul",
  "email": "rahul@example.com"
}

Form Submission

Angular template-driven forms normally use the ngSubmit event.

HTML
<form
  #loginForm="ngForm"
  (ngSubmit)="onSubmit(loginForm)">

  <input
    type="email"
    name="email"
    ngModel>

  <input
    type="password"
    name="password"
    ngModel>

  <button type="submit">
    Login
  </button>

</form>

Component:

TypeScript
import { NgForm } from '@angular/forms';

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

The NgForm object gives the component access to both form values and form state.

Submitting the Bound Object

If fields are already connected to a component object using two-way binding, passing the form object is not always necessary.

TypeScript
user = {
  email: '',
  password: ''
};

onSubmit(): void {
  console.log(this.user);
}

Template:

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

  <input
    type="email"
    name="email"
    [(ngModel)]="user.email">

  <input
    type="password"
    name="password"
    [(ngModel)]="user.password">

  <button type="submit">
    Login
  </button>

</form>

Both approaches are valid. The best choice depends on whether the application needs direct access to Angular's form metadata.

Form Validation

Validation ensures that users enter acceptable data before it is processed.

Template-driven forms can use familiar HTML validation attributes, while Angular integrates those rules into its form state.

Example:

HTML
<input
  type="text"
  name="username"
  [(ngModel)]="user.username"
  required
  minlength="3"
  maxlength="20">

Angular monitors these rules and updates the validity of the corresponding form control.

Built-in Validators

Angular template-driven forms support commonly used validation rules.

Important examples include:

  • required
  • minlength
  • maxlength
  • pattern
  • email validation through Angular's email directive
  • minimum and maximum numeric constraints where applicable

Required Validation

HTML
<input
  type="text"
  name="name"
  ngModel
  required>

The control is invalid while it contains no acceptable value.

Email Validation

HTML
<input
  type="email"
  name="email"
  ngModel
  required
  email>

The field must contain a value accepted by Angular's email validator.

Minimum Length

HTML
<input
  type="password"
  name="password"
  ngModel
  minlength="8">

The value must satisfy the configured minimum length.

Maximum Length

HTML
<input
  type="text"
  name="username"
  ngModel
  maxlength="20">

This limits or validates the permitted length according to the applied form behavior.

Pattern Validation

A pattern can be used when data must match a specific format.

HTML
<input
  type="text"
  name="mobile"
  ngModel
  pattern="[0-9]{10}">

This example expects ten numeric digits.

Accessing Individual Form Control State

A template reference variable can expose the NgModel directive for a particular control.

HTML
<input
  type="email"
  name="email"
  [(ngModel)]="user.email"
  #email="ngModel"
  required
  email>

You can then inspect properties such as:

HTML
<p>Valid: {{ email.valid }}</p>
<p>Invalid: {{ email.invalid }}</p>
<p>Touched: {{ email.touched }}</p>
<p>Dirty: {{ email.dirty }}</p>

This is especially useful for displaying validation messages.

Validation Messages

A good form should explain why a value is invalid rather than simply preventing submission.

Example:

HTML
<label for="email">Email</label>

<input
  id="email"
  type="email"
  name="email"
  [(ngModel)]="user.email"
  #email="ngModel"
  required
  email>

<div *ngIf="email.invalid && email.touched">

  <p *ngIf="email.errors?.['required']">
    Email is required.
  </p>

  <p *ngIf="email.errors?.['email']">
    Enter a valid email address.
  </p>

</div>

This approach avoids showing errors immediately when the page first loads.

The validation message appears after the user interacts with the field.

Validation Using Modern Angular Control Flow

Applications using Angular's built-in control-flow syntax can express the same logic with @if.

HTML
<input
  type="email"
  name="email"
  [(ngModel)]="user.email"
  #email="ngModel"
  required
  email>

@if (email.invalid && email.touched) {

  @if (email.errors?.['required']) {
    <p>Email is required.</p>
  }

  @if (email.errors?.['email']) {
    <p>Enter a valid email address.</p>
  }

}

The validation behavior is the same. Only the template control-flow syntax differs.

Form-Level Validation Status

The complete form exposes validity information through NgForm.

HTML
<form #userForm="ngForm">

  <input
    type="text"
    name="name"
    ngModel
    required>

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

</form>

The button remains disabled until the form becomes valid.

This is a common pattern, although server-side validation must still be performed because browser-side validation alone cannot protect application data.

Form State Tracking

Angular automatically tracks user interaction with form controls.

The most important states are:

StateMeaning
validAll current validation rules pass
invalidAt least one validation rule fails
pristineThe user has not changed the value
dirtyThe user has changed the value
untouchedThe control has not been blurred after interaction
touchedThe user has interacted with and left the control
submittedThe form has been submitted

These states help developers decide when validation feedback should be displayed.

Pristine and Dirty

A control is initially considered pristine.

Text
pristine = true
dirty = false

After the user changes the value:

Text
pristine = false
dirty = true

Example:

HTML
<p>Dirty: {{ username.dirty }}</p>
<p>Pristine: {{ username.pristine }}</p>

Touched and Untouched

Before the user interacts with a field:

Text
untouched = true
touched = false

After the field receives focus and is subsequently left:

Text
untouched = false
touched = true

This makes touched particularly useful for validation messages.

HTML
@if (username.invalid && username.touched) {
  <p>Please enter a valid username.</p>
}

Angular CSS State Classes

Angular automatically adds CSS classes to form controls based on their state.

Common classes include:

Text
ng-valid
ng-invalid
ng-pristine
ng-dirty
ng-untouched
ng-touched

These classes can be used to visually highlight validation status.

Example:

CSS
input.ng-invalid.ng-touched {
  border: 1px solid red;
}

input.ng-valid.ng-touched {
  border: 1px solid green;
}

This lets Angular's existing form state drive the UI without manually adding CSS classes.

Custom Validation

Built-in validators cannot cover every business requirement.

Suppose a username must not contain spaces. A custom validator directive can enforce this rule.

Example:

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

@Directive({
  selector: '[noSpaces]',
  standalone: true,
  providers: [
    {
      provide: NG_VALIDATORS,
      useExisting: NoSpacesValidatorDirective,
      multi: true
    }
  ]
})
export class NoSpacesValidatorDirective implements Validator {

  validate(control: AbstractControl): ValidationErrors | null {
    const value = control.value;

    if (!value) {
      return null;
    }

    return value.includes(' ')
      ? { noSpaces: true }
      : null;
  }
}

Use it in the template:

HTML
<input
  type="text"
  name="username"
  [(ngModel)]="user.username"
  #username="ngModel"
  required
  noSpaces>

Validation message:

HTML
@if (username.errors?.['noSpaces'] && username.touched) {
  <p>Username cannot contain spaces.</p>
}

The validator returns:

TypeScript
null

when the value is valid.

When the value is invalid, it returns a validation error object:

TypeScript
{
  noSpaces: true
}

Cross-Field Validation Considerations

Sometimes validation depends on multiple controls.

Examples include:

  • password and confirm password
  • start date and end date
  • minimum and maximum values
  • country and postal code
  • shipping and billing selections

Template-driven forms can support more advanced validation through custom directives attached to the form or a group of controls.

However, when validation logic becomes highly interconnected, Reactive Forms often provide a clearer implementation because the form model is explicitly defined in TypeScript.

Form Reset

Template-driven forms can be reset using the resetForm() method.

HTML
<form
  #userForm="ngForm"
  (ngSubmit)="onSubmit(userForm)">

  <input
    type="text"
    name="name"
    [(ngModel)]="user.name">

  <button type="submit">
    Save
  </button>

  <button
    type="button"
    (click)="userForm.resetForm()">
    Reset
  </button>

</form>

Calling:

TypeScript
userForm.resetForm();

resets the form's controls and state.

States such as dirty and touched are also reset.

Resetting with Initial Values

A form can also be reset with specific values.

TypeScript
form.resetForm({
  name: 'Guest',
  email: ''
});

This is useful when the application wants to restore predefined defaults instead of clearing every field.

Resetting the Component Model

When [(ngModel)] is connected to an object, developers may also want to restore that object.

Example:

TypeScript
user = {
  name: '',
  email: ''
};

resetForm(form: NgForm): void {
  this.user = {
    name: '',
    email: ''
  };

  form.resetForm(this.user);
}

Keeping the component model and form state synchronized prevents confusing UI behavior after a reset.

Complete Template-Driven Form Example

Component:

TypeScript
import { Component } from '@angular/core';
import { FormsModule, NgForm } from '@angular/forms';

@Component({
  selector: 'app-profile-form',
  standalone: true,
  imports: [FormsModule],
  templateUrl: './profile-form.component.html'
})
export class ProfileFormComponent {

  user = {
    name: '',
    email: '',
    age: null as number | null
  };

  onSubmit(form: NgForm): void {
    if (form.invalid) {
      return;
    }

    console.log('Submitted Data:', this.user);
  }

  reset(form: NgForm): void {
    this.user = {
      name: '',
      email: '',
      age: null
    };

    form.resetForm(this.user);
  }
}

Template:

HTML
<form
  #profileForm="ngForm"
  (ngSubmit)="onSubmit(profileForm)">

  <div>
    <label for="name">Name</label>

    <input
      id="name"
      type="text"
      name="name"
      [(ngModel)]="user.name"
      #name="ngModel"
      required
      minlength="3">

    @if (name.invalid && name.touched) {

      @if (name.errors?.['required']) {
        <p>Name is required.</p>
      }

      @if (name.errors?.['minlength']) {
        <p>Name must contain at least 3 characters.</p>
      }

    }
  </div>

  <div>
    <label for="email">Email</label>

    <input
      id="email"
      type="email"
      name="email"
      [(ngModel)]="user.email"
      #email="ngModel"
      required
      email>

    @if (email.invalid && email.touched) {

      @if (email.errors?.['required']) {
        <p>Email is required.</p>
      }

      @if (email.errors?.['email']) {
        <p>Enter a valid email address.</p>
      }

    }
  </div>

  <div>
    <label for="age">Age</label>

    <input
      id="age"
      type="number"
      name="age"
      [(ngModel)]="user.age"
      #age="ngModel"
      required
      min="18">

    @if (age.invalid && age.touched) {
      <p>Enter a valid age of 18 or above.</p>
    }
  </div>

  <button
    type="submit"
    [disabled]="profileForm.invalid">
    Save Profile
  </button>

  <button
    type="button"
    (click)="reset(profileForm)">
    Reset
  </button>

</form>

This example demonstrates the main pieces of a template-driven form:

  • FormsModule
  • ngForm
  • ngModel
  • two-way binding
  • built-in validation
  • control state
  • validation messages
  • submission
  • reset behavior

Using Select Elements

Template-driven forms work with <select> elements as well.

TypeScript
roles = [
  'Developer',
  'Tester',
  'Designer'
];

selectedRole = '';

Template:

HTML
<select
  name="role"
  [(ngModel)]="selectedRole"
  required>

  <option value="">
    Select Role
  </option>

  @for (role of roles; track role) {
    <option [value]="role">
      {{ role }}
    </option>
  }

</select>

The selected option is synchronized with the component property.

Using Radio Buttons

Radio buttons can share the same model.

HTML
<label>
  <input
    type="radio"
    name="experience"
    value="fresher"
    [(ngModel)]="experience">
  Fresher
</label>

<label>
  <input
    type="radio"
    name="experience"
    value="experienced"
    [(ngModel)]="experience">
  Experienced
</label>

Component:

TypeScript
experience = 'fresher';

Only one radio button in the group can be selected.

Using Checkboxes

Checkboxes are commonly bound to boolean properties.

HTML
<label>
  <input
    type="checkbox"
    name="acceptedTerms"
    [(ngModel)]="acceptedTerms">
  I accept the terms and conditions.
</label>

Component:

TypeScript
acceptedTerms = false;

When selected:

TypeScript
acceptedTerms === true

When cleared:

TypeScript
acceptedTerms === false

Grouping Controls

Related template-driven controls can be grouped using ngModelGroup.

Example:

HTML
<form #userForm="ngForm">

  <div ngModelGroup="address">

    <input
      type="text"
      name="city"
      ngModel>

    <input
      type="text"
      name="state"
      ngModel>

  </div>

</form>

The resulting value can have a nested structure:

JSON
{
  "address": {
    "city": "Pune",
    "state": "Maharashtra"
  }
}

This can make larger forms easier to organize.

Standalone ngModel

Sometimes an input appears inside a form but should not be registered as part of the Angular form.

You can configure ngModel as standalone.

HTML
<input
  [(ngModel)]="showAdvanced"
  [ngModelOptions]="{ standalone: true }"
  type="checkbox">

This is useful for UI-only controls that should not become part of the submitted form model.

Updating on Blur

By default, form values are generally updated as the user changes the field.

ngModelOptions can change the update strategy.

Example:

HTML
<input
  type="text"
  name="username"
  [(ngModel)]="username"
  [ngModelOptions]="{ updateOn: 'blur' }">

With this configuration, the model update is associated with the blur interaction rather than every ordinary value-change interaction.

This can be useful when validation or processing should not run for every keystroke.

Updating on Submit

A control can also be configured to update when the form is submitted.

HTML
<input
  type="text"
  name="username"
  [(ngModel)]="username"
  [ngModelOptions]="{ updateOn: 'submit' }">

This behavior can reduce unnecessary updates when intermediate values are not needed.

Form Submission Best Practices

When implementing template-driven forms, several practical rules improve reliability.

Validate before processing

Even when the submit button is disabled, verify form validity in the submission handler.

TypeScript
onSubmit(form: NgForm): void {
  if (form.invalid) {
    return;
  }

  // Process valid form data
}

Never rely only on client-side validation

Angular validation improves user experience but does not replace backend validation.

A user can bypass browser-side checks and send requests directly to an API.

The server should independently validate:

  • required values
  • data types
  • lengths
  • formats
  • authorization
  • business constraints

Prevent duplicate submissions

When an API request is being processed, disabling the submit button can help prevent users from accidentally submitting the same form multiple times.

Display meaningful errors

Prefer:

Text
Password must contain at least 8 characters.

instead of:

Text
Invalid input.

Specific feedback helps users correct problems faster.

Common Mistakes

Forgetting FormsModule

If FormsModule is missing, Angular will not recognize template-driven form directives correctly.

Make sure it is imported wherever the template requires these features.

Forgetting the name Attribute

Incorrect:

HTML
<form>
  <input [(ngModel)]="user.email">
</form>

Preferred:

HTML
<form>
  <input
    name="email"
    [(ngModel)]="user.email">
</form>

Registered template-driven controls need a meaningful name.

Showing Validation Errors Too Early

This can create a poor user experience:

HTML
@if (email.invalid) {
  <p>Invalid email.</p>
}

The message can appear before the user even interacts with the form.

A better condition is:

HTML
@if (email.invalid && email.touched) {
  <p>Enter a valid email.</p>
}

Processing Invalid Forms

Avoid submitting data without checking the form state.

TypeScript
onSubmit(form: NgForm): void {
  if (form.invalid) {
    return;
  }

  // Continue
}

Using Complex Business Logic Directly in the Template

Templates should remain readable.

Avoid expressions containing complicated validation or transformation logic.

Move complex logic into:

  • component methods
  • custom validators
  • services
  • domain-specific utilities

Template-Driven Forms vs Reactive Forms

Both approaches use Angular's forms infrastructure, but they organize the form model differently.

Template-Driven FormsReactive Forms
Form definition is mainly in the templateForm model is explicitly created in TypeScript
Uses ngModel extensivelyUses FormControl, FormGroup, FormArray, and related APIs
Less setup for simple formsMore explicit setup
Convenient for small formsBetter suited to complex forms
Validation rules often appear in HTMLValidation logic is commonly configured in TypeScript
Angular creates much of the form model automaticallyDeveloper explicitly constructs the form model
Easy for straightforward CRUD screensStrong fit for dynamic and highly testable form logic

Neither approach is universally better.

The choice should depend on the form's complexity and application requirements.

When Template-Driven Forms Are a Good Choice

Template-driven forms work particularly well for:

  • login forms
  • contact forms
  • feedback forms
  • simple profile forms
  • newsletter subscriptions
  • basic registration forms
  • search and filter forms
  • small CRUD interfaces

They are especially convenient when:

Text
The number of controls is small
+
Validation is straightforward
+
Form structure rarely changes dynamically

When Reactive Forms May Be Better

Consider Reactive Forms when the application requires:

  • dynamically adding and removing controls
  • complex nested form structures
  • extensive cross-field validation
  • highly reusable custom validators
  • complex conditional validation
  • explicit observable form-state handling
  • easier unit testing of form logic
  • large enterprise forms

Choosing the appropriate form architecture early can significantly reduce future maintenance complexity.

Practical Example: Registration Validation Flow

Suppose a registration form contains:

Text
Name
Email
Password
Terms Accepted

A practical validation sequence can be:

Text
User opens form
        ↓
All controls are initially pristine
        ↓
User interacts with Name
        ↓
Control becomes touched
        ↓
Angular checks required/minlength
        ↓
Error message appears if invalid
        ↓
User fixes the value
        ↓
Control becomes valid
        ↓
All controls become valid
        ↓
Submit button becomes available
        ↓
ngSubmit executes
        ↓
Component validates form again
        ↓
Data is sent to backend

Understanding this lifecycle makes template-driven form behavior easier to debug.

Important Interview Points

What is a template-driven form in Angular?

A template-driven form is an Angular form approach where most of the form definition and validation rules are declared in the HTML template. Angular creates and manages the underlying form model automatically.

Which module is required?

FormsModule provides the main directives required for template-driven forms.

What does ngModel do?

ngModel connects a form control to Angular's form model and can also synchronize the control value with a component property.

What is ngForm?

ngForm represents the Angular form and exposes its value, controls, validation status, interaction state, and submission state.

Why is name required with ngModel?

The name identifies a control when Angular registers it inside the parent form.

What is the difference between dirty and touched?

dirty indicates that the control value has changed.

touched indicates that the user has interacted with the control and moved focus away from it.

How are validation messages normally displayed?

Validation messages are commonly shown when a control is both invalid and touched.

HTML
@if (email.invalid && email.touched) {
  <p>Enter a valid email.</p>
}

How do you reset a template-driven form?

Use:

TypeScript
form.resetForm();

or pass replacement values:

TypeScript
form.resetForm(initialData);

Can custom validators be used?

Yes. Custom validator directives can participate in Angular's validation system and return custom validation errors.

Key Takeaways

  • Template-driven forms are configured mainly in HTML templates.
  • FormsModule enables template-driven form functionality.
  • ngModel connects controls with Angular's form system.
  • [(ngModel)] provides convenient two-way binding.
  • ngForm represents the complete form.
  • Every registered control should have an appropriate name.
  • Angular automatically tracks valid, invalid, dirty, pristine, touched, and untouched states.
  • Built-in validation rules can handle many common requirements.
  • Custom validators can enforce application-specific rules.
  • Validation messages should normally appear only after meaningful user interaction.
  • resetForm() resets values and form state.
  • Template-driven forms are a strong choice for simple and moderately sized forms.
  • Reactive Forms are generally preferable when form structure or validation becomes highly dynamic or complex.
  • Client-side validation improves usability, but backend validation is still mandatory for real applications.

Practical Learning Checklist

After studying template-driven forms, you should be able to:

  • Import and configure FormsModule.
  • Create a form using ngForm.
  • Bind input values using ngModel.
  • Implement two-way form binding.
  • Submit form data using ngSubmit.
  • Apply required, email, length, and pattern validation.
  • Read individual control validation errors.
  • Display useful validation messages.
  • Track dirty, pristine, touched, and untouched states.
  • Disable submission while the form is invalid.
  • Create a custom validator.
  • Reset a form correctly.
  • Work with text fields, checkboxes, radio buttons, and select elements.
  • Organize related controls using ngModelGroup.
  • Decide when template-driven forms are appropriate and when Reactive Forms would be a better design.

Question Hint