Can I update a user's attributes with a form in rails? - ruby-on-rails

is there a way i can use a form to update a user's points in rails with a simple form that takes in the name and the points I want to append?
Heres the link to the app i made : enter link description here
my view for the index is :
<div class="container">
<h1 class="text-center">Listing Students</h1>
<div class ="jumbotron">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Points</th>
</tr>
</thead>
<% #users.each do |user| %>
<tr>
<td><%= user.name %></td>
<td><%= user.points %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Delete', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<br>
<%= link_to 'New Student', new_user_path, class:'btn btn-primary' %>
</div>
I tried using the code in my edit page :
<div class="container">
<div class=" text-center">
<div class="col-md-6 col-md-offset-3">
<div class= "well well-lg">
<%= render 'form' %>
<%= link_to 'Back', users_path, class:"back" %>
</div>
</div>
</div>
</div>
to render the form in the index but it gives me an error of #user can not be nill
I simply need to update the users points in the index page , it would be great if i can greate a modal to appear and then type in the name and edit it. Ive been working on this for hours and seem to update it .

Sure, you could use simple_form or form_for. Or you could do inplace editing. Your question is not very precise as to what exactly you need.
UPDATE 1:
Assuming that you have added simple_form gem to the gemfile, and that you have something like this in the routes.rb : resources :users, then edit view of the UsersController could look something like this:
#in controller
def edit
#user = User.find(params['id'])
end
And the view:
<%= simple_form_for #user do |f| %>
<%= f.input :name %>
<%= f.input :points %>
<%= f.button :submit %>
<% end %>
Please try it and let me know if it works for you (the edit view; in order to persist, you will need and update action as well).

Add a div on index page to open modal popup:
<div class='modal_popup>
</div>
For updating name and points of user on index page, you have to change edit link like this:
<%= link_to 'Edit', edit_user_path(user), remote: true %>
Controller changes:
def edit
#user = User.find(params[:id])
end
_modal.html.erb
<div class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">Modal title</h4>
</div>
<%= form_for #user, remote: true do |f| %>
<div class="modal-body">
<%= f.text_field :name %>
<%= f.text_field :points %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<%= f.submit 'Update', class: "btn btn-primary" %>
</div>
<% end %>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
edit.js.erb
$('.modal_popup').html("<%= j(render partial: '/users/modal') %>");
$('.modal').show();
After updating user in update.js.erb add below lines of code:
$('.modal').hide();
window.location.reload();
Hope this may help..

Related

Lightbox with Rails

So I am trying to use Lightbox2 on my application. I installed the gem and followed all the steps, but having trouble figuring out where to call it in the application.
This is my post index
<div class="container">
<div id="profuploads">
<div id="posts" class="transitions-enabled">
<% #posts.each do |post| %>
<div class="box panel panel-default">
<%= link_to image_tag(post.image.url(:medium)), post %>
<div class="panel-body">
<strong><%= post.user.username if post.user %></strong><br/>
<%= post.description %>
<% if post.user == current_user %>
<div class="actions">
<%= link_to 'Edit', edit_post_path(post) %>
<%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %>
</div>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
</div>
This is my post show
<div class="row">
<div class="col-md-offset-1 col-md-10">
<div class="panel panel-default">
<div class="panel-heading center">
<%= image_tag #post.image.url, height: '300' %>
</div>
<div class="panel-body">
<p><strong><%= link_to(#post.user.username.capitalize, user_path(#post.user.id)) if #post.user %></strong></p>
<p><%= #post.description %></p>
<div class="votes">
<strong>VIEWS</strong>
<%= #post.hits %>
<div class="votes">
<%= link_to like_post_path(#post), method: :put do%>
<button type="button" class="btn btn-info" aria-label="Left Align">
<span class="glyphicon glyphicon-thumbs-up glyphicon-align-center" aria-hidden="true"></span>
<span class="badge"><%= #post.get_upvotes.size %></span>
</button>
<%end%>
</div>
<% if #post.user == current_user %>
<%= link_to 'Edit', edit_post_path(#post) %>
<% end %>
</div>
</div>
</div>
</div>
With Lightbox do I need to just git rid of my post show page altogether?
Lightbox is a javascript library which needs proper html markup in order to do it's work. Your show view looks good except it does not include the markup Lightbox looks for.
From Lightbox Getting Started guide:
Add a data-lightbox attribute to any image link to enable Lightbox. For the value of the attribute, use a unique name for each image. For example:
Image #1
When Lightbox initializes, it looks for any image tags that contain data-lightbox attribute and hooks its handlers to them. You need to provide this attribute:
<%= image_tag #post.image.url, height: '300', "data-lightbox" => #post.image.url %>
This should make Lightbox pick up your images.

Rails Conditional Statements on Object Index Page

In my rails app, I have an index of an object I'm calling Lessons. If a user marks the lesson as being completed, it gets recorded in a join table called Completions and the completed_step boolean gets set to true.
In the show action for the lesson, I'm able to insert a conditional statement to modify the button for "Complete Lesson."
That button logic works fine like this:
<% if current_user.completed_steps.include? #lesson %>
<%= button_to "This Lesson is Completed", #lesson.next, class: "btn btn-success btn-lg", :method => :get %>
<% else %>
<%= button_to "Mark this Lesson as Complete", complete_lesson_path, class: "btn btn-warning btn-lg" %>
<% end %>
My question is how do I incorporate this type of logic check on the index view?
I'd like the links to each Lesson to appear in a Bootstrap Panel and I want to change the color of the panel depending upon whether or not the user completed_step for each Lesson.
I tried wrapping the panel in the same statement and it doesn't work, it stays red even though the user has completed these lessons.
Here's what I attempted:
<% #lessons.each do |lesson| %>
<% if current_user.completed_steps.include? #lesson %>
<div class="panel panel-success">
<% else %>
<div class="panel panel-danger">
<% end %>
<div class="panel-heading">
<h3 class="panel-title">
<%= link_to(lesson) do %>
<strong><%= lesson.title %></strong>
<% end %>
</h3>
</div>
<div class="panel-body">
<td><%= lesson.summary %></td>
</div>
</div>
What am I doing wrong or why isn't this working?
Please change your script
<% #lessons.each do |lesson| %>
<% if current_user.completed_steps.include? lesson %>
<div class="panel panel-success">
<% else %>
<div class="panel panel-danger">
<% end %>
<div class="panel-heading">
<h3 class="panel-title">
<%= link_to(lesson) do %>
<strong><%= lesson.title %></strong>
<% end %>
</h3>
</div>
<div class="panel-body">
<td><%= lesson.summary %></td>
</div>
</div>
You need to pass same object as you given in in you for loop .
<% #lessons.each do |lesson| %>
<% if current_user.completed_steps.include? lesson %>
<div class="panel panel-success">
<% else %>
#Pramod Gupta is right. Also, look into using Presenters for this type of logic that is used for decorating the views.

rails leaving out some parts from fragment caching

I have a rails 4 app using pundit gem for authorization. If I do russian-doll fragment caching like the code below, the conditional statement used for authorization will be also cached, which is not good, since edit/delete buttons should only be available for the post.user.
What is the good way to get around this? Should I split the cache into smaller parts or is there a way to exclude some parts of the caching? What's the rails convention in this case?
index.html.erb
<% cache ["posts-index", #posts.map(&:id), #posts.map(&:updated_at).max, #posts.map {|post| post.user.profile.updated_at}.max] do %>
<%= render #posts %>
<% end %>
_post.html.erb
<% cache ['post', post, post.user.profile ] do %>
<div class="row>
<div class="col-md-2">
<%= link_to user_path(post.user) do %>
<%= image_tag post.user.avatar.url(:base_thumb), class: 'post-avatar' %>
<% end %>
</div>
<div class="col-md-8">
<span class="post-user-name"><%= post.user.full_name %></span>
<span class="post-updated"><%= local_time_ago(post.updated_at) %></span>
<div class="post-body">
<%= post.body %>
</div>
<div class="col-md-2" style="text-align:right;">
<!--############### THIS IS THE PART THAT SHOULD NOT BE CACHED #############-->
<% if policy(post).edit? && policy(post).delete? %>
<li class="dropdown">
<ul class = "dropdown-menu dropdown-menu-right">
<li>
<%= link_to "Edit Post", edit_post_path(post), remote: true, type: "button", 'data-toggle' => "modal", 'data-target' => "#updatepost_#{post.id}" %>
</li>
<li>
Delete Post
</li>
</ul>
</li>
<% end %>
<!--########################## UNTIL HERE ############################-->
</div>
</div>
<div class = "row comment-top-row" style="padding-bottom:10px;">
<div class="col-md-12 post-comment-form">
<%= render partial: 'posts/post_comments/post_comment_form', locals: { post: post } %>
</div>
</div>
<div class = "row">
<div class="col-md-12 post-comment-insert-<%= post.id%>">
<%= render partial: 'posts/post_comments/post_comment', collection: post.post_comments.ordered.included, as: :post_comment, locals: {post: post} %>
</div>
</div>
<% if policy(post).edit? %>
<div class="modal fade updatepost" id="updatepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<!-- FORM GETS RENDERED HERE VIA JS -->
</div>
<% end %>
<% if policy(post).delete? %>
<div class="modal fade" id="deletepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
......
</div>
<% end %>
<% end %>
Russian Doll Caching is simple but handy way for caching, there's no complex options or convention to exclude part of fragment from it. Above that, It's more relative to cache strategies. Here are two strategies for this user specific situation:
Rearrange individually and Cache fragments manually, which I don't recommend. Because it's more complex and doesn't leverage the advantages of Russian Doll Caching. Not so maintainable as well. Here is an example:
index.html.erb
<% # pull out cache %>
<%= render #posts %>
_post.html.erb
<% cache post %>
<%= # first part %>
<% end %>
<% # without cache %>
<%= # user specific part %>
<% cache post %>
<%= # third part %>
<% end %>
Preferred way: Add current_user as part of cache_key, which means you will have as many fragment caches as approximately your users and the fragments will automatically invalidate whenever the post or the user has changed their fingerprint. This is more elegant and maintainable. Here is an example:
index.html.erb
<% cache ["posts-index", #posts.map(&:id), #posts.map(&:updated_at).max, #posts.map {|post| post.user.profile.updated_at}.max] do %>
<%= render #posts %>
<% end %>
_post.html.erb
<% cache ['post', post, post.user.profile, current_user ] do %>
<div class="row>
<div class="col-md-2">
<%= link_to user_path(post.user) do %>
<%= image_tag post.user.avatar.url(:base_thumb), class: 'post-avatar' %>
<% end %>
</div>
<div class="col-md-8">
<span class="post-user-name"><%= post.user.full_name %></span>
<span class="post-updated"><%= local_time_ago(post.updated_at) %></span>
<div class="post-body">
<%= post.body %>
</div>
<div class="col-md-2" style="text-align:right;">
<% if policy(post).edit? && policy(post).delete? %>
<li class="dropdown">
<ul class = "dropdown-menu dropdown-menu-right">
<li>
<%= link_to "Edit Post", edit_post_path(post), remote: true, type: "button", 'data-toggle' => "modal", 'data-target' => "#updatepost_#{post.id}" %>
</li>
<li>
Delete Post
</li>
</ul>
</li>
<% end %>
</div>
</div>
<div class = "row comment-top-row" style="padding-bottom:10px;">
<div class="col-md-12 post-comment-form">
<%= render partial: 'posts/post_comments/post_comment_form', locals: { post: post } %>
</div>
</div>
<div class = "row">
<div class="col-md-12 post-comment-insert-<%= post.id%>">
<%= render partial: 'posts/post_comments/post_comment', collection: post.post_comments.ordered.included, as: :post_comment, locals: {post: post} %>
</div>
</div>
<% if policy(post).edit? %>
<div class="modal fade updatepost" id="updatepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<!-- FORM GETS RENDERED HERE VIA JS -->
</div>
<% end %>
<% if policy(post).delete? %>
<div class="modal fade" id="deletepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
......
</div>
<% end %>
<% end %>

Form attribute is not nil but getting First form attribute can't be nil error

I'm getting an error on my edit user modal that says that the first argument can't be nil; however, the form works and pulls the information as expected. The setup is like this:
1) I have a table that summarizes employees
2) each employee has a modify action
3) the modify action opens up a modal with the edit form
The form works and users are able to edit employees. However, every time it's open I get an internal error notification saying that the first argument can't be nil or empty. just to be sure, I'm displaying the employee name in the edit modal to confirm it's not nil (and it's not). The problem is, I'm getting notifications for internal server errors caused by this issue although somehow the form is working.
Any thoughts on what could be causing this and how to fix it?
Employee table code:
<tr>
<td> <%= employee.name %> </td>
<td> <%= employee.status ? t(:active) : t(:inactive) %> </td>
<td> <%= mmm_dd_yy_date(employee.created_at) %> </td>
<td> <%= mmm_dd_yy_date(employee.updated_at) %> </td>
<td> <%= link_to t(:edit), '#', "data-toggle" => "modal", "data-target" => "#EditModal_#{employee.id}", "data-remote" => edit_employee_path(employee) + "#modal-edit-form" %> |
<%= link_to t(:remove), employee, method: :delete, data: { confirm: t(:confirm_remove_employee, name: employee.name)} %>
<!-- Modal -->
<div id='<%= "EditModal_#{employee.id}" %>' class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title"><%= t(:edit_employee) %></h4>
</div>
<div class="modal-body">
<%=render partial: 'edit_form', locals: {employee: employee} %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><%= t(:close) %></button>
</div>
</div>
</div>
</div>
</td>
</tr>
Form modal code:
<div id= "modal-edit-form">
<%= employee.name %>
<%=form_for(employee) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class = "row" >
<div class = "col-xs-6">
<%= f.label :name%>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :status %>
<%= f.collection_select :status, {true: t(:yes), false: t(:no)}, :first, :last, selected: employee.status %>
<%= f.submit t(:update), class: "btn btn-primary"%>
</div>
</div>
<% end %>
</div>
The code looks good to me.
--
The one thing I would say is that including form_for multiple times on the same page is a big no-no - a bunch of conflicts will occur (id etc).
I would personally move the functionality to your employees#edit action as ajax:
#app/controllers/employees_controller.rb
class EmployeesController < ApplicationController
respond_to :js, :html, :json, only: :edit
def edit
#employee = Employee.find params[:id]
respond_with #employee
end
end
#app/views/employees/edit.js.erb
$("<%=j render 'edit' %>").appendTo("body");
// fire modal
#app/views/employees/edit.html.erb
<div id='<%= "EditModal_#{employee.id}" %>' class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title"><%= t(:edit_employee) %></h4>
</div>
<div class="modal-body">
<%=render partial: 'edit_form', locals: {employee: #employee} %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><%= t(:close) %></button>
</div>
</div>
</div>
</div>
#app/views/employees/index.html.erb
<%= link_to t(:edit), '#', data: { toggle: "modal", target: "#EditModal_#{employee.id}", remote: edit_employee_path(employee) + "#modal-edit-form" }, remote: true %>
This way, you'll only be appending the edit forms when you render them.

Bootstrap collapse within table/between table rows not working

I have a question regarding the Bootstrap collapse feature. I'm pretty sure that I'm overlooking something quite obvious or easy to fix, but I googled it alot and played with the code, but without success.
I have a "account settings" page, where all account information of a user is shown in a table-like format, with the table cells of the last table column always containing an "edit"-button to edit that information. When people click "edit", an edit form shall expand just beneath that table row.
I followed the scheme at http://twitter.github.com/bootstrap/javascript.html#collapse , the collapse function itself works fine, but the problem is that each form always expands above my table, regardless of which edit button I click on. I made a screenshot of how it looks like. http://imageshack.us/photo/my-images/834/problemyn.png/ Instead of being above the whole table I want it to expand just beneath the specific row, pushing the lower rows down.
Here my code:
<table class="table">
<tbody>
<tr>
<td>Username</td>
<td><%= #user.name %></td>
<td><button class="btn btn-danger" data-toggle="collapse" data-target="#username">Edit</button></td>
</tr>
<div id="username" class="collapse">
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |form| %>
<%= render 'shared/error_messages', object: form.object %>
<%= form.label :name, "Change Username" %>
<%= form.text_field :name %>
<%= form.submit "Save changes", class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
<tr>
<td>Email</td>
<td><%= #user.email %></td>
<td><button class="btn btn-danger" data-toggle="collapse" data-target="#email">Edit</button></td>
</tr>
<div id="email" class="collapse">
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |form| %>
<%= render 'shared/error_messages', object: form.object %>
<%= form.label :email, "Change Email" %>
<%= form.text_field :email %>
<%= form.submit "Save changes", class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
<tr>
<td>Password</td>
<td>Last time updated: <%= #user.updated_at %></td>
<td><button class="btn btn-danger" data-toggle="collapse" data-target="#password">Edit</button></td>
</tr>
<div id="password" class="collapse">
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |form| %>
<%= render 'shared/error_messages', object: form.object %>
<%= form.label :password, "Change Password" %>
<%= form.text_field :password %>
<%= form.label :password_confirmation, "Confirm Password" %>
<%= form.password_field :password_confirmation %>
<%= form.submit "Save changes", class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
<tr>
<td>Language</td>
<td>English</td>
<td><button class="btn btn-danger" data-toggle="collapse" data-target="#language">Edit</button></td>
</tr>
<div id="language" class="collapse">
<div class="row">
<div class="span6 offset3">
<!-- code for language form -->
</div>
</div>
</div>
<tr>
<td>Avatar</td>
<td><%= avatar_status %></td>
<td><button class="btn btn-danger" data-toggle="collapse" data-target="#demo">Edit</button></td>
</tr>
<div id="demo" class="collapse">
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |form| %>
<%= form.label :avatar %>
<%= form.file_field :avatar %>
<%= form.submit "Add avatar", class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
</tbody>
</table>
To collapse a table row, you should write extra css for the collapsed row:
table .collapse.in {
display: table-row !important;
}
It will fix the display issue after the row expanded.
Hi I'm sorry I won't have a satisfying answer. It is not allowed to have anything other than <tr> elements as descendants of <tbody> so your HTML is not valid. This is what causes the glitchy behavior. Your best bet is to wrap every part of your form in a new table element.

Resources