Angular Template-Driven Forms
Angular is a powerful framework used for developing dynamic web applications, and one of its key features is the ability to handle forms seamlessly. Angular provides two ways to handle reactive and template-driven forms. Template-driven forms allow for a more declarative approach where form logic is defined in the template rather than the component logic. This approach often aligns more intuitively with HTML form handling, making it accessible and straightforward for developers familiar with traditional form handling.
Overview
Template-driven forms in Angular are primarily controlled via directives such as ngModel
. This directive is essential for creating form elements, binding them to the model, and validating user input. Here, the form is managed in the HTML file, enabling a seamless integration with the template structure. By contrast, reactive forms are more functional and are controlled programmatically from the component class.
Setting Up Template-Driven Forms
To utilize template-driven forms in Angular, you must first import the FormsModule
module into the appropriate module (typically AppModule
). This module provides the necessary directives to create template-driven forms. Here's how to import it:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // Import FormsModule
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule // Include FormsModule in imports
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Creating a Simple Form
Once you've imported FormsModule
, you can start creating a form in your component's template. Let's create a simple form for collecting a user's name and email:
<!-- form.component.html -->
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)">
<div>
<label for="name">Name:</label>
<input type="text" id="name" name="name" ngModel>
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email" ngModel>
</div>
<button type="submit">Submit</button>
</form>
In this code snippet:
#userForm="ngForm"
creates a template reference variable nameduserForm
that refers to the form itself, and thengForm
directive manages the form's state.ngModel
directive binds each input element to the form's data model, making it easy to track changes and validate input.(ngSubmit)="onSubmit(userForm)"
binds the form's submit event to a method in the component class that processes the form data.
Handling Form Submission
In the component class, you need to define the onSubmit
method to handle the form submission. Here's how you can do it:
// form.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.css']
})
export class FormComponent {
onSubmit(form: any) {
if (form.valid) {
console.log('Form Submitted!', form.value);
} else {
console.log('Form is invalid');
}
}
}
In the onSubmit
method:
form.valid
checks if the form is valid according to any validations applied.form.value
contains the values of the form controls, making it easy to access the submitted data.
Adding Validation
Angular's FormsModule
provides several built-in validators that you can use directly in your template to validate form inputs. Here’s an example that refines the email input by adding required and email validation:
<!-- form.component.html -->
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)">
<div>
<label for="name">Name:</label>
<input type="text" id="name" name="name" ngModel required>
<span *ngIf="name.invalid && name.touched">
Name is required
</span>
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email" ngModel required email>
<span *ngIf="email.invalid && email.touched">
Please enter a valid email
</span>
</div>
<button type="submit" [disabled]="userForm.invalid">Submit</button>
</form>
In this code snippet:
required
is a built-in validator that checks if the field is not empty.email
is another built-in validator that checks if the input is a valid email address.name.invalid && name.touched
is used to show the validation message only after the user has interacted with the input.- The submit button is disabled via
[disabled]="userForm.invalid"
to prevent submission if the form is in an invalid state.
Accessing Form Controls
In template-driven forms, you can access individual form controls using template reference variables. This allows you to apply additional logic or styling based on the control's state:
<!-- form.component.html -->
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)">
<div>
<label for="name">Name:</label>
<input #name="ngModel" type="text" id="name" name="name" ngModel required>
<span *ngIf="name.invalid && name.touched">
Name is required
</span>
</div>
<div>
<label for="email">Email:</label>
<input #email="ngModel" type="email" id="email" name="email" ngModel required email>
<span *ngIf="email.invalid && email.touched">
Please enter a valid email
</span>
</div>
<button type="submit" [disabled]="userForm.invalid">Submit</button>
</form>
Here, #name="ngModel"
and #email="ngModel"
create template reference variables for the name and email inputs. These variables expose the state of the ngModel directive, allowing for more granular control over form validation and user interaction.
Dynamic Form Fields
Template-driven forms are also flexible enough to handle dynamically generated form fields. Here’s an example of how you can add fields dynamically:
<!-- form.component.html -->
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)">
<div *ngFor="let skill of skills; let i = index">
<label [for]="'skill'+i">Skill {{i+1}}:</label>
<input [id]="'skill'+i" [name]="'skill'+i" ngModel required>
</div>
<button type="button" (click)="addSkill()">Add Skill</button>
<button type="submit" [disabled]="userForm.invalid">Submit</button>
</form>
In the component class, the addSkill
method can be implemented as follows:
// form.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.css']
})
export class FormComponent {
skills: string[] = [];
addSkill() {
this.skills.push('');
}
onSubmit(form: any) {
if (form.valid) {
console.log('Form Submitted!', form.value);
} else {
console.log('Form is invalid');
}
}
}
In this example:
- The
*ngFor
directive is used to loop over an array of skills and generate corresponding input fields. - The
addSkill
method adds a new empty string to theskills
array, effectively adding a new skill input field to the form.
Custom Validation
While Angular provides a range of built-in validators, you can also create custom validators for more complex validation logic. Custom validators are functions that return either null
for valid states or an error object for invalid states. Here’s an example of a custom validator for checking if a field’s value is not a specific string:
// custom-validators.directive.ts
import { AbstractControl, ValidationErrors } from '@angular/forms';
export function forbiddenNameValidator(forbiddenName: string): (control: AbstractControl) => ValidationErrors | null {
return (control: AbstractControl): ValidationErrors | null => {
const forbidden = (control.value?.toLowerCase() === forbiddenName.toLowerCase());
return forbidden ? { forbiAccessDeniedddenName: {value: control.value} } : null;
};
}
In this example, forbiddenNameValidator
is a factory function that returns a validator function. You can use this custom validator in your template-driven form as follows:
<!-- form.component.html -->
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)">
<div>
<label for="name">Name:</label>
<input #name="ngModel" type="text" id="name" name="name" ngModel required [ngModelOptions]="{standalone: true}" [forbiddenName]="forbiddenName">
<span *ngIf="name.errors?.forbiddenName && name.touched">
Name is forbidden
</span>
</div>
<div>
<label for="email">Email:</label>
<input #email="ngModel" type="email" id="email" name="email" ngModel required email>
<span *ngIf="email.invalid && email.touched">
Please enter a valid email
</span>
</div>
<button type="submit" [disabled]="userForm.invalid">Submit</button>
</form>
In the component class, you can define the forbiddenName
property:
// form.component.ts
import { Component } from '@angular/core';
import { forbiddenNameValidator } from './custom-validators.directive';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.css']
})
export class FormComponent {
forbiddenName: string = 'admin';
onSubmit(form: any) {
if (form.valid) {
console.log('Form Submitted!', form.value);
} else {
console.log('Form is invalid');
}
}
}
In this setup:
- The
forbiddenNameValidator
is applied to the name input using the[forbiddenName]
directive. - The
standalone: true
option in[ngModelOptions]
is used to apply the custom validator to individual fields without requiring a form control group. - If the name matches the forbidden name, the validation error
forbiddenName
is triggered, and a corresponding error message is displayed.
Summary
Template-driven forms in Angular offer a straightforward and intuitive way to handle forms using directives directly in the template. By leveraging built-in validators and creating custom validators, you can enforce input constraints and ensure data integrity. With dynamic form generation and seamless validation handling, template-driven forms make form management accessible and efficient in complex applications. Whether you're building simple registration forms or sophisticated multi-step forms, template-driven forms provide a powerful toolset to simplify the process.
Examples, Set Route, and Run the Application Then Data Flow Step by Step for Beginners: Angular Template-Driven Forms
Template-driven forms in Angular are designed to handle form control in a straightforward way, directly linking the form controls to the component's template. This approach is simpler and more intuitive, especially for beginners. Below is a step-by-step guide to creating a template-driven form using Angular, including setting up routes, running the application, and understanding the data flow.
Step 1: Set Up the Angular Application
Install Angular CLI: If you haven’t installed Angular CLI globally on your system, open your terminal and install it using npm:
npm install -g @angular/cli
Create a New Angular Project: Start by creating a new Angular project. Replace
form-app
with your desired project name.ng new form-app cd form-app
Serve the Application: Run the application to ensure everything is set up correctly.
ng serve
Navigate to
http://localhost:4200/
in your web browser. You should see the default Angular app running.
Step 2: Create a Component for the Form
Generate a New Component: Use Angular CLI to generate a new component, for example,
contact
.ng generate component contact
Open the Component Files: First, open
contact.component.html
to define your form structure.<form #contactForm="ngForm" (ngSubmit)="onSubmit(contactForm.value)"> <div> <label for="name">Name:</label> <input type="text" id="name" required [(ngModel)]="model.name" name="name" #name="ngModel"> <div *ngIf="name.invalid && name.touched" class="alert alert-danger"> Name is required. </div> </div> <div> <label for="email">Email:</label> <input type="email" id="email" required email [(ngModel)]="model.email" name="email" #email="ngModel"> <div *ngIf="email.invalid && email.touched" class="alert alert-danger"> Valid email is required. </div> </div> <div> <label for="phone">Phone:</label> <input type="tel" id="phone" required [(ngModel)]="model.phone" name="phone" #phone="ngModel"> <div *ngIf="phone.invalid && phone.touched" class="alert alert-danger"> Phone is required. </div> </div> <button type="submit" [disabled]="contactForm.invalid">Submit</button> </form>
Component Logic in TypeScript: Modify
contact.component.ts
to define the form model and handle the form submission.import { Component } from '@angular/core'; @Component({ selector: 'app-contact', templateUrl: './contact.component.html', styleUrls: ['./contact.component.css'] }) export class ContactComponent { model = { name: '', email: '', phone: '' }; onSubmit(formData) { console.log('Form Submitted!', formData); alert('Form Submitted!'); } }
Step 3: Set Up Routing for the Form Component
Configure Routes: Open
app-routing.module.ts
to define a route for theContactComponent
.import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ContactComponent } from './contact/contact.component'; const routes: Routes = [ { path: '', redirectTo: '/contact', pathMatch: 'full' }, { path: 'contact', component: ContactComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Add FormsModule: Open
app.module.ts
and importFormsModule
to enable template-driven forms.import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { ContactComponent } from './contact/contact.component'; @NgModule({ declarations: [ AppComponent, ContactComponent ], imports: [ BrowserModule, AppRoutingModule, FormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Add Navigation Link in Template: Open
app.component.html
and add a navigation link to theContactComponent
.<h1>Welcome to the Contact Form App</h1> <a routerLink="/contact">Go to Contact Form</a> <router-outlet></router-outlet>
Step 4: Run the Application and Test the Form
Launch the Application: Serve the application once more.
ng serve
Test the Form: Navigate to
http://localhost:4200/
in your web browser. Click on "Go to Contact Form" to access your created form. You can fill out the form and submit it, which should trigger theonSubmit
method and display an alert with the form data.
Data Flow in Template-Driven Forms
- Template: The HTML template defines the form structure. Decorate the form with the
ngForm
directive and individual input controls withngModel
. - Two-Way Data Binding:
ngModel
is used to create two-way data binding between the form controls and the component's properties. This allows the form data to be automatically updated in the component whenever the user interacts with the form. - Form Validation: You can use standard HTML5 validation attributes like
required
oremail
along with Angular-specific directives to provide client-side validation. - Form Submission: Use the
(ngSubmit)
event to handle form submission. The form data is passed as an argument to the component’s method, where you can implement your logic to handle the data (e.g., submission to a server, display in a dialog).
Using template-driven forms in Angular can greatly simplify the development process for beginners, focusing on simple and declarative form handling directly within the component template. With this guide, you should be well-equipped to get started with template-driven forms in Angular.
Certainly! Here are the top 10 questions with detailed answers related to Angular Template-driven Forms:
1. What are Angular Template-driven Forms?
Answer:
Angular Template-driven Forms provide a way to manage forms by directly manipulating the DOM and using directives in the template. This approach is straightforward and best suited for simpler forms. Angular Template-driven Forms are managed via directives like ngModel
and form
that are bound to the component's class properties.
2. How do you create a Template-driven Form in Angular and what are its key components?
Answer:
Creating a Template-driven Form in Angular involves several key components:
- HTML Template (Template/Slice of View):
Use directives likengForm
andngModel
in the template to create form elements. Example:<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)"> <input type="text" name="firstName" ngModel required> <input type="submit" [disabled]="userForm.invalid"> </form>
- Component Class (Model/Controller):
Define methods and properties in the component class to handle form data and events. Example:import { Component } from '@angular/core'; @Component({ selector: 'app-sign-up-form', templateUrl: './sign-up-form.component.html', }) export class SignUpFormComponent { onSubmit(form: any): void { console.log('Form Submitted!', form.value); } }
- NgModule (Application Module):
Import theFormsModule
from@angular/forms
in the NgModules where you want to use template-driven forms. Example:import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { SignUpFormComponent } from './sign-up-form/sign-up-form.component'; @NgModule({ declarations: [ SignUpFormComponent, // ... other components ], imports: [ BrowserModule, FormsModule, // Import FormsModule ], providers: [], bootstrap: [AppComponent], }) export class AppModule {}
3. How do you bind data to form fields using two-way data binding (ngModel
)?
Answer:
Two-way data binding in Angular enables direct synchronization between the HTML form and the component properties. ngModel
is the directive that provides this functionality.
Syntax:
<input type="text" [(ngModel)]="person.name" name="name">
In the component class:
export class SignUpFormComponent {
person = {
name: '',
};
}
Now, as the user modifies the input, the person.name
property gets updated immediately, and vice versa.
4. What are the built-in validators in Angular for Template-driven Forms?
Answer:
Angular provides a set of built-in validators for common use cases in template-driven forms:
required:
Ensures the input field is not empty.<input type="text" ngModel required>
minlength:
Ensures the input value meets the minimum length requirement.<input type="text" ngModel minlength="3">
maxlength:
Ensures the input value does not exceed a specified maximum length.<input type="text" ngModel maxlength="10">
pattern:
Validates the input against a regular expression.<input type="text" ngModel pattern="^[a-zA-Z]*$">
min and max:
Validates the input in a numeric range.<input type="number" ngModel min="1" max="100">
You can also leverage custom validators via Angular's Validators
API when required.
5. How can you handle form submission in Template-driven Forms?
Answer:
Handling form submission in Angular involves binding an event listener to the form's submit event and accessing the form's data and validity.
Steps:
Use
(ngSubmit)
directive on the form element:<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)"> <!-- form fields --> </form>
Define the
onSubmit
method in the component class:onSubmit(form: NgForm): void { if (form.valid) { console.log('Form is valid'); console.log('Form Data:', form.value); } else { console.log('Form is invalid'); } }
Here, form
refers to an instance of NgForm
, which contains the form's data and validity state.
6. How do you track the state of form controls in Template-driven Forms (e.g., pristine vs dirty, untouched vs touched)?
Answer:
Angular provides several properties on form controls and the form itself to track its state:
Properties:
Control Properties:
These apply to individual form controls likeinput
,textarea
,select
, etc.pristine
:
The control has not been interacted with yet.dirty
:
The control has been interacted with (either touched or modified).touched
:
The control has been visited by the user.untouched
:
The control has not been visited.valid
:
The control's value passes all validation checks.invalid
:
The control's value does not pass all validation checks.Example:
<input type="text" [(ngModel)]="person.name" name="name" required #name="ngModel"> <div *ngIf="name.invalid && (name.dirty || name.touched)"> Name is required! </div>
Form Properties (
NgForm
):
Apply to the entire form.submitted
:
The form has been submitted at least once.valid
:
The form's entire value passes all validation checks.invalid
:
The form's value does not pass all validation checks.pristine
anddirty
:
Same as control properties but for the form.
Usage in Templates: You can use these properties to conditionally render validation messages or control button states.
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)"> <input type="text" [(ngModel)]="person.name" name="name" required #name="ngModel"> <div *ngIf="name.invalid && (name.dirty || name.touched)"> Name is required! </div> <button type="submit" [disabled]="userForm.invalid">Submit</button> </form>
7. How do you reset a Template-driven Form after submission?
Answer:
Resetting an Angular Template-driven form after submission is straightforward. You can utilize the NgForm
method reset()
to clear the form's data and reset its state.
Steps:
Bind a
(ngSubmit)
event to call a reset method:<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)"> <!-- form fields --> </form>
Define the
onSubmit
method to reset the form:onSubmit(form: NgForm): void { if (form.valid) { console.log('Submitting form', form.value); form.reset(); // Reset form after submission } }
Example:
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)">
<input type="text" [(ngModel)]="person.name" name="name" required>
<input type="submit" value="Submit">
</form>
import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
selector: 'app-sign-up-form',
templateUrl: './sign-up-form.component.html',
})
export class SignUpFormComponent {
person = {
name: '',
};
onSubmit(form: NgForm): void {
if (form.valid) {
console.log('Form Submitted!', form.value);
form.reset(); // Reset form fields to default values and state
}
}
}
8. What is the difference between Template-driven and Reactive Forms in Angular?
Answer:
While both Template-driven and Reactive Forms provide ways to manage form data and validation in Angular applications, they have distinct approaches and use cases:
Template-driven Forms:
Definition:
Managed via directives in the template. Forms are less dynamic and configured imperatively within the HTML.Pros:
- Ease of Use:
Well-suited for simpler, less dynamic forms. - Less Code:
Require less boilerplate in the component class. - Declarative:
Leverage Angular's template syntax and directives for validation and form management.
- Ease of Use:
Cons:
- Limited Flexibility:
Not ideal for complex or dynamic forms that require programmatic manipulation. - Testing:
Testing can be more challenging due to a higher degree of HTML dependency.
- Limited Flexibility:
Key Directives:
ngForm
ngModel
required
,minlength
,maxlength
,pattern
, etc.
Reactive Forms:
Definition:
Managed via form models defined in the component class. Forms are more dynamic and configured programmatically.Pros:
- Dynamic Forms:
Easily handle complex and dynamic form structures, such as adding/removing fields. - Strong Typing and Immediate Access:
Form data is strongly typed and immediately accessible within the component class. - Testing:
Easier to unit test due to separation of concerns.
- Dynamic Forms:
Cons:
- More Setup:
Require more boilerplate code to define forms and their controls. - Steeper Learning Curve:
Initial setup can be more complex compared to template-driven forms.
- More Setup:
Key Classes:
FormGroup
FormControl
FormArray
Validators
Comparison:
| Feature | Template-driven Forms | Reactive Forms |
|----------------------------|-----------------------|----------------------|
| Managing State | Imperative via directives | Declarative via form models in the component class |
| Validation | Directives (required, minlength, etc.) | Programmatic via Validators
|
| Dynamic Forms | Limited support | Extensive support |
| Testing | Challenges Due to HTML | Easier |
| Configuration | Declarative in the Template | Programmatic in the Component Class |
| Control Over Form Data | Limited | Full access |
9. How do you validate a group of controls in a Template-driven Form?
Answer:
In Angular Template-driven Forms, validating a group of controls can be achieved using ngModelGroup
and custom form validation directives. However, built-in directives like pattern
, minlength
, maxlength
, etc., are typically used for individual controls. For complex group validations, you may need to create a custom directive.
Steps:
Group Controls Using
ngModelGroup
:<form #addressForm="ngForm" (ngSubmit)="onSubmit(addressForm)"> <ngModelGroup #address="ngModelGroup" name="address"> <input type="text" name="street" [(ngModel)]="addressData.street" required> <input type="text" name="city" [(ngModel)]="addressData.city" required> </ngModelGroup> <button type="submit" [disabled]="addressForm.invalid">Submit</button> </form>
Handle Group Submission:
export class AddressFormComponent { addressData = { street: '', city: '', }; onSubmit(form: NgForm): void { console.log('Form Submitted!', form.value); console.log('Address group valid:', form.controls.address.valid); } }
Custom Validation:
For more complex validations, create a custom directive:
Create a Custom Directive:
import { Directive, Input } from '@angular/core'; import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; @Directive({ selector: '[appMatch]', providers: [{ provide: NG_VALIDATORS, useExisting: MatchValidatorDirective, multi: true }], }) export class MatchValidatorDirective implements Validator { @Input('appMatch') matcherControlName!: string; validate(control: AbstractControl): ValidationErrors | null { const matcherControl = control.root.get(this.matcherControlName); if (matcherControl) { matcherControl.updateValueAndValidity(); } return matcherControl && control.value !== matcherControl.value ? { match: true } : null; } }
Register the directive in a module:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { SignUpFormComponent } from './sign-up-form/sign-up-form.component'; import { MatchValidatorDirective } from './match-validator.directive'; @NgModule({ declarations: [ SignUpFormComponent, MatchValidatorDirective, // Register directive ], imports: [ BrowserModule, FormsModule, ], providers: [], bootstrap: [AppComponent], }) export class AppModule {}
Use the custom directive in the template:
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)"> <input type="password" name="password" [(ngModel)]="userData.password" required> <input type="password" name="confirmPassword" [(ngModel)]="userData.confirmPassword" required appMatch="password"> <div *ngIf="userForm.controls.confirmPassword?.errors?.match"> Passwords do not match! </div> <button type="submit" [disabled]="userForm.invalid">Submit</button> </form>
10. How can you add custom validation to Template-driven Forms?
Answer:
Adding custom validation to Angular Template-driven Forms can be accomplished by creating custom directives that implement the Validator
interface. These directives can then be applied to form controls to enforce specific validation rules.
Steps:
Create a Custom Validator Directive:
import { Directive, Input } from '@angular/core'; import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; @Directive({ selector: '[appCustomValidator]', providers: [{ provide: NG_VALIDATORS, useExisting: CustomValidatorDirective, multi: true }], }) export class CustomValidatorDirective implements Validator { @Input('appCustomValidator') customValidationFn: (control: AbstractControl) => ValidationErrors | null = (control: AbstractControl) => null; validate(control: AbstractControl): ValidationErrors | null { return this.customValidationFn(control); } }
Register the directive in a module:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { SignUpFormComponent } from './sign-up-form/sign-up-form.component'; import { CustomValidatorDirective } from './custom-validator.directive'; @NgModule({ declarations: [ SignUpFormComponent, CustomValidatorDirective, // Register directive ], imports: [ BrowserModule, FormsModule, ], providers: [], bootstrap: [AppComponent], }) export class AppModule {}
Use the custom directive in the template:
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)"> <input type="text" name="username" [(ngModel)]="userData.username" required appCustomValidator="validateUsername"> <div *ngIf="userForm.controls.username?.errors?.custom"> {{ userForm.controls.username.errors.custom.message }} </div> <button type="submit" [disabled]="userForm.invalid">Submit</button> </form>
Define the custom validation function in the component class:
export class SignUpFormComponent { userData = { username: '', }; validateUsername(control: AbstractControl): ValidationErrors | null { if (control.value && control.value.toLowerCase() === 'admin') { return { custom: { message: 'Admin is not an allowed username' } }; } return null; } onSubmit(form: NgForm): void { console.log('Form Submitted!', form.value); } }
Explanation:
- Step 1: The
CustomValidatorDirective
implements theValidator
interface and defines avalidate
method that applies a custom validation function passed through theappCustomValidator
input. - Step 2: The directive is registered in a module, making it available for use in components.
- Step 3: The custom validator directive (
appCustomValidator
) is applied to an input field, and thevalidateUsername
method is passed as the custom validation function. - Step 4: The
validateUsername
method checks if the input value is "admin" and returns a validation error if it is.
By following these steps, you can extend the validation capabilities of your Angular Template-driven Forms to enforce custom rules tailored to your application's requirements.