Sterling Rose Design Blog
Using AJAX to Change a User's Role
Authored by Dana Jones
February 09, 2009 17:26
2 Comments
Tags: Rails AJAX
Authored by Dana Jones
February 09, 2009 17:26
2 Comments
Tags: Rails AJAX
Scenario: Client wants to display a list of users, and the role each user currently has. The user’s role should be the selected option in a select box of all roles. Administrator should be able to update the user’s role by simply selecting a new role from the select box for that user, without having to reload the page.
Approach: Each row in the user’s list should have an id that uniquely identifies it as being associated with that particular user. Each select box should also have an id field associated with the user. Choosing a new role should trigger the
Code:
Additional Notes: In the first code snippet above (the index view) you want a fully-closed table tag. Textile isn’t playing nice with that particular tag at the moment, so – apologies. :) Update: All fixed now. :)
CommentsApproach: Each row in the user’s list should have an id that uniquely identifies it as being associated with that particular user. Each select box should also have an id field associated with the user. Choosing a new role should trigger the
onchange event, which executes the update on the role and displays the new user’s row in place, without reloading the entire user list.Code:
## app/views/users/index.html.erb
<table>
<% @users.each do |u| %>
<tr id='user_row_<%= "#{u.id}" %>'>
<td><%= link_to "#{u.first_name} #{u.last_name}", user_path(u) %></td>
<td><%= u.username %></td>
<td><%= u.email %></td>
<td><%= select_tag "user#{u.id.to_s}", options_for_user_roles(u), :onchange =>
remote_function(:url => {:controller => "permissions", :action =>
"update_one_row"}, :with => "'user_id=' + #{u.id} + '&role_id=' +
$('user#{u.id}').value") %></td>
<td><%= link_to(image_tag('/images/edit.png'), edit_user_path(u)) %></td>
</tr>
<% end %>
</table>
## app/controllers/users_controller.rbdef index
@users = User.all
end## app/controllers/permissions_controller.rbdef update_one_row
@user = User.find(params[:user_id])
@company = @user.company
@role = Role.find(params[:role_id])
unless @user.has_role?(@role.rolename)
@user.roles.clear
@user.roles << @role
end
end## app/views/permissions/update_one_row.js.erb$("user_row_<%=@user.id%>").replace(<%=js render(:partial =>
"/users/update_one_row") %>);## app/views/users/_update_one_row.html.erb<tr <%= "id='user_row_#{@user.id}'" %>>
<td><%= link_to "#{@user.first_name} #{@user.last_name}", user_path(@user) %></td>
<td><%= @user.username %></td>
<td><%= @user.email %></td>
<td><%= select_tag "user#{@user.id.to_s}", options_for_user_roles(@user), :onchange =>
remote_function(:url => {:controller => "permissions", :action => "update_one_row"},
:with => "'user_id=' + #{@user.id} + '&role_id=' + $('user{@user.id}').value") %></td>
<td><%= link_to(image_tag('/images/edit.png'), edit_user_path(@user)) %></td>
</tr>## app/helpers/permissions_helper.rbdef options_for_user_roles(user)
## exclude "administrator" from the roles you can
## assign to a user
@roles = Role.find(:all) - [Role.find_by_rolename('administrator')]
options = ""
@roles.each do |role|
if user.roles.include?(role)
options += "<option id='#{role.id}' selected='selected'
value='#{role.id}'>#{role.rolename}</option>"
else
options += "<option id='#{role.id}' value='#{role.id}'>#{role.rolename}</option>"
end
end
options
endAdditional Notes: In the first code snippet above (the index view) you want a fully-closed table tag. Textile isn’t playing nice with that particular tag at the moment, so – apologies. :) Update: All fixed now. :)
It just occurred to me but you may have already thought of my suggestions but went for brevity in the post. In that case please feel free to ignore my comment.
Posted May 11, 2009 by Mike Breen
Posted May 11, 2009 by Mike Breen


A small refactoring I would suggest is that you move the logic for getting the roles (in permissions_helper.rb) into a named_scope on the Role model. Or you can set up a default_scope on Role to exclude the admin role so you can use Role.all and not worry about the admin.
Posted May 11, 2009 by Mike Breen