Custom registration forms for different roles using Devise and Rolify on Rails

89 Views Asked by At

We're trying to create a conditional statement inside Devise's new registration form so that different fields are shown for different types of roles. The problem is that the role itself should already be decided before the registration form is rendered, so we implemented a select_role view where the user chooses between 2 roles: user and volunteer. This information is stored for the purpose of creating the new registration and the user is redirected to the form page with its correct conditional.

We tried to change Devise's sign up shared link from <%= link_to "Sign up", new_registration_path(resource_name) %> to <%= link_to "Sign up", select_role_path %>, but if in the select_role method in the registrations controller we add the redirect_to new_user_registration_path line, the sign up link just redirects straight to the devise form without the user making their choice and if we remove this line and get to the select_role view, the submit button just renders the current page again.

I. app/views/devise/registrations/new.html.erb

<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">
    <%= f.input :name, required: true, input_html: { autocomplete: "name" } %>

    ...

    <%= f.input :password_confirmation, required: true, input_html: { autocomplete: "new-password" } %>
    <% if session[:chose_volunteer] %>
      <%= f.input :profession %>
      <%= f.input :professional_registration %>
      <%= f.input :specialty %>
      <%= f.hidden_field :roles, value: 'volunteer' %>
    <% else %>
      <%= f.hidden_field :roles, value: 'user' %>
    <% end %>
  </div>

II. app/views/users/registrations/select_role.html.erb

<%= form_tag select_role_path, method: :post do %>
  <%= select_tag :role_id, options_for_select(Role.where(name: ['user', 'volunteer']).pluck(:name, :id)) %>
  <%= submit_tag "Select Role" %>
<% end %>

III. app/controllers/users/registrations_controller.rb

class Users::RegistrationsController < Devise::RegistrationsController
  def select_role
    chose_volunteer = params[:role_id] == Role.find_by(name: 'volunteer').id.to_s
    session[:chose_volunteer] = chose_volunteer
    #redirect_to new_user_registration_path
  end
end

IV. app/controllers/registrations_controllers.rb

class RegistrationsController < Devise::RegistrationsController
  before_action :configure_sign_up_params, only: [:create]

  def create
    super
    resource.add_role(params[:user][:roles])
  end

  protected

  def configure_sign_up_params
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :age, :phone, :city, :state, :country, :address, :instagram, :profession, :professional_registration, :specialty, roles: []])
  end
end

V. config/routes.rb

Rails.application.routes.draw do
  devise_for :users, controllers: { registrations: "registrations" }
  root to: "pages#home"
  devise_scope :user do
    get 'select_role', to: 'users/registrations#select_role'
  end
  resources :users
  resources :forum_threads do
    resources :forum_posts, only: [:create]
  end
end

There are no create or select_role methods in my users controller.

How do I correctly redirect the user to the new registration form from the select_role page with the role choice being carried over?

1

There are 1 best solutions below

1
On

I would just create separate routes, controllers and views for the two different kinds of registrations. It's a lot less wonky and good OOP.

# config/routes.rb
# ...
devise_for :volunteers, 
   class_name: "User",
   module: "volunteers",
   only: :registrations
class RegistrationsController < ::Devise::RegistrationsController
  def create
    super do |resource|
      resource.add_role(resource_role)
    end
  end

  private
  # this is a method we invented
  def resource_role
    :user
  end
end
module Volunteers
  class RegistrationsController < ::RegistrationsController

    private

    def resource_role
      :volunteer
    end
  end
end
<%= link_to "Volunteer registration", new_volunteer_registration_path %>

Code reuse can be acheived by inheritance, mixins and partials.