different layout for sign_in action in devise

2019-01-10 01:19发布

问题:

I'm trying to use a different/custom layout named "devise" for the sign_in action. I found this page in the devise wiki, and the second example even says you can do it per-action (in this case, sign_in action), but it shows no example of doing that. Someone on IRC told me I could try this:

class ApplicationController < ActionController::Base
  protect_from_forgery

  layout :layout_by_resource

  def layout_by_resource
    if devise_controller? && resource_name == :user && action_name == 'sign_in'
      "devise"
    else
      "application"
    end
  end
end

But it does not seem to be working as it's still loading the default application layout. I would appreciate any help.

回答1:

Another way to apply custom layout for an action is as following.

According to How To: Create custom layouts "You can also set the layout for specific Devise controllers using a callback in config/environment.rb (rails 2) or config/application.rb (rails 3). This needs to be done in a to_prepare callback because it's executed once in production and before each request in development."

config.to_prepare do
    Devise::SessionsController.layout "devise"
    Devise::RegistrationsController.layout proc{ |controller| user_signed_in? ? "application"   : "devise" }
    Devise::ConfirmationsController.layout "devise"
    Devise::UnlocksController.layout "devise"            
    Devise::PasswordsController.layout "devise"        
end

Usually a layout distinction is made between pages behind login and pages which do not require authentication, so the above approach works most of the time. But I also experimented with using action_name helper to set a layout for a particular action and it worked like charm:

config.to_prepare do
    Devise::SessionsController.layout proc{ |controller| action_name == 'new' ? "devise"   : "application" }
end

I think this is the better and built in way to change the layout based on devise controller/action instead of creating a helper in ApplicationController.



回答2:

I just created app/views/layouts/devise/sessions.html.erb and put my layout in there.



回答3:

I figured it out, but I'll keep this question here in case other people are curious.

It was a stupid mistake. The fact is sign_in is the path, not the action. Looking at the relevant source, I can see that the required action is new, i.e., creating a new Devise Session. Changing my above code's conditional to:

if devise_controller? && resource_name == :user && action_name == 'new'

Works beautifully.

Hope that helps someone out there.



回答4:

This is how I did it. I wanted a different layout if the user had to sign in, but a different layout if the user had to edit his/her profile.

I am using Rails 4.1.1

In the application controller, add this :

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?

  layout :layout_by_resource

  # Define the permitted parameters for Devise.
  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:firstname, :lastname, :email, :password, :password_confirmation)}
    devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:avatar, :firstname, :lastname, :email, :password, :password_confirmation, :current_password) }
  end

  def layout_by_resource
    if devise_controller? and user_signed_in?
      'dashboard'
    else
      'application'
    end
  end
end


回答5:

The by far simplest solution is to just create a layout called devise.html.haml in your app/views/layouts folder. and the Rails magic takes care of the rest.

app/views/layouts/devise.html.haml


回答6:

Surprised to not see this answer anywhere, but you can also do this:

In routes.rb, change your devise config to look something like this:

  devise_for :users, controllers: {
    sessions: 'sessions'
  }

Then in app/controllers/sessions_controller.rb

class SessionsController < Devise::SessionsController
  layout 'devise', only: [:new]
end

This is especially useful if you need to do additional logic overrides in any of the Devise controllers.



回答7:

Just incase you didn't know, you can also use rake routes to see the routes in your rails app along with the action/controller they map to.

 new_user_registration GET    /accounts/sign_up(.:format)       {:action=>"new", :controller=>"devise/registrations"}
edit_user_registration GET    /accounts/edit(.:format)          {:action=>"edit", :controller=>"devise/registrations"}
                       PUT    /accounts(.:format)               {:action=>"update", :controller=>"devise/registrations"}
                       DELETE /accounts(.:format)               {:action=>"destroy", :controller=>"devise/registrations"}


回答8:

Here's a one-liner for those who want all devise actions to use a new layout:

class ApplicationController < ActionController::Base
  protect_from_forgery

  layout Proc.new { |controller| controller.devise_controller? ? 'devise' : 'application' }
end