DEV Community

Shilpi Agrawal
Shilpi Agrawal

Posted on • Edited on

Password visibility toggle using stimulus.js and Rails 6

Recently I wanted to implement password show hide feature in devise forms using Stimulus.js in Rails 6. Being a beginner in stimulus. I had to struggle a lot to find a right way to do it.

There are two ways of implementation and two scenarios which I will be covering in this article.

  1. When you want to implement toggle just in one password field.

  2. When you want to implement toggle in two password fields for e.g. in signup form we will have two password fields.

I am assuming whoever is reading this they have basic idea of Stimulus.js

Scenario 1.

This is how my basic devise password field looks like

<div class="form-group my-4" data-controller="password-toggle">
 <%= f.label :password %>
 <%= f.password_field :password, autocomplete: "current-password", class: 'form-control p-2 my-2 unhide', placeholder: "Password", "data-target": "password-toggle.unhide"%>
 <a data-action="click->password-toggle#password" class="password-field-icon-1 far fa-eye-slash"></a>
</div>
Enter fullscreen mode Exit fullscreen mode

There are three things which I have added which converts my field into accepting stimulus events.

  1. Defining stimulus controller: data-controller="password-toggle" password_toggle_controller.js is my stimulus controller file.

  2. Defining target field: "data-target": "password-toggle.unhide"

  3. Defining click event: Click event to be defined on icon tag. data-action="click->password-toggle#password"

Now, this is how our password_toggle_controller.js will look like.

import { Controller } from "stimulus";

export default class extends Controller {

  static targets = ["unhide"]

  password(e) {
    if (this.input.type === "password") {
      e.target.classList.remove('fa-eye-slash');
      e.target.classList.add('fa-eye');
      this.input.type = "text";
    } else {
      e.target.classList.remove('fa-eye');
      e.target.classList.add('fa-eye-slash');
      this.input.type = "password";
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The basic idea of this piece of code is, if input field type is password and you click on visibility icon it will change the field type to text and toggle the icon and vice versa.

Now, if this works for you and this is only your requirement then great!

Scenario 2

But if your requirement is not this and you want to enable the toggle on both password fields. Then we have to use more of jquery.

And in my code as I have used SVG type of icon. e.target doesn't always gives me correct target to make the changes in icon. This article briefly explains why does click event on SVG icon behaves like that.

So, I had to change my way of approaching to this problem.

<div class="form-group pb-1" data-controller="password-toggle">
  <label>Password</label>
  <%= f.password_field :password, autocomplete: "off", class: 'form-control p-2', required: true, placeholder: 'Password' %>
  <a data-action="click->password-toggle#password" class="password-field-icon-2 far fa-eye-slash"</a> 
</div>

<div class="form-group pb-1" data-controller="password-toggle">
  <label>Password Confirmation</label>
  <%= f.password_field :password_confirmation, autocomplete: "off", class: 'form-control p-2', required: true, placeholder: 'Confirm Password' %>
  <a data-action="click->password-toggle#password" class="password-field-icon-2 far fa-eye-slash">     </a>
</div>
Enter fullscreen mode Exit fullscreen mode

As, you can see I removed the data-target as I am not going to use that, instead I will use the event object, in my stimulus controller.

import { Controller } from "stimulus";

export default class extends Controller {

  password(e) {

    var password_field = $(e.currentTarget).prev("input")[0];
    var icon = $(e.currentTarget).closest("svg")[0];

    if (password_field.type === "password") {
      icon.classList.remove('fa-eye-slash');
      icon.classList.add('fa-eye');
      password_field.type = "text";
    } else {
      icon.classList.remove('fa-eye');
      icon.classList.add('fa-eye-slash');
      password_field.type = "password";
    }
  }
 }
}
Enter fullscreen mode Exit fullscreen mode

The basic algo of the code remains same, the only change is how do we fetch the input field and how do we fetch the icon. Instead of using e.target I used e.currentTarget which correctly gives me the icon tag.

And instead of using this.target I am using same e.currentTarget to get the input field just previous to it, which is basically gives me the input password field for that icon event.

Thank you! For reading it out. I hope it helps someone. If you have better idea than this. Please let me know. I would love implement it in my project. Don't forget to show some love. ❤️

Top comments (0)