Rails 4 ajax:error not triggering unless ajax:success has been triggered first

1k views Asked by At

I can't figure out what I am doing incorrectly when trying to load ActiveRecord validation errors with AJAX. I cannot get the ajax:error to trigger unless I have already submitted a valid form and triggered ajax:success. I have tried only binding the ajax:error to see if the success could be blocking it. I have read through the rails documentation on working with javascript in rails, and Googled my problem to no avail. I have tried to use both .bind() and .on() but I haven't seen a difference. I am sure there is something incredibly simple that I am missing, but I have been looking at this too long and I don't seem to be getting anywhere.

Here is the code.

Model

class User < ActiveRecord::Base
  before_create :set_activation_key
  before_save :encrypt_password
  before_save :downcase_username
  after_save :clear_password

  validates :username,
            format: {
                with: /\A.*\z/,
                message: 'invalid'
            },
            uniqueness: {message: 'already in use'},
            presence: {message: 'cannot be blank'}

  validates :email_address,
            format: {
                with: /\A.+@.+\..+\z/,
                message: 'invalid'
            },
            uniqueness: {message: 'already in use'},
            presence: {message: 'cannot be blank'}


  validates :password, :confirmation => true, 
            presence: {message: 'cannot be blank'},
            length: {:within => 6..20,
                     :too_long => 'too long',
                     :too_short => 'too short',
            }

  def encrypt_password
    if password.present?
      self.salt = BCrypt::Engine.generate_salt
      self.password = BCrypt::Engine.hash_secret(password, salt)
    end
  end

  def clear_password
    self.password = nil
  end

  def set_activation_key
    self.activation_key = SecureRandom.urlsafe_base64
  end

  def downcase_username
    self.username = self.username.downcase
  end

  def self.authenticate(username, password)
    user = User.find_by_username(username.to_s.downcase)
    if user && user.password == BCrypt::Engine.hash_secret(password, user.salt)
      user
    else
      nil
    end
  end
end

Controller

class Users::UsersController < ApplicationController

  # GET /register
  def new
    @user = User.new
  end

  # POST /users
  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        UserMailer.registration_email(@user.email_address).deliver
        format.html { redirect_to root_path, notice: 'Check your email' }
        format.js
      else
        format.html { render action: 'new' }
        format.js { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  private

  def user_params
    params.require(:user).permit(:username, :email_address, :password, :password_confirmation)
  end
end

Partial

<%= form_for User.new, remote: true do |f| %>
    <div id="registration_messages"></div>
    <div id="registration_errors"></div>
    <%= f.label :username %>
    <%= f.text_field :username%>

    <%= f.label :email_address %>
    <%= f.text_field :email_address %>

    <%= f.label :password %>
    <%= f.password_field :password %>

    <%= f.label :password_confirmation, 'Confirm Password' %>
    <%= f.password_field :password_confirmation %>

    <%= f.submit 'Register', :class => 'tiny button' %>
    <a class="close-reveal-modal">&#215;</a>
<% end %>

create.js.erb

$(document).ready(function() {
    $('#new_user')
            .bind("ajax:success", function(event, data, xhr, status){
                var $form = $(this);
                $form.find('input[type=text], input[type=password], textarea').val('');
                $("div#registration_messages").append('<div data-alert class="alert-box success">Check your email</div>');
            }).bind("ajax:error", function(event, data, xhr, error){
                console.log(event);
                console.log(data);
                console.log(xhr);
                console.log(error);
                alert("ajax:error");
            });
});
1

There are 1 answers

7
steel On BEST ANSWER

This doesn't help you with your current technique, but you could try doing it in pure JQuery and ditch the js.erb file. I've always found this way more straightforward once you get the hang of it. I would expect something like the following code not to produce the issue you're seeing.

controller

# POST /users
def create
  @user = User.new(user_params)

  respond_to do |format|
    if @user.save
      UserMailer.registration_email(@user.email_address).deliver
      format.html { redirect_to root_path, notice: 'Check your email' }
      format.js { render nothing: true, status: :created }
    else
      format.html { render action: 'new' }
      format.js { render json: @user.errors, status: :unprocessable_entity }
    end
  end
end

javascript.js

  # could go in application.js while you're testing it, but best to factor it out into it's own file.
  $('form#new_user').on('ajax:success', function(event, data, xhr, status) {
    var $form = $(this);
    $form.find('input[type=text], input[type=password], textarea').val('');
    $("div#registration_messages").append('<div data-alert class="alert-box success">Check your email</div>');
  });
  $('form#new_user').on('ajax:error', function(event, data, xhr, status) {
    console.log(event);
    console.log(data);
    console.log(xhr);
    console.log(error);
    alert("ajax:error");
  });