Reputation: 851
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:
and both to processed by the same controller, or the same action
Do I need to create a new controler/route/view, like:
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
Upvotes: 1
Views: 1225
Reputation: 767
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
.
Upvotes: 1
Reputation:
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, e.g.,
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, e.g., 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.
Upvotes: 1
Reputation: 2637
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).
Upvotes: 0