I am experimenting with nested resources:
My routes:
resources :conversations do
resources :replies do
resources :comments
end
end
I was able to get the form for replies to work with the conversation, but now I'm adding the additional complexity of getting comments to work with replies.
The entirety of the forms are all under the conversation show path.
<%= form_for([#conversation, #reply]) do |f| %>
<%= render 'shared/response_form', f: f %>
<%= f.submit "Reply", class: "btn btn-large btn-primary" %>
<% end %>
The above form for replies works fine and gets no errors, the below form for comments gets an error:
undefined method `reply_comments_path'
<%= form_for([#reply, #comment]) do |f| %>
<%= render 'shared/response_form', f: f %>
<%= f.submit "Comment", class: "btn btn-large btn-primary" %>
<% end %>
Here is my conversations controller for show, this is where I think the problem is:
def show
#conversation = Conversation.find(params[:id])
#replies = #conversation.replies
#reply = current_user.replies.build
#If I change the above line to #conversations.replies.build
#it breaks the ability to show replies above the form.
#comments = #reply.comments
#comment = #reply.comments.build
end
However, someone else suggested doing this:
<%= form_for([#conversation, #reply, #comment]) do |f| %>
<%= render 'shared/response_form', f: f %>
<%= f.submit "Comment", class: "btn btn-large btn-primary" %>
<% end %>
But it only ended up with a routing error:
No route matches {:controller=>"comments", :format=>nil, :conversation_id=>#<Conversation id: 3, content: "Goes here.", user_id: 1, created_at: "2012-12-10 21:20:01", updated_at: "2012-12-10 21:20:01", subject: "Another conversation">, :reply_id=>#<Reply id: nil, content: nil, user_id: 1, created_at: nil, updated_at: nil, conversation_id: nil>}
I always get this undefined method path error when I try making new forms and I always manage to forget what I did wrong. The answer never seems to be the routes.
EDIT:
Under the create section in the controller I have:
#replies = #conversation.replies
#reply = current_user.replies.build
#If I change the above line to #conversations.replies.build
#it breaks the ability to show replies above the form.
I have no idea why #reply = #conversation.replies.build breaks the ability to show the existing replies. I get an error saying it can't convert nil to a number, and can't see the reply.created_at or reply.content. Whatever is causing that may be a clue as to why I'm having this problem. However, in the replies controller I AM using
#reply = conversation.replies.build(content: params[:reply][:content], user_id: current_user.id)
EDIT:
Just to add, Stackoverflow does something very similar to what I'm trying to achieve here, except that you can comment on the questions as well as the answers.
Look at the end of your error:
... :reply_id=>#<Reply id: nil, content: nil, user_id: 1, created_at: nil, updated_at: nil, conversation_id: nil>}
You can't create a form for #comment if #reply is not saved. You need to persist #reply before create a #comment to it.
If you haven't validates on Reply model try this simple test on show action:
# #reply = current_user.replies.build
#reply = current_user.replies.create
See comments for answer.
Related
Here is the relevant code:
--- app/controllers/movies_controller.rb
def show
movie = convert_movie_data([Movie.find(params[:id])])
#movie = movie[0]
#movie_rating = movie[1]
#comments = Comment.all
if user_signed_in?
#new_comment = new_movie_comment
end
end
at the console, #comments returns
#<Comment id: nil, title: nil, comment: nil, user_id: 21, movie_id: 10, created_at: nil, updated_at: nil>
--- app/views/movies/show.html.haml
= render partial: 'shared/new', locales: { new_comment_form: #new_comment }
The partial at "= partial" renders when user is not logged in.
Here is where the problem lies (I suspect):
--- app/views/shared/_new.html.erb
<% if user_signed_in? %>
<%= simple_form for new_comment_form:, method: :post, url: new_comment_path do |f| %>
<%= f.input :title %>
<%= f.input :comment %>
<%= f.button :submit %>
<% end %>
You are signed in
<% else %>
Please sign in to comment
<% end %>
I suspect the problem lies with the way I constructed the simple_form for tag.
At the controller, I injected values that where needed to construct the comment (current_user.id, movie_id) and at the show view, I attempted to transfer the instance variable through locales (new_comment_form:) but then I'm sure that I made a mistake with the construction of the simple form.
I was made aware of the fact that I did not supply the error message. Thanks for pointing that out #z3r0ss
SyntaxError at /movies/21
syntax error, unexpected ','
...ple_form_for new_comment_form:, method: :post, url: new_comm...
... ^
/app/views/shared/_new.html.erb:12: syntax error, unexpected keyword_ensure, expecting end-of-input
ensure
^~~~~
I hope that this helps, and thanks again for any input.
First I would make sure that I'm sending a new instance of Comment from MoviesController
def show
#comment = Comment.new
end
A couple of notes on this
1 - Remember you pass the #movie instance with a before_action (that would be useful if you are using the instance in other controller methods)
2 - You can access to all the comments that belong to the movie from the view with a #movie.comments (I'm assuming there is an association there!)
Regarding the form, you can try this:
<%= simple_form_for [#movie, #comment] do |f| %>
<%= f.input :title%>
<%= f.input :comment%>
<%= f.submit 'Post comment'%>
<% end %>
And finally, at the CommentsController, remember to redirect to wherever you want. For example, the show of the same #movie
[...]
if #comment.save
redirect_to #movie
else
render 'movies/show'
end
May be you can be clearer in terms of what errors you have. From the look of it, it looks like it's a syntax error. It should be simple_form_for new_comment_form and not simple_form for new_comment_form:.
So I'm using the following in both my show and index:
<%= image_tag posts.avatar(:large), alt: 'Avatar for #{posts.title}', class: 'img-responsive' %>
Since they are the same in both I'm trying to move them to a view helper. Then in my views I'm putting:
<%= post_image_tag %>
My initial take was the following:
def post_image_tag
image_tag posts.avatar(:large), alt: 'Avatar for #{posts.title}', class: 'img-responsive'
end
I end up with : undefined local variable or method `posts' for #<#:0x007fcdd273e860>
Did you mean? #posts
Cool. So I change it to:
def post_image_tag
image_tag #posts.avatar(:large), alt: 'Avatar for #{posts.title}', class: 'img-responsive'
end
Now I end up with: undefined method `avatar'.
So I decided that it might just be that I'm not referencing it correctly and trying to pull ActiveRecord on a single post so I try:
def post_image_tag
#posts.each do |posts|
image_tag posts.avatar(:large), alt: 'Avatar for #{posts.title}', class: 'img-responsive'
end
end
At this point something FINALLY renders on the page. Except it looks like an HTML nightmare with:
[#<SpudPost id: 1, spud_user_id: 2, title: "The Top ", content: "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transi...", comments_enabled: false, visible: true, published_at: "2018-01-30 14:45:58", created_at: "2018-02-05 17:41:42", updated_at: "2018-02-05 17:41:42", url_name: "the-top", is_news: false, meta_keywords: nil, meta_description: nil, content_format: "HTML", content_processed: nil, blog_key: "blog", custom_author: nil, identifier: "9b0d97c9-6855-4ad6-85ac-cade6012b5de", avatar_file_name: "ice.jpg", avatar_content_type: "image/jpeg", avatar_file_size: 68494, avatar_updated_at: "2018-02-05 17:41:40">,
It goes further repeating the next item and the next, etc. What in the world can I put on the image_tag to make it render correctly? I've also tried changing the view helper in my view to:
<%= raw(post_image_tag)%>
Then I end up with [#, #, #, #]
You took a wrong turn in this rabbit hole. Go back to your first version of the helper and pass the post as parameter
def post_image_tag(post)
image_tag(post.avatar(:large), alt: 'Avatar for #{post.title}', class: 'img-responsive')
end
# this "posts" should really be named "post", since it's a single post,
# not a collection of them.
<%= post_image_tag(posts) %>
The image is for 1 post, so you should name the variable 'post' and not 'posts'. Anyway this is just for clarity and not an error.
You can define a helper that accepts a param:
def post_image_tag post
image_tag post.avatar(:large), alt: 'Avatar for #{post.title}', class: 'img-responsive'
end
and call it this way (assuming post is 1 post):
<%= post_image_tag(post) %>
If you have many posts in #posts, you should do:
<%= #posts.each do |post| %>
<%= post_image_tag(post) %>
<% end %>
You can also make another helper to handle many posts (it may need some changes, but you get the idea):
def posts_image_tag posts
result=''
posts.each do |post|
result += post_image_tag(post)
end
result.thml_safe
end
and call it this way (assuming #posts has all your posts):
<%= posts_image_tag(#posts) %>
It's a very strange bug bugged me for couple of hours already.
There are 3 models: user, school, post. Basically on the school page(school#show), there are a list of posts, and user can make new post.
The school controller:
def show
#school = School.find(params[:id])
$current_school = #school
#post = #school.posts.build if logged_in?
#posts = #school.posts
The school/show.html.erb:
<div>
<aside>
<%= render 'shared/post_form' %>
</aside>
</div>
<div>
<% if #school.posts.any? %>
<%= render #posts %>
<% end %>
</div>
_post_form.html.erb:
<%= form_for(#post) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new post..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
posts/_post.html.erb:
<li>
<%= image_tag post.user.avatar.url(:thumb) %>
<span class="user"><%= link_to post.user.name, post.user %></span>
<span class="content"><%= post.content %></span>
</li>
Post controller:
def create
#post = $current_school.posts.create(content:params[:post[:content],user_id:current_user.id)
if #post.save
flash[:success] = "Post created!"
redirect_to $current_school
else
render 'static_pages/home'
end
end
The problem is, when navigate to school#show, it says
undefined method `avatar' for nil:NilClass
If I delete the
#post = #school.posts.build if logged_in?
line, and the _post_form.html.erb fragment, the list of posts shows fine.
If I delete the _post.html.erb fragment, the new post function works fine as well.
But when they exist together, error comes up.
I guess the problem is at this line:
#post = #school.posts.build if logged_in?
because when I comment it out, the list of post shows fine. When I add it back, #posts can return a list of valid post, but post is nil.
Couldn't figure out what's wrong, please help.
Thanks
Edit: when visit url/schools/1, it crashes with NilClass error, post command returns
#<Post id: nil, content: nil, user_id: nil, school_id: 1, created_at: nil, updated_at: nil>
However, it should return the first post from #posts
Is it possible that
#post = #school.posts.build if logged_in?
assign #post a nil post, and when it renders #posts, it is looking for #post which become nil instead of first post in #posts?
If so, how can I correct this?
Edit2: I tried to change #post to #test:
#test = #school.posts.build if logged_in?
and the same nilClass error persists. So my previous guess is not valid. There should be something wrong with using build here. But what's wrong?
Your Post does not have a User associated with it on the new/show action, unless you're somehow plugging a user in on the model. Try:
#post = #school.posts.build(user: current_user) if logged_in?
This line: #post = #school.posts.build if logged_in?,
should be without build method.
Like this:
#post = #school.posts if logged_in?
Please try
#post=#school.posts if logged_in?
and see if it works.
Also another important debugging thing that I can tell you is the gem 'pry'.Install it and you can check the value of variables anywhere in your views/models/controllers.
Hope it helps.
I have the following view:
# index.html.web
<h1>Listing answers</h1>
<%= will_paginate %>
<%= render [#answers]%>
<%= will_paginate %>
And the following index action:
def index
#question = Question.find(params[:question_id])
#answers = #question.answers.paginate(page: params[:page])
respond_with(#answers)
end
I'm getting the following errror:
ActiveRecord::AssociationRelation [#<Answer id: 2, content: "b", user_id: nil, question_id: 1, created_at: "2015-04-27 14:59:32", updated_at: "2015-04-27 14:59:32">, #<Answer id: 3, content: "d", user_id: 1, question_id: 1, created_at: "2015-04-27 15:15:01", updated_at: "2015-04-27 15:15:01"] is not an ActiveModel-compatible object. It must implement :to_partial_path.
How can I fix it?
You can do it in two different ways.
<%= render #answers %> OR
<%= render partial: 'answer', collection: #answers %>
In both cases, you will get an object named answer in partial view.
And you must have a file named _answer.html.erb should exists in same directory.
When you pass a collection of instances to render the partial, you don't need to put any square bracket around the instance.
<%= render #answers %>
And don't forget to create a view name _answer.html.erb as well
source: http://guides.rubyonrails.org/layouts_and_rendering.html#passing-local-variables
index.html.erb
<h1>Listing answers</h1>
<%= will_paginate #answers%>
<%= render #answers%>
<%= will_paginate #answers%>
There must be a partial called _answer.html.erb
The above mention line will call _answer.html.erb with answer object in a loop.
I've been trying got find this answer, and maybe it's too simple...
In rails, how is the best way to iterate through results from activerecord pull the specific fields that you want?
I have a controller for comments (named posts) that pulls all records:
def index
#posts = Post.find(:all)
end
Then in the index view, when I use <%= #posts %> I get all of the data...which is great...
#<Post id: 1, user_id: "9", picture: nil, comments: "here's a first comment", title: nil, twitter: nl, frame: nil, created_at: "2012-05-09 04:21:16", updated_at: "2012-05-09 04:21:16"> #<Post id: 2, user_id: "9", picture: nil, comments: "here's a second comment", title: nil, twitter: "please", frame: nil, created_at: "2012-05-09 05:20:03", updated_at: "2012-05-09 05:20:03">
How can I now iterate through test so that the view shows the data from the comments and created_at fields:
Here's the first comment, 2012-05-09 04:21:16
Here's the second comment, 2012-05-09 05:20:03
I've tried the following and get an error.
<% #posts.each do |c| %>
<%= c.posts.comments %>
<%= c.posts.created_at %>
<% end %>
The "c" in #posts.each do |c| represents the specific post object in the #posts collection.
So, in a sense you are trying to do <%= post.posts.comments %>.
Here's how the code should look:
<% #posts.each do |p| %>
<%= p.comments %>
<%= p.created_at %>
<% end %>
Change things to this:
<% #posts.each do |post| %>
<%= post.comments %>
<%= post.created_at %>
<% end %>
I find it makes it easier for people to follow if you name the inner variable as the singular of the out variable -- therefore #posts on the outside becomes post on the inside.
Good luck!