views:

57

answers:

2

Hopefully I can be clear on my issue. If you need more clarification on what I mean, please let me know and I'll try to clear up anything I can.

I am currently working on a simple Ruby on Rails project and I want to add some interactivity to my login form. Currently, the login form works fine if you click the Login link which then takes you to the new_user_sessions page. But I want to cut out the redirection and when the user clicks Login, the login form will be revealed in about 700 milliseconds on that same page. I plan to simply just use the function show(700) to reveal the login form. The problem I'm having is, I figured I could put this login form in the layouts directory so the user could have possibility of logging in from every page. The thing is my login form relies on a @user_session object from the User_sessions (using authlogic) controller. Here is what my new.html.erb file for the User_sessions controller contains:

<%= form_for @user_session do |f| %>
<%= render '/shared/error_messages', :target => @user_session %>

<p>
  <%= f.label :username %><br />
  <%= f.text_field :username %><br />
</p>
<p>
  <%= f.label :password %><br />
  <%= f.password_field :password %><br />
</p>
<p class="button">
  <%= f.submit "Submit" %>
</p>
<% end %>`

When a user clicks the Login link I would like this form to been shown via the show() function. Is there a way I can do this? Maybe by having some sort of render statement in the application.html.erb that renders this new User_sessions view? Thanks for any help.

A: 

Put your login form into a partial:

app/views/user_sessions/_login.erb

<%= form_for user_session do |f| %>
<%= render '/shared/error_messages', :target => user_session %>

<p>
  <%= f.label :username %><br />
  <%= f.text_field :username %><br />
</p>
<p>
  <%= f.label :password %><br />
  <%= f.password_field :password %><br />
</p>
<p class="button">
  <%= f.submit "Submit" %>
</p>
<% end %>

Render the partial from your layout:

app/views/layout/application.html.erb

<% if @user_session %>
  <%= render "user_sessions/login", :user_session => @user_session %>
<% end %>

And finally, make sure you set @user_session to an empty UserSession object where appropriate:

app/controllers/application_controller.rb

before_filter :set_user_session
protected
  def set_user_session
    # do some logic here to set @user_session when appropriate.
  end
Jacob
the layout has access to the @user_session? Meaning the if statement will return true?
Fizz
the layout has access to everything the normal view does. it just wraps all of your views.
Lukas
Well the good news is, it doesn't crash or give me some error. The bad news is, when I click Login in the animation isn't playing. I tried it with just text and the animation works fine, so its not the jQuery. I believe its that the partial is not being rendered. For this to work, must the partial be in the views/user_sessions directory because I have it under views/shared/.
Fizz
are you watching the jquery output via firebug or anything like that?
Lukas
No i'm not. Thats a good idea.
Fizz
I noticed that when I removed the if @user_session statement and just had the render statement I got the following error message: undefined method `model_name' for NilClass:Class
Fizz
That's because user_session is not getting set properly in the partial (form_for does some magic with activerecord). Most probably, your before filter is not correctly setting @user_session to a new UserSession object.
Jacob
Oh, and if you want to put the partial in /views/share, you should change the render line to `shared/login`.
Jacob
would it be enough to do something like `@user_session = UserSession.new` in the set_user_session function?
Fizz
Yes, but take care to use the same variable when you attempt to save the UserSession at logon, otherwise logon errors won't get passed to your view. Also, you probably don't want to show the login form if the user is already logged in.
Jacob
I finally got it working thanks. I just needed to setup that set_user_session function.
Fizz
A: 

In the view, use the above suggestion, by creating a login partial and have it render in something like

<div id="login_div" style="display:none">
    <%= render :partial => "login" %>
</div>

For the login link (the one that is supposed to show the login, you could just add the :remote => true property and then in your login action (the one that displays the login form),

respond_to do |wants|
   wants.html { redirect_to :login }
   wants.js { render :action => "login.js.erb" }
end

And then in your login.js.erb you can do

$("#login_div").show(700)

This way you can also support those who have JS disabled. Alternatively, you could do all this solely in the application.js file (if using jQuery):

$('#login_link').click(function(e) {
    e.preventDefault()
    $("#login_div").show(700)
}

This is a lot more unobtrusive solution.

Lukas
i'll try this out.
Fizz
The login.js.erb is a view now so that doesn't go in the public/javascript folder, but in the views/user_sessions/ directory correct?
Fizz
If i use your render statement as is I get the following error when it tries to render the partial: undefined local variable or method `user_session'
Fizz
re: login.js.erb, that is correct
Lukas
do you set @user_session in the action that does the render? you will need to define it before rendering. when you don't redirect, you only have the variables defined in the action that you call the render in. the `login` action does not get called.
Lukas
I figured the render would be done by the 'new' action. The only statement in this action is: `@user_session = UserSession.new`
Fizz
it will need to be in the same action as the js render statement. have you tried the other jQuery method? that only requires the hidden div and the last code block in the above solution.
Lukas