Syntax
The syntax of ngModel is very simple. It binds a component’s property to an HTML form control, enabling two-way data flow. Here's the general syntax:
<input [(ngModel)]="propertyName" />
Key Points
- [(ngModel)]: This is a combination of property binding ([ ]) and event binding (( )), enabling two-way data binding.
- propertyName: Refers to the property in the component that is linked to the form control.
Two-Way Binding
Two-way binding is one of the most powerful features of Angular, & `ngModel` makes it incredibly easy to implement. Two-way binding means that any changes made in the view (like an input field) are automatically reflected in the model (your component’s data), & any changes in the model are immediately shown in the view. This creates a seamless connection between the user interface & the underlying data.
Let’s understand this with an example. Suppose you have a simple form where the user can enter their name, & you want to display a greeting message that updates as the user types. Let’s discuss how you can achieve this using `ngModel`:
<input type="text" [(ngModel)]="name">
<p>Hello, {{ name }}!</p>
In this example, `[(ngModel)]` is the syntax for two-way binding. The square brackets `[]` represent property binding (model to view), & the parentheses `()` represent event binding (view to model). Together, they create a two-way data flow.
Let’s discuss this step-by-step:
1. When the user types in the input field, the `name` property in your component is updated.
2. The updated `name` value is then displayed in the paragraph below the input field.
3. If you change the `name` property programmatically in your component, the input field will also update to reflect the new value.
Let’s look at a more detailed example with a component:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<input type="text" [(ngModel)]="name">
<p>Hello, {{ name }}!</p>
<button (click)="resetName()">Reset Name</button>
`
})
export class AppComponent {
name: string = '';
resetName() {
this.name = '';
}
}
In this example, we’ve added a button that resets the `name` property to an empty string when clicked. When the button is pressed, the input field & the greeting message both update immediately because of two-way binding.
Two-way binding is not just limited to text inputs. It works with other form elements like checkboxes, radio buttons, & dropdowns. For example, you can bind a checkbox to a boolean property like this:
<input type="checkbox" [(ngModel)]="isSubscribed">
<p>Subscription Status: {{ isSubscribed ? 'Subscribed' : 'Not Subscribed' }}</p>
Here, `isSubscribed` is a boolean property in your component. When the checkbox is checked or unchecked, the value of `isSubscribed` is updated, & the message below the checkbox changes accordingly.
Note: In summary, two-way binding with `ngModel` makes it easy to keep your view & model in sync. It reduces the amount of code you need to write & ensures that your application responds instantly to user input.
Selectors
The ngModel directive can be used with different selectors to bind data to various HTML elements. Here are some common selectors:
1. Input Elements
Used with input fields like text boxes, checkboxes, or radio buttons.
<input type="text" [(ngModel)]="username" />
2. Select Dropdowns
Bind dropdown options to a property in the component.
<select [(ngModel)]="selectedOption">
<option *ngFor="let option of options" [value]="option">{{ option }}</option>
</select>
3. Textareas
Synchronize data with multi-line text input fields.
<textarea [(ngModel)]="description"></textarea>
Approach
To use ngModel, you must ensure the FormsModule is imported into your Angular module. Here's the step-by-step approach:
Step 1: Import FormsModule
In the module file, import the FormsModule from @angular/forms.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, FormsModule],
bootstrap: [AppComponent],
})
export class AppModule {}
Step 2: Create a Component
Create a property in your component class to bind to the ngModel directive.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
username: string = '';
selectedOption: string = '';
description: string = '';
options: string[] = ['Option 1', 'Option 2', 'Option 3'];
}
Step 3: Bind Data in Template
Use the [(ngModel)] directive in the HTML template to bind the properties.
<h1>Two-Way Data Binding Example</h1>
<label for="username">Username:</label>
<input id="username" type="text" [(ngModel)]="username" />
<p>Your username is: {{ username }}</p>
<label for="options">Select an Option:</label>
<select id="options" [(ngModel)]="selectedOption">
<option *ngFor="let option of options" [value]="option">{{ option }}</option>
</select>
<p>You selected: {{ selectedOption }}</p>
<label for="description">Description:</label>
<textarea id="description" [(ngModel)]="description"></textarea>
<p>Description: {{ description }}</p>
Examples
Example 1: Binding to a Text Input
This example shows how ngModel binds an input field to a property in the component.
Component (app.component.ts):
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
username: string = '';
}
Template (app.component.html):
<h2>Username Input</h2>
<input type="text" [(ngModel)]="username" />
<p>Your username is: {{ username }}</p>
Output:
When you type into the input field, the username property updates, and the text displayed reflects the current value.
Example 2: Binding to a Select Dropdown
This example demonstrates binding a select dropdown to a property.
Component (app.component.ts):
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
selectedOption: string = '';
options: string[] = ['Angular', 'React', 'Vue'];
}
Template (app.component.html):
<h2>Framework Selector</h2>
<select [(ngModel)]="selectedOption">
<option *ngFor="let option of options" [value]="option">{{ option }}</option>
</select>
<p>You selected: {{ selectedOption }}</p>
Output:
The selected framework updates dynamically as you choose an option.
Example 3: Using Textarea
Binding a multiline input with ngModel.
Component (app.component.ts):
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
description: string = '';
}
Template (app.component.html):
<h2>Multiline Text Binding</h2>
<textarea [(ngModel)]="description"></textarea>
<p>Description: {{ description }}</p>
Output:
The description field reflects the content typed into the textarea.
CSS Classes
Angular automatically adds & removes CSS classes to form controls based on their state. This feature is incredibly useful for styling forms dynamically & providing visual feedback to users. When you use `ngModel`, Angular applies specific classes to the form controls, such as `ng-valid`, `ng-invalid`, `ng-pristine`, & `ng-dirty`. These classes reflect the current state of the control, allowing you to create responsive & interactive forms.
Let’s understand the most commonly used CSS classes:
1. `ng-valid` & `ng-invalid`:
- `ng-valid` is added when the control’s value meets all validation rules.
- `ng-invalid` is added when the control’s value fails any validation rule.
2. `ng-pristine` & `ng-dirty`:
- `ng-pristine` is added when the control has not been modified by the user.
- `ng-dirty` is added when the user has modified the control’s value.
3. `ng-touched` & `ng-untouched`:
- `ng-touched` is added when the user has focused on the control & then blurred out (clicked away).
- `ng-untouched` is added when the user has not interacted with the control.
4. `ng-pending`:
- `ng-pending` is added when the control is undergoing asynchronous validation.
Let’s look at an example of how these classes work:
<form>
<label for="username">Username:</label>
<input type="text" id="username" name="username" [(ngModel)]="username" required minlength="3" #username="ngModel">
<div *ngIf="username.invalid && (username.dirty || username.touched)">
<p *ngIf="username.errors?.required">Username is required.</p>
<p *ngIf="username.errors?.minlength">Username must be at least 3 characters long.</p>
</div>
</form>
In this example, the `username` input field is bound to the `username` property in your component. The `required` & `minlength` validators ensure that the field is not empty & contains at least 3 characters.
When the user interacts with the form, Angular adds or removes CSS classes based on the control’s state. For example:
- Initially, the input field will have the classes `ng-pristine`, `ng-untouched`, & `ng-invalid`.
- When the user starts typing, the `ng-pristine` class is replaced with `ng-dirty`.
- If the user clicks away from the input field, the `ng-untouched` class is replaced with `ng-touched`.
- If the input meets the validation rules, the `ng-invalid` class is replaced with `ng-valid`.
You can use these classes to style your form controls dynamically. For example, you can add a green border to valid fields & a red border to invalid fields:
input.ng-valid {
border: 2px solid green;
}
input.ng-invalid.ng-touched {
border: 2px solid red;
}
In this CSS:
- The `ng-valid` class adds a green border to valid input fields.
- The `ng-invalid` & `ng-touched` classes add a red border to invalid fields that have been touched by the user.
You can also use these classes to show or hide error messages. For example:
<div class="error-message" *ngIf="username.invalid && username.touched">
<p *ngIf="username.errors?.required">Username is required.</p>
<p *ngIf="username.errors?.minlength">Username must be at least 3 characters long.</p>
</div>
In this case, the error messages are displayed only when the input field is invalid & has been touched by the user.
In summary, Angular’s automatic CSS class management makes it easy to create dynamic & responsive forms. By using classes like `ng-valid`, `ng-invalid`, `ng-pristine`, & `ng-dirty`, you can provide real-time visual feedback to users & ensure that your forms are both functional & visually appealing.
Validate User Input
Validating user input is a critical part of building forms in any application. Angular makes this process straightforward with the help of `ngModel` & built-in validation features. With the help of `ngModel`, you can easily check if the data entered by the user meets specific requirements & display helpful error messages when it doesn’t.
To validate user input, Angular provides a set of built-in validators, like `required`, `minLength`, `maxLength`, & `pattern`. These validators can be applied directly to form controls using `ngModel`. Let’s look at an example of how to validate a simple form field:
<form>
<label for="username">Username:</label>
<input type="text" id="username" name="username" [(ngModel)]="username" required minlength="3">
<div *ngIf="username.invalid && (username.dirty || username.touched)">
<p *ngIf="username.errors?.required">Username is required.</p>
<p *ngIf="username.errors?.minlength">Username must be at least 3 characters long.</p>
</div>
</form>
In this example, the `username` input field has two validators: `required` & `minlength`. The `required` validator ensures that the field is not empty, & the `minlength` validator ensures that the input is at least 3 characters long.
Let’s see how the validation works step by step:
1. When the user interacts with the input field (e.g., types something & then leaves the field), Angular checks if the input meets the validation rules.
2. If the input is invalid, Angular adds specific error messages to the `errors` object of the `username` control.
3. The `*ngIf` directive is used to display error messages only when the input is invalid & has been touched or modified by the user.
To make this work, you need to define the `username` control in your component & use Angular’s `NgModel` directive to bind it to the input field. Let’s see how you can do it:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<form>
<label for="username">Username:</label>
<input type="text" id="username" name="username" [(ngModel)]="username" required minlength="3" #username="ngModel">
<div *ngIf="username.invalid && (username.dirty || username.touched)">
<p *ngIf="username.errors?.required">Username is required.</p>
<p *ngIf="username.errors?.minlength">Username must be at least 3 characters long.</p>
</div>
</form>
`
})
export class AppComponent {
username: string = '';
}
In this code, `#username="ngModel"` creates a reference to the `ngModel` directive, which allows you to access the control’s state (e.g., `invalid`, `dirty`, `touched`) & display error messages accordingly.
You can also create custom validators if the built-in ones don’t meet your needs. For example, if you want to validate that the username doesn’t contain any special characters, you can create a custom validator like this:
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
export function noSpecialCharsValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const regex = /^[a-zA-Z0-9]*$/;
const valid = regex.test(control.value);
return valid ? null : { noSpecialChars: true };
};
}
To use this custom validator, you need to add it to your form control:
<input type="text" id="username" name="username" [(ngModel)]="username" required minlength="3" [ngModelOptions]="{ updateOn: 'blur' }" #username="ngModel" [noSpecialChars]="true">
<p *ngIf="username.errors?.noSpecialChars">Username cannot contain special characters.</p>
In summary, `ngModel` makes it easy to validate user input in Angular forms. By combining built-in validators with custom ones, you can ensure that the data entered by users is accurate & meets your application’s requirements.
Application Status
Understanding the status of your application’s forms is crucial for creating a smooth user experience. Angular provides several properties & methods to track the state of form controls, such as whether they have been touched, modified, or are valid. These statuses help you provide real-time feedback to users & ensure that your forms behave as expected.
When using `ngModel`, you can access the status of a form control through the `NgModel` directive. Here are some key properties you can use:
1. `pristine` & `dirty`
- A control is `pristine` if the user hasn’t interacted with it yet.
- It becomes `dirty` as soon as the user modifies its value.
2. `touched` & `untouched`
- A control is `touched` if the user has focused on it & then blurred out (clicked away).
- It remains `untouched` if the user hasn’t interacted with it at all.
3. `valid` & `invalid`:
- A control is `valid` if it meets all the validation rules.
- It is `invalid` if it fails any of the validation rules.
4. ‘pending`:
- A control is `pending` if it is in the middle of an asynchronous validation process.
Let’s look at an example to understand how these properties work:
<form>
<label for="email">Email:</label>
<input type="email" id="email" name="email" [(ngModel)]="email" required email #email="ngModel">
<div *ngIf="email.invalid && (email.dirty || email.touched)">
<p *ngIf="email.errors?.required">Email is required.</p>
<p *ngIf="email.errors?.email">Please enter a valid email address.</p>
</div>
<p>Control Status:</p>
<ul>
<li>Pristine: {{ email.pristine }}</li>
<li>Dirty: {{ email.dirty }}</li>
<li>Touched: {{ email.touched }}</li>
<li>Untouched: {{ email.untouched }}</li>
<li>Valid: {{ email.valid }}</li>
<li>Invalid: {{ email.invalid }}</li>
<li>Pending: {{ email.pending }}</li>
</ul>
</form>
In this example, the `email` input field is bound to the `email` property in your component. The `required` & `email` validators ensure that the field is not empty & contains a valid email address.
Let’s see what happens when the user interacts with the form:
1. Initially, the control is `pristine`, `untouched`, & `invalid` (if no value is entered).
2. When the user starts typing, the control becomes `dirty`.
3. When the user clicks away from the input field, the control becomes `touched`.
4. If the input meets the validation rules, the control becomes `valid`. Otherwise, it remains `invalid`.
You can use these statuses to provide real-time feedback to users. For example, you can disable the submit button until all form controls are valid:
<button type="submit" [disabled]="email.invalid">Submit</button>
In this case, the submit button is disabled as long as the `email` field is invalid.
You can also use these statuses to style your form controls dynamically. For example, you can add a red border to invalid fields:
input.ng-invalid.ng-touched {
border: 1px solid red;
}
This CSS rule applies a red border to any input field that is invalid & has been touched by the user.
In summary, tracking the status of your form controls is essential for creating responsive & user-friendly forms. By using properties like pristine, dirty, touched, & valid, you can provide real-time feedback & ensure that your forms behave as expected.
Frequently Asked Questions
What is the purpose of ngModel in Angular?
The ngModel directive facilitates two-way data binding, allowing synchronization between a component's property and an HTML form element.
Why is FormsModule necessary for ngModel?
The FormsModule provides the required functionality to use template-driven forms and directives like ngModel in Angular.
Can we use ngModel without importing FormsModule?
No, ngModel requires FormsModule to work. Ensure it is imported in your module file.
Conclusion
In this article, we discussed the ngModel directive in Angular, which is crucial for achieving two-way data binding in your applications. From syntax and selectors to practical examples, we discussed how to use ngModel effectively for creating dynamic, user-friendly forms. Mastering ngModel will simplify handling user inputs and managing data in Angular applications.
You can also check out our other blogs on Code360.