I have comments as a subresource of articles, as below:
resources :articles do
resources :comments
end
I want each Article's show page to display all of its corresponding comments, as well as have a form where a user (if signed in) can create a new comment that corresponds with the article.
Here's the show action for ArticlesController:
def show
#comment = #article.comments.build
if user_signed_in?
#comment.user_id = current_user.id
end
respond_with(#article)
end
Here's relevant code from the CommentsController:
before_action :set_article
...
def create
#comment = #article.comments.create(comment_params)
#comment.user_id = current_user.id
if #comment.save
flash[:notice] = "Comment successfully created!"
end
redirect_to #article
end
...
private
def set_article
#article = Article.find(params[:article_id])
end
end
And here's the Article show view:
<b>Post comments:</b><br />
<table>
<thead>
<tr>
<th>User email</th>
<th>Comment</th>
</tr>
</thead>
<tbody>
<% #article.comments.each do |comment| %>
<tr>
<td><%= comment.user.email %></td>
<td><%= comment.body %></td>
</tr>
<% end %>
</tbody>
</table>
<br />
<%= form_for ([#article, #comment]) do |f| %>
<div class="field">
<%= f.text_area :body, placeholder: "Compose new comment..." %>
</div>
<%= f.submit "Post" %>
<% end %>
Here's the two situations I need to fix:
When no user is signed in, the view fails to render because comment.user returns nil
When a user is signed in, a phantom comment for each article appears, because the show action in the ArticlesController is building a new (empty) comment with #comment = #article.comments.build
What is the best way to simultaneously solve both of these problems?
If you don't want to do that in the view, you can do this sort of thing in the controller.
def show
#comment = Comment.new(:article => #article)
respond_with(#article)
end
This won't add the comment to the article.comments array and won't try to render a blank comment.
Do you need to set the user_id on the comment in the form on the show action? If not, you can do this in create
def create
#comment = Comment.new(comment_params)
#comment.article = #article
#comment.user = current_user
if #comment.save
flash[:notice] = "Comment successfully created!"
redirect_to #article
else
render :show
end
end
The benefit to this - you can now handle errors in the form (like if they submit a blank comment, or minimum characters). Even if you handle it in the view with javascript/disabled buttons, it's nice to have the server also support validations and errors.
Just kidding, got it.
Put the build call into the form rather than the ArticleController:
<%= form_for ([#article, #article.comments.build]) do |f| %>
<div class="field">
<%= f.text_area :body, placeholder: "Compose new comment..." %>
</div>
<%= f.submit "Post" %>
<% end %>
and delete everything out of the show action:
def show
respond_with(#article)
end
Related
I am new to Ruby and I am trying to display comments on my show page but when I create a comment it does not display. I have rendered a partial on my show page in order to display the comments but they do not appear.
The strange thing is that the create action works but it takes me to this page: http://localhost:3000/hairstyles/2/comments which has nothing on it (in my app this page is under views>comments>create.html.erb) instead i'd like it to go to the view page for the hairstyle and display the comment..........
If anyone can help and see if there are any errors in my code I'd be grateful. Thanks in advance.
Comments Controller:
class CommentsController < ApplicationController
def new
#hairstyle = Hairstyle.find(params[:hairstyle_id])
#comment = Comment.new
end
def create
#comment = Comment.new(comment_params)
#hairstyle = Hairstyle.find(params[:hairstyle_id])
#comment.save
end
def destroy
end
end
private
def comment_params
params.require(:comment).permit(:content)
end
Hairstyle view page where I want to display the comments:
<div class="container">
<div>
<%= cl_image_tag #hairstyle.photo, width: 300, height: 200, crop: :fill %>
<h1><%= #hairstyle.name %></h1>
<p><%= #hairstyle.description%></p>
<p><%= #hairstyle.category%></p>
<p><%= #hairstyle.video_url %></p>
</div>
</div>
<div>
<%= link_to 'Back', hairstyles_path %>
</div>
<h6>Comments for: <%= #hairstyle.name %> <small><%= %></small></h6>
<h2>
<%= pluralize #hairstyle.comments.size, "comment" %>
</h2>
<div id="comments">
<% if #hairstyle.comments.blank? %>
Be the first to leave a comment for <%= #hairstyle.name %>
<% else %>
<% #hairstyle.comments.each do |comment| %>
<%= render 'comments/show', comment: comment %>
<% end %>
<% end %>
</div>
<%= render 'comments/form', comment: #comment %>
Comments form i am rendering which does work and is displayed:
views>comments>_form.html.erb
<div class="flex-box">
<%= simple_form_for([ #hairstyle, comment ]) do |form| %>
<%= form.input :content, as: :text %>
<%= form.button :submit %>
<% end %>
</div>
Comments content that I am rendering that does not display once I have added a comment to my hairstyle on the show page:
views>comments>_show.html.erb
<p><%= comment.content %></p>
The default behavior of rails controllers is it will redirect to the index page after the create method. that's why you are redirected to that path.
you can simply use redirec_to in your create method in your comment controller like below
def create
#comment = Comment.new(comment_params)
#hairstyle = Hairstyle.find(params[:hairstyle_id])
#comment.save
redirect_to hairstyle_path(#comment.hairstyle)
end
You never link your comment to your hairstyle, your hairstyle_id from your comment is nil this is why #hairstyle.comments returns an empty array
def create
#hairstyle = Hairstyle.find(params[:hairstyle_id])
#comment = #hairstyle.comments.build(comment_params)
# equivalent to:
# comment = Comment.new(comment_params)
# comment.hairstyle = #hairstyle
if comment.save
redirect_to hairstyle_path(#hairstyle)
else
# handle errors
end
end
I'm using rails 4.0.8. I added a comment section to a model called 'Things', but I keep getting the same error "param is missing or the value is empty: thing" when I press the submit comment button. It says the error is in the Things#Controller. What am I doing wrong?
UPDATE: I removed the url path from the form, but a new error returns "Couldn't find Thing without an ID". The error is in Comments#Controller.
VIEW FOR THING/SHOW
<div id= "thing">
<h1>
<%= #thing.name %>
</h1>
<br>
<div id= "commentsection">
Comments
<div id= "comments">
<br>
<% #thing.comments.each do |c| %>
<%= c.username %>
<br>
<%= c.text %>
<% end %>
<%= form_for #comment, :url => thing_path do |f| %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.label :comment %>
<%= f.text_field :text %>
<%= f.submit "Enter", class: "btn btn-small btn-primary" %>
<% end %>
</div>
</div>
</div>
THINGS CONTROLLER
class ThingsController < ApplicationController
def show
#thing = Thing.find(params[:id])
#thing.comments.build
#comment = Comment.new
end
def index
end
def new
#thing = Thing.new
#things = Thing.all
end
def create
#thing = Thing.new(thing_params)
if #thing.save
redirect_to #thing
else
render 'new'
end
end
private
def thing_params
params.require(:thing).permit(:name, :avatar)
end
end
COMMENTS CONTROLLER (I put asterisks around the line where the error is)
class CommentsController < ApplicationController
def show
#comment = Comment.find(params[:id])
end
def new
#comment = Comment.new
#comments = Comment.all
end
def create
****#thing = Thing.find(params[:thing_id])****
#comment = #thing.comments.create(comment_params)
redirect_to thing_path(#thing)
end
end
private
def comment_params
params.require(:comment).permit(:user, :text, :upvotes, :downvotes, :thing_id)
end
end
ROUTES
Website::Application.routes.draw do
get "comments/new"
get "comments/show"
get "things/new"
root 'home_page#home'
get "all/things/new" => 'things#new'
get "all/allthings"
resources :things
resources :good_comments
get "things/show"
get "things/results"
end
You are posting the #comment form to post '/things' path.
<%= form_for #comment, :url => thing_path do |f| %>
It should just be <%= form_for #comment do %> (Rails is smart enough to plug in the comments_path) or if you feel like being more explicit (even though it's not necessary)
<%= form_for #comment, url: :comments_path do %>
Another note though, if you want that Comment to be tied to that specific Thing then in your models it should be
Class Thing
has_many :comments
end
Class Comment
belongs_to :thing
end
Then make sure in your database comment has a thing_id foreign_key field and then your form for comment should actually look like
<%= form_for #thing, #comment do %>
<% end %>
I am new to Rails and working on creating a generic "facebook" type of app as practice with users and posts associated with each user. However, I'm currently having an issue where I think the form that I am using to create the posts is also being rendered out as a blank post with no post ID where I display all of the posts in a section below. I think that this post is being shown even before it is being saved to the database.
Here is my code in my view:
<div class="newpostcontainer">
<div class="newposttext">
<%= form_for([#user, #user.posts.build]) do |f| %>
<%= f.text_area :post, size: "69x1" %>
</div>
<div class="newpostsubmitbutton">
<%= f.submit %>
</div>
<% end %>
</div>
<% #user.posts.reverse_each do |p| %>
<div class="postedcontainer">
<div class="minipostpic">
<%= image_tag #user.photo.url, width: 32, height: 32 %>
</div>
<div class="nameofposter"><%= #user.name %></div>
<div class="dateofpost"><%= p.created_at%></div>
<div class="postcontent"><%= p.id%></div> <br>
<div class="postcontent"><%= p.post%></div> <br>
<div class="likecommentdelete">
<%= link_to "Delete", [p.user, p], method: :delete %> | Like | Comment
</div>
</div>
<%end%>
</div>
Here is my controller:
def index
#user = User.find(params[:user_id])
#posts = #user.posts.all
end
def create
#user = User.find(params[:user_id])
#post = #user.posts.create!(post_params)
redirect_to user_path(#user)
end
def show
#user = User.find(params[:user_id])
#post = #user.posts.find(params[:id])
redirect_to user_path(#user)
end
def destroy
#user = User.find(params[:user_id])
#post = #user.posts.find(params[:id])
#post.destroy
if #post.destroy
redirect_to user_path(#user)
else
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit!
end
end
And here is my model:
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
validates_presence_of :post
end
I'm pretty sure the issue has something to do with my form to create the new post because when I remove it or comment it out, the extra blank post with no post ID goes away.
Any thoughts or suggestions?
Thank you!!
I think you need to permit the field values to be posted:
i.e.,
params.require(:post).permit!
should be
params.require(:post).permit(:name, :post)
then only it will POST I think.
Hope it helps :)
This is because of rails 4 strong parameter feature. You need to whitelist your active models parameters. For more details refer to here.
In your case you need to do something like this:
params.require(:post).permit(:post)
where the ":post" inside require is your model and the other one is your permitted field that is your textarea.
Several issues -
Form
<%= form_for([#user, #user.posts.build]) do |f| %>
Why are you building an associative object? #user.posts.build will not persist your data, and will cause all sorts of non-conventional issues I would highly recommending building the posts associative object in your controller's new action before using in the view, so you can do this:
#app/controllers/users_controller.rb
def new
#user = current_user
#user.posts.build
end
<%= form_for #user do |f| %>
Association
You're trying to edit the post attribute with this statement:
<%= f.text_area :post, size: "69x1" %>
This won't work in any circumstance, as :post is an association, not an object. Rails only allows you to change / add attributes to specific objects, which means you'll be better doing something like this:
<%= f.fields_for :posts do |p| %>
<%= p.text_area :title %>
<%= p.text_area :body %>
<% end %>
Strong Params
You're currently permitting all your params? You'll be better doing this:
def post_params
params.require(:user).permit(posts_attributes: [:title, :body])
end
Use Posts Controller
A better way will be to just use the posts_controller, like this:
#app/controllers/posts_controller.rb
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.save
end
#app/views/posts/new.html.erb
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<% end %>
I have a table of venues and offers. Each venue can have have many offers.
I would like to be able to add the offers to the venues from the venues edit page. So far I have this (code below) but its giving a "NoMethodError in Venues#edit, undefined method `model_name' for NilClass:Class" error.
venues edit page
(the div id="tabs-3" is a container in an accordion)
<div id="tabs-3">
<%= form_for [#venue, #offer] do |f| %>
<h2 class="venue_show_orange">Offers</h2>
<%= f.fields_for :offers do |offer| %>
<div class="add_offer">
<%= offer.text_field :title %><br>
</div>
<div class="button"><%= submit_tag %></div>
<% end %>
<% end %>
</div>
offers controller
class OffersController < ApplicationController
def new
#offer = Offer.new
end
def create
#offer = #venue.offers.create!(params[:offer])
#offer.venue = #venue
if #offer.save
flash[:notice] = 'Offer added'
redirect_to offers_path
else
render :action => :new
end
end
def edit
#offer = Offer.find(params[:id])
end
def update
#offer = Offer.find(params[:id])
#offer.attributes = params[:offer]
if #offer.save!
flash[:notice] = 'Offer updated successfully'
redirect_to offers_path(#offer)
end
end
end
venues controller
(nothing offer related in here - is this where I'm going wrong?)
class VenuesController < ApplicationController
protect_from_forgery :only => [:update, :delete, :create]
load_and_authorize_resource
def new
#venue = Venue.new
5.times { #venue.venuephotos.build }
end
def create
#venue = Venue.new params[:venue]
if #venue.save
flash[:notice] = 'Venue added'
redirect_to venues_path
else
render :action => :new
end
end
def edit
#venue = Venue.find(params[:id])
5.times { #venue.venuephotos.build }
end
def update
#venue = Venue.find(params[:id])
#venue.attributes = params[:venue]
if #venue.save!
flash[:notice] = 'Venue updated successfully'
redirect_to :back
end
end
end
Any help is much appreciated thanks very much!
edit
venues edit page
<div id="tabs-3">
<%= form_for #venue do |f| %>
<div class="edit_venue_details">
<h2 class="venue_show_orange">Offers</h2>
<%= render :partial => 'offers/offer', :collection => #venue.offers %>
<div class="clearall"></div>
<h2 class="edit_venue_sub_header">Add a new offer</h2>
<%= f.fields_for :offers do |offer| %>
<% if offer.object.new_record? %>
<p class="edit_venue">title: <br>
<%= offer.text_field :title, :class => "edit_venue_input" %></p>
<% end %>
<% end %>
</div>
<button class="submit_button" type="submit"> Save changes</button>
<% end %>
</div>
whats being displayed
however if I add a new offer, that will display correctly:
Some remarks:
1) Replace:
<%= form_for [#venue, #offer] do |f| %>
with:
<%= form_for #venue do |f| %>
Because offers data will be updated through the related venue, only one controller action will handle the form.
2) If you want to add some unexisting offers in this form, you shoud instantiate them the way you did with venuephotos
3) Show your Venue model. You should have at least:
accepts_nested_attributes_for :offers
Fixed with:
<%= f.fields_for :offers, #venue.offers.build do |offer| %>
I have a status update, and comment db table.
A user has many status updates, and a status update has many comments. Similar to facebook, When a users friend goes to the users feed page (show page), they should be able to comment on the users status updates.
I'm having issues saving a users friends comment.. my code is below.. I think it has something to do with the Comments Controller, Create method, "#comment = #statusupdate.comments.build(params[:comment])"
any guidance is much appreciated! thanks!
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#statusupdates = #user.statusupdates.paginate(:page => params[:page], :per_page => 25)
#statusupdate = Statusupdate.new
#comment = Comment.new
end
end
show.html.erb
<% form_for #statusupdate do |f| %>
<%= f.error_messages %>
<div class="field">
<%= f.text_field :content %>
</div>
<% #statusupdates.each do |s| %>
<%= s.content %><br />
<% form_for #comment do |f| %>
<%= f.error_messages %>
<div class="field">
<%= f.text_field :comment %>
</div>
<div class="field">
<%= f.hidden_field :user_id, :value => current_user.id %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<br><br>
<% end %>
<% end %>
class CommentsController < ApplicationController
def create
#comment = #statusupdate.comments.build(params[:comment])
if #comment.save
flash[:success] = "Comment created!"
redirect_to root_path
else
#feed_items = []
render 'pages/home'
end
end
end
Check the html of the form to make sure its right. Also see what parameters are getting sent to the create action.
The main thing I see is that the forms for the status update and the comments are nested, and both use the block parameter f. This could cause things to get very strange (especially since the scoping of block parameters differs between ruby 1.8 and 1.9). It also seems like you don't actually want the forms nested. You should also probably fix the indentation in your html.
show.html.erb- I changed the top line of the status update comment form to:
<% form_for (s, s.comments.build) do |f| %>
...
class CommentsController < ApplicationController
def create
#statusupdate = statusupdate.find(params[:statusupdate_id])
#comment = #statusupdate.comments.create(params[:comment])
...
I don't exactly know what's going on here but it worked for me~ hope it helps someone out~