Ismoh
Ismoh

Reputation: 1104

form_for - First argument in form cannot contain nil or be empty

If I type a user's username and password, everything works fine, BUT if I type nothing or just the username the following error appears:

First argument in form cannot contain nil or be empty in <%= form_for @user, :as => :user, :url => user_sign_in_path(@user), :html => {:class => 'navbar-form', :role => 'login'} do |user_form_builder|%>

app/views/user/sign_in.html.erb:10:in '_app_views_user_sign_in_html_erb__420697496_32907636' app/controllers/user_controller.rb:23:in 'login'

I think you'll need the controller, route and view:

View:

<%= form_for @user, :as => :user, :url => user_sign_in_path(@user), :html => {:class => 'navbar-form', :role => 'login'} do |user_form_builder|%>
      <div class="form-group text-xs">
        <%= user_form_builder.text_field :username, :class => 'form-control input-xs', :placeholder => 'eMail or Username'%><br>
        <%= user_form_builder.password_field :password, :class => 'form-control input-xs', :placeholder => 'Passwort'%><br>
        <div class="checkbox pull-left">
          <label class="">
            <input type="checkbox"> Angemeldet bleiben
          </label>
        </div>
        <%= user_form_builder.submit 'Anmelden', :class => 'btn btn-success btn-xs pull-right'%><br><br>
        oder <button type="button" class="btn btn-success btn-xs">Registrieren</button>
      </div>
    <% end %>

Controller:

class UserController < ApplicationController
  def sign_in
    @user = User.new
  end

  def login
    username_or_email = params[:user][:username]
    password = params[:user][:password]

    if username_or_email.rindex('@')
      email = username_or_email
      user = User.authenticate_by_email(email, password)
    else
      username = username_or_email
      user = User.authenticate_by_username(username, password)
    end

    if user
      flash[:notice] = 'Welcome'
      redirect_to :root
    else
      flash[:error] = 'Unknown user. Please check your username and password.'
      render :action => "sign_in"
    end
  end

  def change_pw
  end

  def forgot_pw
  end

  def new
  end

  def sent_pw
  end

  def signed_out
  end
end

Routes:

Calendar::Application.routes.draw do
  root "welcome#index"

  get "user/change_pw" => "user#change_pw"
  get "user/forgot_pw" => "user#forgot_pw"
  get "user/new" => "user#new"
  get "user/sent_pw" => "user#sent_pw"
  get "user/sign_in" => "user#sign_in"
  get "user/signed_out" => "user#signed_out"
  get "welcome/index"

  post "user/sign_in" => "user#login"
end

I can't find the problem.

Upvotes: 0

Views: 600

Answers (2)

vee
vee

Reputation: 38645

On failure to authenticate, you are rendering sign_in action. With render, as opposed to redirect_to, the action does not execute; it only renders the action's view. So, the expected @user instance variable is not defined in the login action which is the reason for this error.

Try updating login action as follows:

  def login
    ...

    if user
      flash[:notice] = 'Welcome'
      redirect_to :root
    else
      flash[:error] = 'Unknown user. Please check your username and password.'

      # Assign user to instance variable for the `sign_in` view!
      @user = user 

      render :action => "sign_in"
    end
  end

And instead of defining an extra variable, you could choose to replace all local user variables with instance variables as @user in your login action.

Update: The quickest workaround to this problem would be to create a new user with the supplied parameters as follows:

def login
    ...

    if user
      flash[:notice] = 'Welcome'
      redirect_to :root
    else
      flash[:error] = 'Unknown user. Please check your username and password.'

      # Create new user instance variable for the `sign_in` view!
      @user = User.new(params[:user]) 

      render :action => "sign_in"
    end
  end

Upvotes: 2

Kirti Thorat
Kirti Thorat

Reputation: 53038

Update login method as below:

  def login
    username_or_email = params[:user][:username]
    password = params[:user][:password]

    if username_or_email.rindex('@')
      email = username_or_email
      user = User.authenticate_by_email(email, password)
    else
      username = username_or_email
      user = User.authenticate_by_username(username, password)
    end

    if user
      flash[:notice] = 'Welcome'
      redirect_to :root
    else
      flash[:error] = 'Unknown user. Please check your username and password.'
       @user = User.new   ## Set @user instance variable
      render :action => "sign_in"
    end
  end

You are getting error because @user instance variable is not set in case of authentication failure.

Also, you might want to replace user with @user as suggested by Vee. Otherwise, if you are accessing @user in case of successful authentication (on root page), then again you'll get error.

Upvotes: 1

Related Questions