简体   繁体   中英

Ruby on Rails / Devise - Split user edit page into 2 pages

I'm trying to split the users edit page (app/views/devise/registrations/edit.html.erb) into 2 pages for better UI, like:

/settings 
/settings/profile

I'm fairly new to Rails, did Michael Hartl's tutorial and had read a few more I got my hands on, just building my first application, even if I have some experience with php

This is the view I try to split in 2, it is a view provided by the Devise gem (app/views/devise/registrations/edit.html.erb)

<h2>Login Details</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
  <%= devise_error_messages! %>

  <%# sensitive info %>
  <%= f.label :email %><br />
  <%= f.email_field :email, autofocus: true %>
  ....
  <%= f.label :current_password %> <i>(to confirm changes)</i><br />
  <%= f.password_field :current_password, autocomplete: "off" %>

  <h2>Profile Details</h2>

  <%# non-sensitive info %>
  <%= f.label :name %><br />
  <%= f.text_field :name %>

  <%= f.submit "Update" %>
<% end %>

It uses a custom RegistrationsController (app/controllers/registrations_controller.rb)

class RegistrationsController < Devise::RegistrationsController
  def update
    ....
  end
end

Further more, this view is accessed via this route:

edit_user GET      /users/:id/edit(.:format)              users#edit

My main question is, how do I split this page into 2:

  • /settings containing Login Details
  • /settings/profile containing Profile details

and both to processed by the same controller, or the same action

Do I need to create a new controler/route/view, like:

  • controller: SettingsProfile
  • route: get 'settings/profile' => 'settings_profile#new'
  • view: app/views/settings_profile/new.html.erb

If so how do I pass the view the "resource" information, or any information for the matter of fact:

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>

Things are pretty fuzzy at this point, please bear with me on this one

As you suggested, one method would be to create a new controller, route, and view to handle this.

I might create a UserProfilesController controller with two actions: UserProfilesController#show and UserProfilesController#edit .

Then as you suggested, a route, eg,

get 'user_profiles/:id' => 'user_profiles#show'
get 'user_profiles/:id/edit' => 'user_profiles#edit'

In Devise parlance, the resource refers to the user. So the :id being passed above must be a User id of course. If you don't want to do that, you could always just assume you meant the current_user in which case you can skip using :id in the routes and just retrieve it in the controllers via current_user.id .

Finally, you just have to split out the profile details from the Devise view and create some under app/views/user_profiles/new.html.erb and similarly for edit.html.erb . Remember to remove the profile bits from the Devise view and I think you're on your way.

An Addendum

@AmrNoman made a good suggestion re: the update method. If you are following with my solution, you would add another action UserProfilesController#update , and a new route in your routes.rb file:

put 'user_profiles/:id/update' => 'user_profiles#update'

Additionally, if you intend to later refactor User to remove the profile details and handle them in a separate model, it may be prudent to replace my references to :id in the above code to :user_id . In this way, if you at some point create, eg, a model called UserProfile it will be clearer that the :id is not the UserProfile#id but the UserProfile#user_id foreign key. This will leave you the ability to use :id to refer to UserProfile.id directly without affecting any API consumer in your app.

It may be a bit overkill but I think it's good practice.

Chris Cameron's answer is right, but I think this is closer to what you want:

First create your routes in routes.rb :

  # this gets the edit page for login details
  get "settings" => "user_profiles#edit_credentials", as: "edit_credentials"

  # this gets the edit page for other profile info
  get "settings/edit" => "user_profiles#edit_profile", as: "edit_profile"

  # update method shared by both forms 
  # (you can create another one to handle both forms separately if you want)
  put "settings" => "user_profiles#update"

Then your controller user_profiles_controller.rb :

class UserProfilesController < ApplicationController
  # fill the methods as you need, you can always get the user using current_user

  def edit_credentials
  end

  def edit_profile
  end

  def update
  end
end

and finally your views, first views/user_profiles/edit_credentials.erb.html , here you show form for login details:

<%= form_for(current_user, url: settings_path) do |f| %>
<% end %>

then same thing in views/user_profiles/edit_profile.erb.html , just change your form contents:

<%= form_for(current_user, url: settings_path) do |f| %>
<% end %>

There might be some errors, but hopefully this gets you in the right direction (also make sure to handle authentication and authorization).

You don't need a separate controller, especially since you're already extending the default Devise RegistrationsController, which already works fine for updating user attributes.

Edit: If these aren't just extended user attributes, and profile is it's own object with its own logic and behaviour, then consider creating it's own controller, to manage the CRUD for that object.

If you're using devise's user/edit page as part one, all you need to do is add a profile action in your custom controller, and create a view file to go with it.

# this is all that's in the edit action from Devise
def edit
  render :edit
end

# add this to your custom RegistrationsController
def profile
  render :profile
end

Then you can fiddle with your routes (see this and this ) until they route the URLs you want to use to the correct controller:

# you probably have this, which covers your current user/edit route
devise_for :users

# but you can add this to extend these routes
devise_scope :user do
  # will route /profile to the profile action on User::RegistrationsController
  get :profile, to: 'users/registrations'

  # or if you want more control over the specifics
  get 'users/settings/profile', to: 'users/registrations#profile', as: :user_profile
end

For your second view/form to update user attributes from another, non-devise controller, you can use form_for current_user, { url: user_registration_path }

If you do want to use resource, you'll have to add this to the top of your registrations controller, so that the resource gets defined on your profile action as well:

prepend_before_filter :authenticate_scope!, only: [:edit, :profile, :update, :destroy]

Take a look at devise's documentation around strong parameters to see how to make sure whatever additional attributes you're going to add to your user are white listed by your custom RegistrationsController .

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