Rails 4 Devise Login as a POPUP window

2019-03-16 02:01发布

问题:

Hi Iam using devise for my authentication of the user

I have followed the various tutorials for getting the login in a popup window but Iam not successful can any one kindly tell how do it in details. I have tried various tutorials but nothing is working every thing is redirecting again in to a login page.

I am a newbie in rails Please kindly help to this.

I am unable to get the login on the same page

Here Is my new.html.erb

<%= form_for(:user,  :html => {:id => 'login_form'}, :url => user_session_path, :remote => :true, :format => :json) do |f| %>

  <%= f.email_field :email, :autofocus => true, :placeholder => "email", :id => 'login_email' %>
  <%= f.password_field :password, :placeholder => "password", :id => 'login_password' %>

  <% if devise_mapping.rememberable? -%>
    <div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
  <% end -%>

  <div><%= image_submit_tag('modals/account/login_submit.png') %></div>
<% end %>

This is my headers page

 <% if user_signed_in? %>
  <p>Welcome <%= current_user.email %></p>
   <%= link_to "Sign out", destroy_user_session_path, :method => :delete %>
<% else %>
  <p>You are not signed in.</p>
  <%= link_to "Sign in", new_user_session_path, :remote => :true %>

<% end %>

回答1:

Firstly, do you want a pop-up or a pop-over?

Pop-ups are new Windows, and are reminiscent of 90's ads, whereas pop-overs (sometimes called modals) are integrated elements in the UI, and are basically just a div on a darkened background

All Web 2.0 sites use pop-overs to enhance usability, whereas pop-ups detract from the experience :)


Pop-Overs

We have implemented the functionality you seek (click "login" at the top of this development site)

There are several things you have to get right to make this work - I'll detail them for you here:


Routes

#config/routes.rb
devise_for :users, :path => '', :controllers => {:sessions => 'sessions', :registrations => 'registrations'}, :path_names => { :sign_in => 'login', :password => 'forgot', :confirmation => 'confirm', :unlock => 'unblock', :registration => 'register', :sign_up => 'new', :sign_out => 'logout'}

First step is to get your routes sorted

Although Devise has good routing documentation, you'll need to customize your routes to include customized session / registration controllers, as well as any custom paths you want

The most important part of the routes is (you need custom controllers!):

:controllers => {:sessions => 'sessions', :registrations => 'registrations'}

Controllers

Next, you need to add ajax response functionality to your sessions and registrations controllers. Even though Devise adds these controllers with every install, they remain hidden, and if you want to add functionality, you'll have to extend them

Here are our sessions & registrations controllers (LOTS of this code can be removed, but it works for us right now):

Session

#app/controllers/sessions_controller.rb
class SessionsController < DeviseController
  prepend_before_filter :require_no_authentication, :only => [ :new, :create ]
  prepend_before_filter :allow_params_authentication!, :only => :create
  prepend_before_filter { request.env["devise.skip_timeout"] = true }

  prepend_view_path 'app/views/devise'

  # GET /resource/sign_in
  def new
    self.resource = resource_class.new(sign_in_params)
    clean_up_passwords(resource)
    respond_with(resource, serialize_options(resource))
  end

  # POST /resource/sign_in
  def create
    self.resource = warden.authenticate!(auth_options)
    set_flash_message(:notice, :signed_in) if is_navigational_format?
    sign_in(resource_name, resource)

    respond_to do |format|
        format.json { render :json => {}, :status => :ok }
        format.html { respond_with resource, :location => after_sign_in_path_for(resource) } 
    end
  end

  # DELETE /resource/sign_out
  def destroy
    redirect_path = after_sign_out_path_for(resource_name)
    signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
    set_flash_message :notice, :signed_out if signed_out && is_navigational_format?

    # We actually need to hardcode this as Rails default responder doesn't
    # support returning empty response on GET request
    respond_to do |format|
      format.all { head :no_content }
      format.any(*navigational_formats) { redirect_to redirect_path }
    end
  end


  protected

  def sign_in_params
    devise_parameter_sanitizer.sanitize(:sign_in)
  end

  def serialize_options(resource)
    methods = resource_class.authentication_keys.dup
    methods = methods.keys if methods.is_a?(Hash)
    methods << :password if resource.respond_to?(:password)
    { :methods => methods, :only => [:password] }
  end

  def auth_options
    { :scope => resource_name, :recall => "#{controller_path}#new" }
  end
end

Registration

#app/controllers/registrations_controller.rb
class RegistrationsController < DeviseController
  prepend_before_filter :require_no_authentication, :only => [ :new, :create, :cancel ]
  prepend_before_filter :authenticate_scope!, :only => [:edit, :update, :destroy]

  before_filter :configure_permitted_parameters

  prepend_view_path 'app/views/devise'

  # GET /resource/sign_up
  def new
    build_resource({})
    respond_with self.resource
  end

  # POST /resource
  def create
    build_resource(sign_up_params)

    if resource.save
      if resource.active_for_authentication?
        set_flash_message :notice, :signed_up if is_navigational_format?
        sign_up(resource_name, resource)
        respond_with resource, :location => after_sign_up_path_for(resource)
      else
        set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
        expire_session_data_after_sign_in!
        respond_with resource, :location => after_inactive_sign_up_path_for(resource)
      end
    else
      clean_up_passwords resource

      respond_to do |format|
        format.json { render :json => resource.errors, :status => :unprocessable_entity }
        format.html { respond_with resource }
      end
    end
  end

  # GET /resource/edit
  def edit
    render :edit
  end

  # PUT /resource
  # We need to use a copy of the resource because we don't want to change
  # the current user in place.
  def update
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
    prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)

    if update_resource(resource, account_update_params)
      if is_navigational_format?
        flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ?
          :update_needs_confirmation : :updated
        set_flash_message :notice, flash_key
      end
      sign_in resource_name, resource, :bypass => true
      respond_with resource, :location => after_update_path_for(resource)
    else
      clean_up_passwords resource
      respond_with resource
    end
  end

  # DELETE /resource
  def destroy
    resource.destroy
    Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
    set_flash_message :notice, :destroyed if is_navigational_format?
    respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
  end

  # GET /resource/cancel
  # Forces the session data which is usually expired after sign
  # in to be expired now. This is useful if the user wants to
  # cancel oauth signing in/up in the middle of the process,
  # removing all OAuth session data.
  def cancel
    expire_session_data_after_sign_in!
    redirect_to new_registration_path(resource_name)
  end

  protected

  # Custom Fields
  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) do |u|
      u.permit(:first_name, :last_name,
        :email, :password, :password_confirmation)
    end
  end

  def update_needs_confirmation?(resource, previous)
    resource.respond_to?(:pending_reconfirmation?) &&
      resource.pending_reconfirmation? &&
      previous != resource.unconfirmed_email
  end

  # By default we want to require a password checks on update.
  # You can overwrite this method in your own RegistrationsController.
  def update_resource(resource, params)
    resource.update_with_password(params)
  end

  # Build a devise resource passing in the session. Useful to move
  # temporary session data to the newly created user.
  def build_resource(hash=nil)
    self.resource = resource_class.new_with_session(hash || {}, session)
  end

  # Signs in a user on sign up. You can overwrite this method in your own
  # RegistrationsController.
  def sign_up(resource_name, resource)
    sign_in(resource_name, resource)
  end

  # The path used after sign up. You need to overwrite this method
  # in your own RegistrationsController.
  def after_sign_up_path_for(resource)
    after_sign_in_path_for(resource)
  end

  # The path used after sign up for inactive accounts. You need to overwrite
  # this method in your own RegistrationsController.
  def after_inactive_sign_up_path_for(resource)
    respond_to?(:root_path) ? root_path : "/"
  end

  # The default url to be used after updating a resource. You need to overwrite
  # this method in your own RegistrationsController.
  def after_update_path_for(resource)
    signed_in_root_path(resource)
  end

  # Authenticates the current scope and gets the current resource from the session.
  def authenticate_scope!
    send(:"authenticate_#{resource_name}!", :force => true)
    self.resource = send(:"current_#{resource_name}")
  end

  def sign_up_params
    devise_parameter_sanitizer.sanitize(:sign_up)
  end

  def account_update_params
    devise_parameter_sanitizer.sanitize(:account_update)
  end
end

Forms

Once you've added the backend functionality, you have to make your forms ajax-enabled. This is pretty simple:

Login

#app/views/devise/sessions/new.html.erb
<%= form_for(devise_resource, :as => devise_resource_name, :html => {:id => 'login_form'}, :url => user_session_path, :remote => :true, :format => :json) do |f| %>

  <%= f.email_field :email, :autofocus => true, :placeholder => "email", :id => 'login_email' %>
  <%= f.password_field :password, :placeholder => "password", :id => 'login_password' %>

  <% if devise_mapping.rememberable? -%>
    <div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
  <% end -%>

  <div><%= image_submit_tag('modals/account/login_submit.png') %></div>
<% end %>

Register

#app/views/devise/registrations/new.html.erb
<%= form_for(devise_resource, :as => devise_resource_name, :html => {:id => 'register_form'}, :url => user_registration_path, :remote => :true, :format => :json) do |f| %>

    <div class="name_input_container">
        <div class="name_input_cell">
            <%= f.text_field :first_name, :autofocus => true, :class => 'first_name', :placeholder => 'first name' %>
            <div class="error"><%= devise_resource.errors[:first_name].first if devise_resource.errors[:first_name].present? %></div>
        </div>

        <div class="name_input_cell_right">
            <%= f.text_field :last_name, :class => 'last_name', :placeholder => 'last name' %>
            <div class="error"><%= devise_resource.errors[:last_name].first if devise_resource.errors[:last_name].present? %></div>
        </div>
    </div>

    <%= f.email_field :email, :placeholder => "email" %>
    <div class="error"><%= devise_resource.errors[:email].first if devise_resource.errors[:email].present? %></div>

    <%= f.password_field :password, :placeholder => "password", :title => "8+ characters" %>
    <div class="error"><%= devise_resource.errors[:password].first if devise_resource.errors[:password].present? %></div>

    <%= f.password_field :password_confirmation, :placeholder => "confirm password" %>
    <div class="error"><%= devise_resource.errors[:password_confirmation].first if devise_resource.errors[:password_confirmation].present? %></div>

    <div class="option_buttons">
        <div class="already_registered">
            <%= link_to 'already registered?', '#', :class => 'already_registered', :id => 'already_registered', :view => 'login' %>
        </div>
        <%= image_submit_tag('modals/account/register_submit.png', :class => 'go') %>
        <div class="clear"></div>
    </div>
<% end %>

Javascript

Finally, you just need to be able to handle the responses from Ajax:

Login

//app/assets/javscripts/application.js
$(document).on('submit', '#login_form', function(e) {
            //do stuff here
}).on('ajax:success', '#login_form', function(e, data, status, xhr) {
        //do stuff here 
}).on('ajax:error', '#login_form', function(e, data, status, xhr) {
        //do stuff here
});

Register

//app/assets/javascripts/application.js
$(document).on('submit', '#register_form', function(e){
    //do stuff here
}).on('ajax:success', '#register_form', function(e, data, status, xhr) {
    //do stuff here
}).on('ajax:error', '#register_form', function(e, data, status, xhr) {
       //do stuff here  
});