Fancy Forgot Password with jQuery/ui and Rails

By now, you have to know how fascinated I am by jQuery, and how much I love using it with Rails to make things simple, yet slick. Last week I did some wireframes for a client, and one of them was the “Forgot Password” feature… which I displayed in a modal dialog in the w/f. It seems to me that popups make sense for these kinds of things, rather than fully redirecting to another page, then upon successful password email, redirecting to even another page. So here was my fast, easy solution using jQuery (+jQuery-UI) and Rails.

First, make it work without Javascript and write your unit tests so they pass accordingly. Accessibility is at stake here people and we want to make sure anyone can retrieve their password at any time. My approach, just so you can follow the logic easily, is the following:

  1. User clicks Forgot Password link
  2. User enters email address
    • Invalid?
      1. Throw an error
    • Valid?
      1. Set new password
      2. Send email containing new password
      3. Alert with success message

So we create our controller method, our view, our notifier, and, if you like (I did), a method on our user model that does the setting and delivering of new password.

Code after the jump!

app/views/sessions/new.html.erb

<%= link_to 'Forgot Password', forgot_password_path, :class => 'popup' %>
<% form_tag session_path do -%>
	<%= label_tag :email %>
	<%= text_field_tag :email %>

	<%= label_tag :password %>
	<%= password_field_tag :password %>

	<div class="inline">
		<%= check_box_tag :remember_me %>
		<%= label_tag :remember_me %>
	</div>

	<%= submit_tag 'Log in', :class => 'submit' %>
<% end -%>
<p>Not a member?</p>
<p>Not a problem.</p>
<%= link_to 'Click here', signup_path %> to sign up!

app/views/users/forgot_password.html.erb

<% form_tag forgot_password_path, :id => 'forgot_password' do %>
	<h2>Forgot Your Password?</h2>
	<p>No worries. Just enter your email address below and we'll send you a fresh password.</p>
	<%= label_tag :email %>
	<%= text_field_tag :email %>

	<%= submit_tag 'Send me a Password', :class => 'submit' %>
<% end %>

app/models/user.rb

def forgot_password!
  self.password = self.password_confirmation = User.random_string(10)
  self.save
  Notifier.deliver_forgotten_password(self)
end

app/controllers/users_controller.rb

  def forgot_password
    if request.post?
      unless params[:email].blank?
        user = User.find_by_email(params[:email])
        respond_to do |format|
          if user
            user.forgot_password!
            format.html {
              flash[:notice] = "A new password has been sent to you. Please check your email."
              redirect_to login_path
            }
            format.js { render :text => "A new password has been sent to you. Please check your email." }
          else
            format.html {
              flash[:warning] = "We could not find a user with that email address."
              redirect_to login_path
            }
            format.js { render :text => "We could not find a user with that email address.", :status => 500 }
          end
        end
      end
    end
  end

app/models/notifier.rb

def forgotten_password(recipient, new_pass)
  recipients recipient.email
  from       "system@example.com"
  subject    "Your new password"
  body       :password => new_pass
  content_type "text/html"
end

app/views/notifiers/forgotten_password.html.erb

Your new password is: <%= @password %>. Please login and change it to something more memorable.

The following is my code that is an extensible function for creating the popups from external source (I might elaborate on it and turn it into a fun little plugin since I’m now using it on 4 different projects). I posted awhile back about the simplicity of using jQuery UI’s dialog to load contents of a form into a simple popup, this extends on it by taking the submit button out of the form, and adding it to the dialog so that it can use the dialog’s inherent “buttons” and their functions to do ajax calls. You certainly don’t have to do it this way, you could leave your button in your form and attach events to each individual button manually… I just find that this adapts to any form that opens in a dialog.

public/javascripts/screen.js

$(document).ready(function() {
  $('a.popup').click(function() {
    $('<div />').appendTo('body').dialog({
      title: $(this).attr('title'),
      modal: true
    }).load($(this).attr('href') + ' form', function() {
      $form = $(this).find('form')
      $form.find(':text:first').focus();
      $btn = $form.find(':submit');
      var txt = $btn.val();
      $btn.remove();
      var buttons = {};
      buttons[txt] = function() {
        $.ajax({
          type: $form.attr('method'),
          url: $form.attr('action'),
          data: $form.serialize(),
          dataType: 'script',
          complete: function(xhr, status) {
            $form.append('<div class="'+status+'">'+xhr.responseText+'</div>');
            return false;
          }
        });
      };
      $(this).dialog('option','buttons', buttons );
    });
    return false;
  });
});

There you have it. It works with or without Javascript. But with it turned on, when the user clicks the “Forgot Password” link on the login page, a dialog will popup with the form contents of the forgot_password.html.erb template. The user types their email address, clicks ‘Send me a Password’ and voila! If the email address is invalid, a message will appear below the form stating the error (you can change this by doing prepend or something where the $form.append line is in the ajax complete function). Or, if it is valid, the password gets reset, the email is sent and our success message appears below the form.

What this doesn’t do however, is close the popup on success. We could break out the ajax complete function into success and error functions, and have the success do a $(this).dialog(‘close’); … or you could be fancy, and set a timer, so that the user can see the success message but then the dialog closes itself after, say, 1 or 2 seconds.

Anywho, that’s all I have for today. :) Happy coding!

No related posts.

Related posts brought to you by Yet Another Related Posts Plugin.

This entry was posted in Development, jQuery, Ruby on Rails. Bookmark the permalink.

5 Responses to Fancy Forgot Password with jQuery/ui and Rails

  1. Kruby says:

    Hi Jen,
    Nice simple code.
    Would like to see your helper method random_string if possible.

  2. jen says:

    Hi Kruby,

    Sorry for the delay – apparently my comment notifications were off. Anyhow, I went digging through a ton of apps I’ve built and couldn’t find any that still had the random_string method in there. After I wrote this post, I started using authlogic for everything so it’s been a long time since I’ve had a need to send a user a new password via email.

    Cheers!

    ~jen

  3. GSP says:

    That random string method might be from here: http://www.aidanf.net/rails_user_authentication_tutorial

    That was where I copied my first Rails attempts of a user login.

    BTW: Great post. I’m in the process of migrating some of my old fashioned CRUD pages into a more ajaxy interface and I think I’ll start with something like this.

  4. Mark says:

    This wouldn’t work if your form contains any file upload fields, anybody know how to do that?

  5. jen says:

    Hey Mark, In order to do an AJAX post with file upload, you’re probably looking for an AJAX “fake”, such as using an iframe or Flash.

    Check out responses here: http://stackoverflow.com/questions/166221/how-can-i-upload-files-asynchronously-with-jquery

    Personally, I use uploadify (http://www.uploadify.com/)

    Either approach definitely requires some tweaking but it can certainly be done! :)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>