> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/angular/angular/llms.txt
> Use this file to discover all available pages before exploring further.

# Template-Driven Forms

> Build forms using directives and ngModel for simpler form scenarios

Template-driven forms use directives in templates to create and manipulate the underlying form object model. They rely on two-way data binding with `ngModel` to track changes and update the data model.

## When to Use Template-Driven Forms

Template-driven forms are ideal for:

* Simple forms with minimal validation
* Forms that closely mirror the view structure
* Quick prototypes and small applications
* Scenarios where you prefer working in templates over component classes

For more complex scenarios, consider [reactive forms](/forms/reactive-forms).

## Setting Up Template-Driven Forms

Import `FormsModule` in your component or module:

```typescript theme={null}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-contact',
  standalone: true,
  imports: [FormsModule],
  template: `...`
})
export class ContactComponent {}
```

## Basic ngModel Usage

The `ngModel` directive creates a `FormControl` instance and binds it to a form control element.

### One-Way Binding

```typescript theme={null}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-name',
  standalone: true,
  imports: [FormsModule],
  template: `
    <label for="name">Name:</label>
    <input id="name" type="text" [ngModel]="name">
    <p>Hello, {{ name }}!</p>
  `
})
export class NameComponent {
  name = 'Nancy';
}
```

### Two-Way Binding

Use banana-in-a-box syntax `[(ngModel)]` for two-way data binding:

```typescript packages/forms/src/directives/ng_model.ts theme={null}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-name-editor',
  standalone: true,
  imports: [FormsModule],
  template: `
    <label for="name">Name:</label>
    <input id="name" type="text" [(ngModel)]="name">
    <p>Hello, {{ name }}!</p>
  `
})
export class NameEditorComponent {
  name = 'Nancy';
}
```

## Working with Forms

When using `ngModel` within a `<form>` tag, provide a `name` attribute so the control can be registered with the parent form.

### Basic Form Example

```typescript packages/forms/src/directives/ng_model.ts theme={null}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-contact-form',
  standalone: true,
  imports: [FormsModule],
  template: `
    <form #contactForm="ngForm" (ngSubmit)="onSubmit(contactForm)">
      <div>
        <label for="firstName">First Name:</label>
        <input 
          id="firstName" 
          name="firstName" 
          type="text" 
          [(ngModel)]="contact.firstName"
          required>
      </div>
      
      <div>
        <label for="lastName">Last Name:</label>
        <input 
          id="lastName" 
          name="lastName" 
          type="text" 
          [(ngModel)]="contact.lastName"
          required>
      </div>
      
      <div>
        <label for="email">Email:</label>
        <input 
          id="email" 
          name="email" 
          type="email" 
          [(ngModel)]="contact.email"
          required
          email>
      </div>
      
      <button type="submit" [disabled]="contactForm.invalid">Submit</button>
    </form>
    
    <p>Form Valid: {{ contactForm.valid }}</p>
    <p>Form Value: {{ contactForm.value | json }}</p>
  `
})
export class ContactFormComponent {
  contact = {
    firstName: '',
    lastName: '',
    email: ''
  };
  
  onSubmit(form: any) {
    console.log('Form submitted:', form.value);
    console.log('Model:', this.contact);
  }
}
```

## Accessing Form State

You can export the form directive into a local template variable using `#varName="ngForm"`:

```html theme={null}
<form #contactForm="ngForm">
  <!-- Form controls -->
</form>

<p>Form Status: {{ contactForm.status }}</p>
<p>Form Valid: {{ contactForm.valid }}</p>
<p>Form Pristine: {{ contactForm.pristine }}</p>
<p>Form Touched: {{ contactForm.touched }}</p>
```

### Accessing Individual Controls

Export individual controls to access their state:

```typescript packages/forms/src/directives/ng_model.ts theme={null}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-email-input',
  standalone: true,
  imports: [FormsModule],
  template: `
    <label for="email">Email:</label>
    <input 
      id="email" 
      name="email" 
      type="email" 
      [(ngModel)]="email"
      #emailField="ngModel"
      required
      email>
    
    <div *ngIf="emailField.invalid && emailField.touched">
      <p *ngIf="emailField.errors?.['required']">Email is required</p>
      <p *ngIf="emailField.errors?.['email']">Invalid email format</p>
    </div>
    
    <p>Valid: {{ emailField.valid }}</p>
    <p>Dirty: {{ emailField.dirty }}</p>
    <p>Touched: {{ emailField.touched }}</p>
  `
})
export class EmailInputComponent {
  email = '';
}
```

## Form Validation

Template-driven forms use directives for validation:

```html theme={null}
<form #userForm="ngForm">
  <div>
    <label for="username">Username:</label>
    <input 
      id="username" 
      name="username" 
      type="text" 
      [(ngModel)]="username"
      #usernameField="ngModel"
      required
      minlength="3"
      maxlength="20"
      pattern="[a-zA-Z0-9]+">
    
    <div *ngIf="usernameField.invalid && usernameField.touched">
      <p *ngIf="usernameField.errors?.['required']">Username is required</p>
      <p *ngIf="usernameField.errors?.['minlength']">
        Username must be at least 3 characters
      </p>
      <p *ngIf="usernameField.errors?.['maxlength']">
        Username cannot exceed 20 characters
      </p>
      <p *ngIf="usernameField.errors?.['pattern']">
        Username can only contain letters and numbers
      </p>
    </div>
  </div>
  
  <div>
    <label for="age">Age:</label>
    <input 
      id="age" 
      name="age" 
      type="number" 
      [(ngModel)]="age"
      #ageField="ngModel"
      required
      min="18"
      max="100">
    
    <div *ngIf="ageField.invalid && ageField.touched">
      <p *ngIf="ageField.errors?.['required']">Age is required</p>
      <p *ngIf="ageField.errors?.['min']">Must be at least 18</p>
      <p *ngIf="ageField.errors?.['max']">Must be 100 or less</p>
    </div>
  </div>
</form>
```

### Built-in Validation Directives

<CardGroup cols={2}>
  <Card title="required">
    Requires a non-empty value
  </Card>

  <Card title="email">
    Validates email format
  </Card>

  <Card title="minlength">
    Minimum length for strings/arrays
  </Card>

  <Card title="maxlength">
    Maximum length for strings/arrays
  </Card>

  <Card title="min">
    Minimum value for numbers
  </Card>

  <Card title="max">
    Maximum value for numbers
  </Card>

  <Card title="pattern">
    Validates against a regex pattern
  </Card>
</CardGroup>

## Grouped Controls

Use `ngModelGroup` to create sub-groups within a form:

```typescript packages/forms/src/directives/ng_model_group.ts theme={null}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-user-form',
  standalone: true,
  imports: [FormsModule, CommonModule],
  template: `
    <form #userForm="ngForm" (ngSubmit)="onSubmit()">
      <div ngModelGroup="name" #nameGroup="ngModelGroup">
        <label for="first">First Name:</label>
        <input 
          id="first" 
          name="first" 
          type="text" 
          [(ngModel)]="user.name.first"
          required>
        
        <label for="last">Last Name:</label>
        <input 
          id="last" 
          name="last" 
          type="text" 
          [(ngModel)]="user.name.last"
          required>
        
        <p *ngIf="nameGroup.invalid && nameGroup.touched">
          Name is incomplete
        </p>
      </div>
      
      <div ngModelGroup="address" #addressGroup="ngModelGroup">
        <label for="street">Street:</label>
        <input 
          id="street" 
          name="street" 
          type="text" 
          [(ngModel)]="user.address.street">
        
        <label for="city">City:</label>
        <input 
          id="city" 
          name="city" 
          type="text" 
          [(ngModel)]="user.address.city">
        
        <label for="state">State:</label>
        <input 
          id="state" 
          name="state" 
          type="text" 
          [(ngModel)]="user.address.state">
      </div>
      
      <button type="submit" [disabled]="userForm.invalid">Submit</button>
    </form>
    
    <pre>{{ userForm.value | json }}</pre>
  `
})
export class UserFormComponent {
  user = {
    name: {
      first: '',
      last: ''
    },
    address: {
      street: '',
      city: '',
      state: ''
    }
  };
  
  onSubmit() {
    console.log('User:', this.user);
  }
}
```

## ngModelOptions

Customize how ngModel works with `ngModelOptions`:

### Standalone Controls

Create a control that doesn't register with the parent form:

```html theme={null}
<form>
  <input name="login" [(ngModel)]="login">
  
  <!-- This checkbox is standalone and won't be in form value -->
  <input 
    type="checkbox" 
    [(ngModel)]="showAdvanced"
    [ngModelOptions]="{standalone: true}">
  Show advanced options?
</form>
<!-- form value: {login: ''} -->
```

### Update On Blur or Submit

Control when the model updates:

```html theme={null}
<input 
  name="description" 
  [(ngModel)]="description"
  [ngModelOptions]="{updateOn: 'blur'}">

<form [ngFormOptions]="{updateOn: 'submit'}">
  <!-- All controls update on submit -->
  <input name="username" [(ngModel)]="username">
  <input name="email" [(ngModel)]="email">
  <button type="submit">Submit</button>
</form>
```

### Custom Name

Provide a name through options instead of the name attribute:

```html theme={null}
<form>
  <my-custom-form-control 
    name="Nancy" 
    [(ngModel)]="value"
    [ngModelOptions]="{name: 'user'}">
  </my-custom-form-control>
</form>
<!-- form value: {user: ''} -->
```

## Handling Different Input Types

### Text Inputs

```html theme={null}
<input type="text" name="username" [(ngModel)]="username">
<input type="email" name="email" [(ngModel)]="email">
<input type="password" name="password" [(ngModel)]="password">
<textarea name="description" [(ngModel)]="description"></textarea>
```

### Checkboxes

```typescript theme={null}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-preferences',
  standalone: true,
  imports: [FormsModule],
  template: `
    <label>
      <input type="checkbox" name="newsletter" [(ngModel)]="newsletter">
      Subscribe to newsletter
    </label>
    <p>Newsletter: {{ newsletter }}</p>
  `
})
export class PreferencesComponent {
  newsletter = false;
}
```

### Radio Buttons

```typescript theme={null}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-survey',
  standalone: true,
  imports: [FormsModule],
  template: `
    <div>
      <label>
        <input type="radio" name="size" [(ngModel)]="size" value="small">
        Small
      </label>
      <label>
        <input type="radio" name="size" [(ngModel)]="size" value="medium">
        Medium
      </label>
      <label>
        <input type="radio" name="size" [(ngModel)]="size" value="large">
        Large
      </label>
    </div>
    <p>Selected size: {{ size }}</p>
  `
})
export class SurveyComponent {
  size = 'medium';
}
```

### Select Dropdowns

```typescript theme={null}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-country-selector',
  standalone: true,
  imports: [FormsModule, CommonModule],
  template: `
    <label for="country">Country:</label>
    <select id="country" name="country" [(ngModel)]="selectedCountry">
      <option [ngValue]="null" disabled>Select a country</option>
      <option *ngFor="let country of countries" [ngValue]="country">
        {{ country.name }}
      </option>
    </select>
    <p>Selected: {{ selectedCountry?.name }}</p>
  `
})
export class CountrySelectorComponent {
  countries = [
    { code: 'US', name: 'United States' },
    { code: 'CA', name: 'Canada' },
    { code: 'MX', name: 'Mexico' }
  ];
  
  selectedCountry: any = null;
}
```

### Multiple Select

```typescript theme={null}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-skills-selector',
  standalone: true,
  imports: [FormsModule, CommonModule],
  template: `
    <label for="skills">Skills:</label>
    <select id="skills" name="skills" [(ngModel)]="selectedSkills" multiple>
      <option *ngFor="let skill of skills" [ngValue]="skill">
        {{ skill }}
      </option>
    </select>
    <p>Selected: {{ selectedSkills | json }}</p>
  `
})
export class SkillsSelectorComponent {
  skills = ['JavaScript', 'TypeScript', 'Angular', 'React', 'Vue'];
  selectedSkills: string[] = [];
}
```

## Template-Driven Forms vs Reactive Forms

| Aspect                  | Template-Driven          | Reactive                |
| ----------------------- | ------------------------ | ----------------------- |
| **Setup**               | FormsModule              | ReactiveFormsModule     |
| **Form model creation** | Implicit (by directives) | Explicit (in component) |
| **Data model**          | Mutable                  | Immutable               |
| **Form validation**     | Directives               | Functions               |
| **Predictability**      | Asynchronous             | Synchronous             |
| **Testability**         | Less testable            | More testable           |
| **Scalability**         | Limited                  | Excellent               |

## When to Choose Template-Driven Forms

<AccordionGroup>
  <Accordion title="Simple forms">
    Template-driven forms are perfect for simple forms with basic validation.
  </Accordion>

  <Accordion title="Quick prototypes">
    Get forms up and running quickly without much setup.
  </Accordion>

  <Accordion title="Template-centric approach">
    If your team prefers working in templates rather than component classes.
  </Accordion>

  <Accordion title="Migration from AngularJS">
    Template-driven forms are similar to AngularJS forms, easing migration.
  </Accordion>
</AccordionGroup>

## Best Practices

<Warning>
  Always provide a `name` attribute when using `ngModel` within a `<form>` tag.
</Warning>

<Tip>
  Use template reference variables to access form and control state in templates.
</Tip>

<Info>
  For complex forms with dynamic controls or extensive validation logic, consider [reactive forms](/forms/reactive-forms).
</Info>

## Next Steps

<CardGroup cols={2}>
  <Card title="Form Validation" icon="shield-check" href="/forms/validation">
    Learn about validation in template-driven forms
  </Card>

  <Card title="Reactive Forms" icon="code" href="/forms/reactive-forms">
    Explore reactive forms for more complex scenarios
  </Card>
</CardGroup>
