Rails: jQuery Ajax issue with partial refresh of comment section - ruby-on-rails

I currently have a comment section that posts only after the whole page refreshes. Although after the post the page refreshes by itself it feels inefficient for the whole page to refresh. I was wondering if anyone can help me with a js file that would refresh just that partial, I am still shakey with my js. Any help is much appreciated! Thank you!
This is my current js for the create.js:
$("#comments").html("<%= escape_javascript(render(:partial => #micropost.comments)) %>");
comment controller
class CommentsController < ApplicationController
def create
#micropost = Micropost.find(params[:micropost_id])
#comment = #micropost.comments.build(params[:comment])
#comment.user_id = current_user.id
#comment.save
respond_to do |format|
format.html
format.js
end
end
end
Comment Section
<div id='CommentContainer-<%= micropost.id%>' class='CommentContainer Condensed2'>
<div class='Comment'>
<%= render :partial => "comments/form", :locals => { :micropost => micropost } %>
</div>
<div id='comments'>
<%=render micropost.comments %>
</div>
</div>

You should use something like this in your controller. This will trigger both the js and html templates as needed.
class CommentsController < ApplicationController
respond_to :html
respond_to :js, only: [ :create ]
def create
# ...
respond_with #comment if #comment.save
end
def index
#comments = Microcomment.find(params[:id]).comments
respond_with #comments
end
end
This will then require views/comments/create.js to respond with something like:
// create.js.erb
$("#comments").get("/api/micropost/<%= #micropost.id %>/comments");
And the view for the comments will be index.html.erb
# index.html.erb
<% #comments.each do |comment| %>
<!-- Display your comment here -->
<% end %>
Now all you have to do is set up a match for /api/micropost/:id/comments in your routes, and this can then serve the list of comments in the desired html format.
Note that this is not completely restful, but I like to keep the /api there to distinguish calls coming from xhr on a url level.

Related

After getting a post with a collection of comments on the same page, how do I add a new comment in RoR?

After finding out from Ursus yesterday about how to get the list of comments for a given post, I was able to modify the post/show.html.erb page to show the post information and the list ( as yet empty ) of comments for that post on that page. However, I am at a loss about how to then add a 'Add Comment' link to that page that would bring up the comment form and set the id of the post into the comments post-id field. The example I was using said to get rid of the entire Views/comments directory, but this leaves me no show page for entering the Comment data. Below is the top portion of the post controller:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
#posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
#post = Post.find(params[:id])
#comments = #post.comments
end
# GET /posts/new
def new
#post = Post.new
end
# GET /posts/1/edit
def edit
end
and here is the top portion of the comments controller:
class CommentsController < ApplicationController
def show
#comment = Comment.find(params[:id])
end
# GET /comments/new
def new
#comment = Comment.new
end
# POST /comments
# POST /comments.json
def create
#post = Post.find(params[:id])
#comment = #post.comments.create(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to #post, notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: #comment }
else
format.html { render :new }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
And here is the posts/show.html.erb page that shows the post and the list of comments table:
<p id="notice"><%= notice %></p>
<link rel="stylesheet" type="text/css" href="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables.css">
<p>
<strong>ID:</strong>
<%= #post.id %>
</p>
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<p>
<strong>Body:</strong>
<%= #post.body %>
</p>
<hr/>
<h1>Comments</h1>
<table id="posts-table">
<thead>
<tr>
<th>Name</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<% #comments.each do |comment| %>
<tr >
<td><%= comment.name %></td>
<td><%= comment.body %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<hr/>
<%= link_to 'Edit', edit_post_path(#post) %> |
<%= link_to 'Add Comment', new_comment_path(#post) %> |
<%= link_to 'Back', posts_path %>
<script>
$(function(){
$("#posts-table").dataTable();
});
</script>
I'm confused about several items:
where does new_comment_path come from and does the #post contain the id needed for creating the comment and linking it to the post?
Do I need a views/comments/show.html.erb page to put the form for the comment on?
Appreciate the help.
where does new_comment_path come from and does the #post contain the
id needed for creating the comment and linking it to the post?
It comes from /config/routes.rb. Resources is a keyword that defines typical
new_comment_path, comment_path, comments_path, etc.
Yes, #post contains the ID.
Do I need a views/comments/show.html.erb page to put the form for the
comment on?
You can if you wish, you are not obligated to show anything or add any forms in a Rails application. Many Rails applications are configured like this: views/comments/_form.html.erb and will render the form as a partial anywhere it is needed.
You can place forms anywhere. Don't forget that at the end of the day, you're just serving HTML. Rails seems special because it has some preset configurations regarding the mapping of controller actions to views. But you're allowed to do anything you want.
eg.
# inside ANY controller
def show
#comment = Comment.first
render 'comments/show.html.erb'
end
That's completely valid, albeit a really bad practice. But in Rails, you're not tied down by any of its configurations. They just exist to make coding a web app faster and easier.

Rails: Render partial with selected instance of model

I have a rails app which displays images of various album covers using an Album model. I want to render a partial view alongside these album covers with the details of a selected album when it is clicked on.
I've looked for examples of this but don't seem to be able to find one that helps me. Having said that I'm new to rails so don't know for sure if I'm looking up the correct terms.
I know this must be a simple question but if anyone can point me in the right direction it would be much appreciated.
UPDATE
This seems similar to what I'm looking to do I think:
How to get Rails 3 link_to :partial to work
I've setup my code as follows:
routes.rb
Rails.application.routes.draw do
root 'albums#index'
resources :albums do
get :details, :on => :member
end
albums_controller.rb
class AlbumsController < ApplicationController
def index
#albums = Album.all
end
def show
#album = Album.find(params[:id])
#tracks = #album.tracks
end
def details
respond_to do |format|
format.js { render :layout => false }
end
end
end
views/albums/index.html.erb
<div class="row">
<% #albums.each do |album| %>
<div class="col-md-3">
<%= link_to 'Details', details_album_path(:id => album.id), :remote => true %>
<div class="thumbnail">
<img src="<%= album.cover %>">
</div>
<% end %>
</div>
<% end %>
</div>
<div id="details">
</div>
views/albums/details.js.erb
$( "#details" ).html( "<%= escape_javascript( render( :partial => "details", :locals => { :album => #album} ) ) %>" );
views/albums/_details.html.erb
<div>
<%= album.title %>
</div>
If I run this and click on the details link for an album I get this error in dev tools:
GET http://localhost:3000/albums/1/details 500 (Internal Server Error)
Rails error message at url shown in dev tools error above
And no partial is shown anywhere.
Very new to rails so any pointers appreciated. If this isn't the best way to go about this then any suggestions welcome.
So I fixed this using Muhammad's suggestion in the comments by updating def details in the albums_controller.rb to include #album = Album.find(params[:id]) :
def details
#album = Album.find(params[:id])
respond_to do |format|
format.js { render :layout => false }
end
end

Rails: Edit Comment without redirecting to the Edit View

I'm building simple web app where people can share their thought / pictures / so on. It has just two controllers Post, and nested in it Comments. Generally, at the moment all works perfectly, users can add, edit and delete posts the same way as comments. The thing I'm trying to do, and have big troubles with, is possibility to Edit comments without redirecting to the Edit Comment View - so to be able to do it from the "posts#show" level, the same way as comments actually are being created. I think it just would look much more nicer... Here are my:
posts_controller.rb
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
def index
#posts = Post.all.order(created_at: :desc)
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#comments = #post.comments
end
def edit
end
def update
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def find_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :description)
end
end
comments_controller.rb
class CommentsController < ApplicationController
before_action :find_post, only: [:create, :edit, :update, :destroy]
def create
#comment = #post.comments.create(comment_params)
if #comment.save
redirect_to #post
else
render 'new'
end
end
def edit
#comment = #post.comments.find(params[:id])
end
def update
#comment = #post.comments.find(params[:id])
#comment.update_attributes(comment_params)
if #comment.save
redirect_to #post
else
render 'edit'
end
end
def destroy
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to #post
end
private
def find_post
#post = Post.find(params[:post_id])
end
def comment_params
params.require(:comment).permit(:content)
end
end
View show.html.haml - for post
%h1 Show page
%h3= #post.title
%p= #post.description
=link_to "Edit memory", edit_post_path
=link_to "Delete memory", post_path, method: :delete
%h4 Share your thoughts about the memory
- #comments.each do |comment|
%p= comment.content
=link_to "Edit thought", edit_post_comment_path(#post, comment)
=link_to "Delete thought", post_comment_path(#post, comment), method: :delete
= simple_form_for([#post, #post.comments.build]) do |c|
= c.input :content
= c.submit "Share thought"
View edit.html.haml - for comments (the one I'd like to get rid of / nest somehow to the view shown above)
= simple_form_for([#post, #comment]) do |c|
= c.input :content
= c.submit "Update thought"
I believe there is a simple solution, however despite the fact that I've been reading a lot about possible solution, for a newbie like me it's still difficult to figure out how this should be programed.
The terminology you're looking for is in-place editing, or inline editing
You'll either need a form or JQuery plugin to make any comment written by current_user (assuming you're using Devise) editable.
I've done this a little (you can sign up for free here, click "profile" and then edit the description):
--
The way you'd want to do it is something like this:
#app/assets/javascripts/application.js
# include x-editable scripts
$(".editable).editable([..options..]);
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<%= post.body %>
<% post.comments.each do |comment| %>
<% if comment.author == current_user %>
<%= content_tag :div, comment.body, class: "editable" %>
<% end %>
<% end %>
<% end %>
I've forgotten how we implemented the provided example; x-editable seems to be the rage now.
Here's how it works:
Include X-Editable (or other plugin) in your app
X-Editable will check for the presence of a .class or #id on an element
Providing such an element will allow X-Editable to make it editable
X-Editable then sends the completed request to the server, acting as Ajax
Looks like there's an x-editable-rails gem you can use:
#Gemfile
gem `x-editable-rails`
#app/assets/javascripts/application.js
#= require editable/bootstrap-editable
#= require editable/rails
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<% post.comments.each do |comment| %>
<%= editable comment, :body if comment.author == current_user %>
<% end %>
<% end %>

Rendering two partials with a form using ajax

I'm rendering a partial that renders a form, using ajax (I can't just render the form directly). Posting a comment works fine when I'm rendering the form without ajax but with ajax it seems the partial can't access the #post variable.
<%= link_to "Render form", submit_comment_path, :remote => true %>
<div id="form">
</div>
I have a submit_comment.js file that looks like this:
$("#form").html("<%= j render(partial: 'comment_partial', locals: {post: #post}) %>");
The comment_partial view:
<%= render 'comments/form' %>
The form view:
<%= simple_form_for [post, Comment.new] %>
...
The submit_comment_path route:
get '/submit_comment', to: 'posts#submit_comment', as: :submit_comment
The posts controller (it's being rendered on the show page):
def show
#post = Post.find(params[:id])
end
def submit_comment
respond_to do |format|
format.html
format.js
end
end
and the comments controller:
def create
#post = Post.find(params[:post_id])
end
If I try to post a new comment it gives me a routing error and takes me to /posts//comment. Putting post.id in the comment_partial gives me an undefined error.
The big piece of the puzzle is understanding that instance variables (#post in this instance) disappear as soon as the Controller renders anything.
You correctly assign #post when you render the show page:
def show
#post = Post.find(params[:id])
end
However, #post disappears the second that show.html.erb is done rendering. When you click on the link to hit the submit comment method, no #post is getting created...
def submit_comment
# No instance variables here! :(
respond_to do |format|
format.html
format.js
end
end
Which means that the submit_comment.js file has no idea which post to be generating a form for.
However, it's not as simple as just throwing another Post.find(params[:id]) into the submit_comment method. You need to:
Define a route that relies on post id
Change the link in show.html.erb to include a specific #post.id
Then find the corresponding post to create a comment for.
It might look something like this...
routes.rb
...
resources :posts do
member do
get 'submit_comment'
end
end
...
Read up on member routes at the Rails Guide. There are other ways of accomplishing a similar route.
posts/show.html.erb
<%= link_to "Render form", submit_comment_post_url(#post), :remote => true %>
Note that the Rails default url helper is different than the one you've got, if you use member routes.
posts_controller.rb
def submit_comment
#post = Post.find(params[:id])
...
end
Hope that helps! Happy form-ing!

How to stay on the same page when I click on sidebar links in Rails

I have 3 models where employee has_many insurances and employee has_many educations and both insurances and educations belongs_to employee.
The entire flow is working fine.
Now I've got a requirement where when I click on employees -> edit, I have to show a sidebar under the edit form with insurances and educations link. And when I click on insurances/ educations, it should render in the same edit page.
employees/edit.html.erb
<h1>
<%= #employee.full_name %>
</h1>
<div class="tabpanel">
<div class="tab-content">
<div class="tab-pane active" id="home" role="tabpanel">
<%= render 'form' %>
</div>
</div>
</div>
<%= render 'layouts/sidebar' %>
layouts/_sidebar.html.erb
<div class="sidebar">
<ul class="nav">
<li>
<%= link_to image_tag("Insurances.png")+"Insurances", employee_insurances_path(#employee) %>
</li>
<li>
<%= link_to image_tag("Education.png")+"Education", employee_educations_path(#employee) %>
</li>
</ul>
</div>
My insurances controller
class InsurancesController < ApplicationController
before_action :set_insurance, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#employee = Employee.find(params[:employee_id])
#insurances = #employee.insurances.all
respond_with(#insurances)
end
def show
#employee = Employee.find(params[:employee_id])
respond_with(#insurance)
end
def new
#employee = Employee.find(params[:employee_id])
#insurance = #employee.insurances.build
end
def edit
#employee = Employee.find(params[:employee_id])
end
def create
#employee = Employee.find(params[:employee_id])
#insurance = #employee.insurances.create(insurance_params)
redirect_to employee_path(#employee)
end
def update
#employee = Employee.find(params[:employee_id])
#insurance.update(insurance_params)
redirect_to employee_path(#employee)
end
def destroy
#employee = Employee.find(params[:employee_id])
#insurance.destroy
redirect_to employee_path(#employee)
end
private
def set_insurance
#insurance = Insurance.find(params[:id])
end
def insurance_params
params.require(:insurance).permit(:name_of_dependent, :relationship,:policy_numbere)
end
end
Any idea how to render the same page when I click on sidebar links?
What I understood is that you are having two edit forms one for insurance and one for education. Or you may have a single form. What you need to do is when the user clicks on the links in the sidebar send a AJAX request to the controller. Create a partial of the form. And in the success of the AJAX request append the form on the page wherever you want like this:
$('#container').append('<%= escape_javascript(render :partial => "form", :locals => {:employee => employee}) %>');
The locals are optional. And the #container is a section/div or any element in the DOM of your existing page which you can rename to anything. And in the controller in the action which you send the request you need to write like this:
respond_to do |format|
format.js
end
This will search for a file of the name of you action with the extension of js.eb. For example you send it to edit action then the file name should be edit.js.erb in the views folder. And in the js.erb write the code of appending the partial.
Hope this helps.

Resources