Introduction
Today we will learn how to create a custom directive for confirming password validation. Also I will demonstrate how to use that directive in components.
Prerequisites
Basic knowledge of Angular components and Angular architecture. Before proceeding we will learn about directives in Angular.
Directive
In Angular, directives are declared using the @Directive keyword. Mainly we have three types of directives in Angular.
Component Directive
This is the one of the main directives of Angular applications. We can't create an Angular application without using component directive. These directives are decorated by @component keyword and also are known as Angular components.
Structural Directive
Structural directives are mainly used to manipulate (Add/Remove or Show/Hide) the DOM element in the html page. Structural directives will be prefixed with `*` .
We have so many built-in structural directives in Angular, such as:
Attribute Directive
Attribute directives are used to change the look and feel of HTML DOM elements, and to change the attributes of existing HTML elements. We can create our own customized attribute directive. We have some built-in attribute directives as well, such as NgStyle , NgClass etc.
Note
I have assumed that you have basic knowledge of Angular and have already created an Angular app. Here, I'll show how to create a directive for confirming password validation and how to use it in your existing app.
Step 1
Create a folder named directive in your app's root folder if it doesn't already exist, and create a subfolder named compare-directive . We will create a directive in this subfolder.
Step 2
Create a directve named compare-password.directive using angular cli, or create a typescript file named compare-password.directive.ts inside the compare-directive folder, and paste the below code in the compare-password.directive.ts file.
- import { Directive, Attribute } from '@angular/core';
- import { Validator, NG_VALIDATORS } from '@angular/forms';
-
-
- @Directive({
- selector: '[compare-password]',
- providers: [{provide: NG_VALIDATORS, useExisting: CompareDirective, multi: true}]
- })
-
- export class CompareDirective implements Validator {
-
- constructor(@Attribute('compare-password') public comparer: string,
- @Attribute('parent') public parent: string){}
-
- validate(c: any): {[key: string]: any} {
- const e = c.root.get(this.comparer);
-
- if (e && c.value !== e.value && !this.isParent) {
- return { compare: true };
- }
-
- if (e && c.value === e.value && this.isParent) {
- delete e.errors['compare'];
- if (!Object.keys(e.errors).length) {
- e.setErrors(null);
- }
- }
-
- if (e && c.value !== e.value && this.isParent) {
- e.setErrors({ compare: true });
- }
- }
-
- private get isParent() {
- if (!this.parent) {
- return false;
- }
- return this.parent === 'true' ? true : false;
- }
- }
In the above code snippet we have creted a custom directive with @Directive decorator. We have given selector value as compare-password, which means we can call this directive from the html element using selector value (i.e compare-password in this case).
This directive is accepting two attributes; the first is to provide the element with what we have to compare the value. The second one is to specify the element is the main parent element to the child element to which we have to compare the value.
At Line 30 we have added the element error name as compare if both values are not equal. We can use this error name to show or hide the error message or for form validation.
Step 3
Now we will create a module and will declare this directive in that module. That way we can directly import this module in any of the modules we want to use this directive. Create a module file named compare-password.module.ts and paste the below code.
- import { NgModule } from '@angular/core';
-
-
- import { CompareDirective } from './compare-password.directive';
-
-
- @NgModule({
- declarations: [
- CompareDirective
- ],
- exports: [
- CompareDirective
- ]
- })
- export class ComparePasswordModule { }
Here we have created a module and declared the directive in that module. Also we have exported the directive so we can use this directive after importing this module in another module.
Note
We can directly declare this directive in app.module or any other module as well, instead of creating separate modules.
Step 4
We have created the directive successfully, now we will learn how to use this directive in another component. We will create a new component, register.component, and we will use our custom directive in this component.
Create a component named register-component.ts using angular cli or manually, and paste the below code.
- import { Component, OnInit } from '@angular/core';
-
- @Component({
- selector: 'app-register',
- templateUrl: './register.component.html'
- })
-
- export class RegisterUserComponent implements OnInit {
- public user: User;
-
- ngOnInit() {
- this.user = {
- username: '',
- email: '',
- password: '',
- confirmPassword: ''
- };
- }
-
- onSubmit(model: User) {
- console.log(model);
- }
- }
-
- export interface User {
- username: string;
- email: string;
- password: string;
- confirmPassword: string;
- }
Here we have created a component and an interface as well to bind the form value using ngmodel.
Step 5
Here comes the main part to call the directive from the HTML Dom element. Paste the below code in register-component.html file.
- <div class="container">
- <div class="row justify-content-center">
- <div class="col-md-8">
- <div class="card">
- <div class="card-header">Register</div>
- <form (ngSubmit)="onSubmit()" #frm="ngForm">
- <div class="card-body">
- <div class="form-group row">
- <label for="email" class="col-md-4 col-form-label text-md-right">Email</label>
- <div class="col-md-6">
- <input type="text" [ngModel]='user.email' class="form-control" name="email">
- </div>
- </div>
- <div class="form-group row">
- <label for="username" class="col-md-4 col-form-label text-md-right">User Name</label>
- <div class="col-md-6">
- <input type="text" [ngModel]='user.username' class="form-control" name="username">
- </div>
- </div>
- <div class="form-group row">
- <label for="pass" class="col-md-4 col-form-label text-md-right">Password</label>
- <div class="col-md-6">
- <input type="password" [ngModel]='user.password' class="form-control" name="txtPassword" #txtPassword="ngModel" compare-password="txtConfirmPassword" parent="true" required>
- <span class="text-danger" *ngIf="txtPassword.dirty && txtPassword.errors && txtPassword.errors.required">Password is required </span>
- </div>
- </div>
- <div class="form-group row">
- <label for="confirm" class="col-md-4 col-form-label text-md-right">Confirm Password</label>
- <div class="col-md-6">
- <input type="password" [ngModel]='user.confirmPassword' class="form-control" name="txtConfirmPassword" #txtConfirmPassword="ngModel" compare-password="txtPassword" required>
- <span class="text-danger" *ngIf="txtConfirmPassword.dirty && txtConfirmPassword.errors && txtConfirmPassword.errors.required">Password is required </span>
- <span class="text-danger" *ngIf="txtConfirmPassword.dirty && ((txtPassword.errors && txtPassword.errors.compare) || (txtConfirmPassword.errors && txtConfirmPassword.errors.compare)) && !txtConfirmPassword.errors.required">Password Doesn't match</span>
- </div>
- </div>
- <div class="col-md-6 offset-md-4">
- <button type="submit" [disabled]="!frm.isValid" class="btn btn-primary">
- Register
- </button>
- </div>
- </div>
- </form >
-
- </div>
- </div>
- </div>
- </div>
Here at Line 23 for password textbox, we have specified the attribute compare-password="txtConfirmPassword" parent="true" which is the actual call of our custom directive. We have given the same name in attribute as we have mentioned in selector of directive. For confirming password we have called the directive at Line 30.
Note -- for password textbox we have given the attribute compare-password as `txtConfirmPassword` and for confirm password textbox as `txtPassword` which means we have to specify the element name to which we want to compare the values.
The below code snippet is used to show and hide the error message when the password doesn't match. Remember we have added the element error name as `compare` if values are not equal while creating a directive in Step 2. Here we have used the same error name to display the error message.
- <span class="text-danger" *ngIf="txtConfirmPassword.dirty && ((txtPassword.errors && txtPassword.errors.compare) || (txtConfirmPassword.errors && txtConfirmPassword.errors.compare)) && !txtConfirmPassword.errors.required">Password Doesn't match</span>
Step 6
At last we have to import the ComparePasswordModule created in Step 3. We have to import the module in the same module in which register-component is declared; i.e app.module.ts or another module.
Note
Also don't forget to import FormModule and ReactiveFormsModule in the same module to use the ngmodel.
Thanks for reading this article. Let me know your feedback to enhance the quality of this article. Happy coding!!