Making an Angular Directive to Toggle Password Visibility

Often when implementing a password field on a form, you’ll want to add a button to toggle the masking/visibility of that password so the user can see what they’ve typed. In Angular, this can be easily achieved on any input field, thanks to directives.

Today we’ll write an attribute directive, which can “change the appearance or behavior of an element, component, or another directive.” For our purposes, we’ll use it to change an input element.

Setting Up

To start, we’ll put a simple input element into our template.

<input placeholder="Password" type="password">

Next, we’ll create the directive. We’ll inject an ElementRef into the constructor to get a reference to the host input element.

import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[toggle]'
})
export class ToggleDirective {
  constructor(private el: ElementRef) { }
}

Let’s be sure to apply the directive as an attribute to our input element back in our template:

<input toggle placeholder="Password" type="password">

Fleshing out the Directive

Back in our directive, let’s add a few things.

First, in the constructor, we’ll create a span element and set its innerHTML to say “show.”

import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[toggle]'
})
export class ToggleDirective {
  constructor(private el: ElementRef) {
    const span = document.createElement('span');
    span.innerHTML = 'show';
  }
}

Second, we’ll get the parent of the host input element and append our span as a child, so it shows up next to our input.

import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[toggle]'
})
export class ToggleDirective {
  constructor(private el: ElementRef) {
    const parent = this.el.nativeElement.parentNode;
    const span = document.createElement('span');
    span.innerHTML = 'show';
    parent.appendChild(span);
  }
}

Third, we want this to toggle when we click on it, so let’s add a click event listener to our span, with the callback triggering a toggle method that we’ll also create.

import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[toggle]'
})
export class ToggleDirective {
  constructor(private el: ElementRef) {
    const parent = this.el.nativeElement.parentNode;
    const span = document.createElement('span');
    span.innerHTML = 'show';
    span.addEventListener('click', () => {
      this.toggle(span);
    });
    parent.appendChild(span);
  }

  toggle(span: HTMLElement) { }
}

Fourth, to make our toggle method work, we’ll add a property _shown to the directive to store the state of the password visibility. We’ll initialize it to false, then set and use it in the toggle method.

When the password is shown, we want to set the span to say “hide” and set the type of the input to “text.” When the password is not shown, we want to set the span to say “show” and set the type of the input to “password.”

import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[toggle]'
})
export class ToggleDirective {

  private _shown = false;

  constructor(private el: ElementRef) {
    const parent = this.el.nativeElement.parentNode;
    const span = document.createElement('span');
    span.innerHTML = 'show';
    span.addEventListener('click', () => {
      this.toggle(span);
    });
    parent.appendChild(span);
  }

  toggle(span: HTMLElement) {
    if (this._shown) {
      this.el.nativeElement.setAttribute('type', 'text');
      span.innerHTML = 'hide';
    } else {
      this.el.nativeElement.setAttribute('type', 'password');
      span.innerHTML = 'show';
    }
  }
}

Now, when you click on “show,” it will toggle the type of the input field to “text,” so you can see their input, and then click “hide” to toggle it back to hiding the password.

The Finished Product

As you can see, it was pretty easy to make such a directive, which can now be applied to password fields wherever you want (as simply as applying the directive like an attribute to your inputs). It’s also easy to extend what we wrote here to position the span element differently, modify the input field in other ways when you toggle the button, and much more.

You can read more about directives in the Angular documentation here, and you can view the full code and example usage for this via StackBlitz here.