简体   繁体   中英

Rails 4: Rendering devise error messages in custom form

I have 2 different devise registration forms, one for normal users and another for vendors, well I created a custom registrations controller and it is correctly working:

class RegistrationsController < Devise::RegistrationsController
    def create
        super
        if resource.save
            if params[:role] == 'vendedor'
                resource.add_role :vendedor
            end
        end
    end

    #In this action I render the custom form for vendors registration
    def new_with_role
        build_resource({})
        case params[:role]
        when 'proveedor'

        else
            redirect_to root_path, alert: 'Unexpected error'
        end
    end
end

I created this route for the custom form for vendors:

devise_scope :user do
    get "/sign_up/:role" => 'registrations#new_with_role', as: 'new_user_with_role'
  end

And this is the form views/devise/registrations/new_with_role.html.erb

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), builder: FoundationFormBuilder) do |f| %>
        <%= f.text_field :name, label: "Company name", autofocus: true %>
        <%= f.email_field :email, label: "Correo electrónico(ideal: Gmail, Hotmail, Outlook, Yahoo)" %>
        <%if @validatable %>
          <%= f.password_field :password, autocomplete: "off", hint: "#{@minimum_password_length} caracteres como mínimo" %>
        <% else %>
          <%= f.password_field :password, autocomplete: "off" %>
        <% end %>
        <%= f.password_field :password_confirmation, label: "Confirmar contraseña", autocomplete: "off" %>
        <%= hidden_field_tag :role, 'vendedor' %> #With this custom param I can set the role of the user in the create action of the custom registrations controller
        <%= f.submit "Enviar", data: { disable_with: "Wait please..." } %>
    <% end %>

The form works great, however I can't figure out how to render the errors in this custom form, When I send the form with intentional errors I get the following:

undefined method `errors' for nil:NilClass

Checking the logs I could find that when there are errors the create action of my RegistrationsController is rendering the new action of Devise, and I think that is causing the error, I want to render the new_with_role action if there is an error filling its form.

These are the logs when I send the form for vendors with intentional mistakes:

Started POST "/users" for ::1 at 2016-02-01 15:33:36 -0500
Processing by RegistrationsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"kwiV0+GwiGTYVQf0iFE19YoZqZKnAgqzGkTr3D9OCYy03ZPy7xC4ZjFSlnfItE4fuofRAg9jMJ3wIN+si8oR+w==", "user"=>{"last_name"=>"", "phone1"=>"", "first_name"=>"", "email"=>"", "preference_details"=>"", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "business_line"=>"2", "role"=>"vendedor", "commit"=>"Enviar"}
   (0.4ms)  BEGIN
  User Exists (1.4ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` = BINARY '' LIMIT 1
   (0.3ms)  ROLLBACK
  Rendered devise/registrations/new.html.erb within layouts/application (84.1ms)
Completed 500 Internal Server Error in 224ms (ActiveRecord: 2.1ms)

ActionView::Template::Error (undefined method `errors' for nil:NilClass):
    11:     <% end %>
    12:     <h5 class="subtitle">O regístrate de la forma tradicional</h5>
    13:     <%= form_for(:user, as: resource_name, url: registration_path(resource_name), builder: FoundationFormBuilder) do |f| %>
    14:         <%= f.text_field :first_name, label: "Nombre", autofocus: true %>
    15:         <%= f.text_field :last_name, label: "Apellido" %>
    16:         <%= f.email_field :email, label: "Correo electrónico" %>
    17:         <div class="row collapse">
  lib/foundation_form_builder.rb:75:in `errors_on?'
  lib/foundation_form_builder.rb:21:in `block (2 levels) in <class:FoundationFormBuilder>'
  app/views/devise/registrations/new.html.erb:14:in `block in _app_views_devise_registrations_new_html_erb__4160932045667554001_70342326706100'
  app/views/devise/registrations/new.html.erb:13:in `_app_views_devise_registrations_new_html_erb__4160932045667554001_70342326706100'
  app/controllers/registrations_controller.rb:3:in `create'


  Rendered /Users/sebastianvelandiagiraldo/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/actionpack-4.2.5/lib/action_dispatch/middleware/templates/rescues/_source.erb (9.4ms)
  Rendered /Users/sebastianvelandiagiraldo/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/actionpack-4.2.5/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (4.2ms)
  Rendered /Users/sebastianvelandiagiraldo/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/actionpack-4.2.5/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (2.0ms)
  Rendered /Users/sebastianvelandiagiraldo/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/actionpack-4.2.5/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb within rescues/layout (43.8ms)

As you can see when there are errors on the form the rendered action is:

Rendered devise/registrations/new.html.erb

it should be:

Rendered devise/registrations/new_with_role.html.erb

I guess that's causing the issue. Any help would be very appreciated

Update 1

I found this issue that is similar, but the answer provided there is useless.

Update 2

Code of the create action of my current Devise version 3.5.2 as requested

def create
  build_resource(sign_up_params)

        resource.save
        yield resource if block_given?
        if resource.persisted?
            if resource.active_for_authentication?
                set_flash_message :notice, :signed_up if is_flashing_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_flashing_format?
                expire_data_after_sign_in!
                respond_with resource, location: after_inactive_sign_up_path_for(resource)
            end
        else
            clean_up_passwords resource
            set_minimum_password_length
            if params[:role].present?
                render :new_with_role
            else
                respond_with resource
            end
        end
end

The form submit action of your custom view is create action:

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), builder: FoundationFormBuilder) do |f| %>

and the action does not tell to render new view in case of error. There isn't any code that mentions that. So either call a custom submit action that renders the custom view in case of error, or instead of calling super, change action code based on some param to render different templates.

If you want a cleaner approach, use the devise json API from your custom views via ajax calls. Here is a link for how you can use the API: http://blog.andrewray.me/how-to-set-up-devise-ajax-authentication-with-rails-4-0/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM