Error during the creation of hidden fields for nested objects - ruby-on-rails

I am using rails 2.3.5. I have a Blog model and Blog has many comments. This is my Blog controller show action
def show
#blog = Blog.find(params[:id])
#comment = Comment.new
end
I would display the Blog and at the end would have an option for creating comment. So I add this in blogs/show.html.erb.
<% form_remote_for #comment do |f| %>
<%= f.label :content %>
<%= f.text_area :content, :rows => 6 %>
<%= f.hidden_field :blog => #blog %>
<%= f.submit %>
<% end %>
But i get the following error when i run this
NoMethodError in Blogs#show
Showing app/views/blogs/show.html.erb where line #270 raised:
undefined method `blog#<Blog:0xb677d8d0>' for #<Comment:0xb67762b0>
Extracted source (around line #270):

Comment model should have belongs_to :blog
Blog model should have has_many :comments
Initialize the comment in the controller like this:
#blog.comments.new
The view should be like this:
<%= f.hidden_field :blog_id %>

you have to hide the id of the blog not the blog object.
<%= f.hidden_field :blog_id%>

The problem is your f.hidden_field line. The first parameter should be the attribute name of the #comment you want in the field, but in your code it's a Hash.
I'd suggest adjusting your show action to set #comment = #blog.comments.build, and change the view to read f.hidden_field :blog_id.

Related

Ancestry Gem for Nested Comments with Rails causing undefined method error

I have been trying to fix an error associated with using the Ancestry gem for comments on my app for Rails 4. I used railscast episode 262 as a guide. However, unlike the episode, my comments model is a nested resource inside another model.Before I go further, I will supply the necessary code for reference. If you like to read the error right away, it is mentioned right after all the code snippets.
The Relevant Models:
class Comment < ActiveRecord::Base
has_ancestry
belongs_to :user
belongs_to :scoreboard
end
class Scoreboard < ActiveRecord::Base
#scoreboard model is like an article page on which users can post comments
belongs_to :user
has_many :teams, dependent: :destroy
has_many :comments, dependent: :destroy
end
Relevant code in the route file:
resources :scoreboards do
resources :comments
resources :teams, only: [:edit, :create, :destroy, :update]
end
The Scoreboards Controller Method for the page on which one can post comments:
def show
#scoreboard = Scoreboard.find_by_id(params[:id])
#team = #scoreboard.teams.build
#comment = #scoreboard.comments.new
end
The Comments Controller:
class CommentsController < ApplicationController
def new
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#comment = #scoreboard.comments.new(:parent_id => params[:parent_id])
end
def create
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#comment = #scoreboard.comments.new comment_params
if #comment.save
redirect_to scoreboard_url(#comment.scoreboard_id)
else
render 'new'
end
end
private
def comment_params
params.require(:comment).permit(:body, :parent_id).merge(user_id: current_user.id)
end
end
I will include the migration for the ancestry gem if any mistakes were made on that :
class AddAncestryToComments < ActiveRecord::Migration
def change
add_column :comments, :ancestry, :string
add_index :comments, :ancestry
end
end
The following code shows the view code:
Scoreboard#show View which is giving me the error in the last line:
<div class= "comment-section">
<%= form_for [#scoreboard, #comment] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :body, class: "comment-field" %>
<%= f.hidden_field :parent_id %> #is it needed to include this here? because this form is for new comments not replies
<%= f.submit "Join the discussion...", class: " comment-button btn btn-primary" %>
<% end %>
<%= nested_comments #scoreboard.comments.reject(&:new_record?).arrange(:order => :created_at) %>
</div>
The (comments partial)_comment.html.erb View:
<div class=" comment-div">
<p> Posted by <%= link_to "#{comment.user.name}", comment.user %>
<%= time_ago_in_words(comment.created_at) %> ago
</p>
<div class="comment-body">
<%= comment.body %>
<%= link_to "Reply", new_scoreboard_comment_path(#scoreboard, comment, :parent_id => comment) %>
</div>
</div>
The helper method to render comments:
def nested_comments(comments)
comments.map do |comment, sub_comment| #the comments.map also gives me an error if I choose to render the comments without the .arrange ancestry method
render(comment) + content_tag(:div, nested_comments(sub_comment), class: "nested_messages")
end.join.html_safe
end
The new.html.erb for Comments which one is redirected to for the replies form submission:
<%= form_for [#scoreboard, #comment] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :body, class: "comment-field" %>
<%= f.hidden_field :parent_id %>
<%= f.submit "Join the discussion...", class: " comment-button btn btn-primary" %>
<% end %>
Upon creating a scoreboard, I am redirected to the show page, where i get the following error:
undefined method `arrange' for []:Array
Even though the array of comments is empty, I get the same error if it wasnt. I have tried .subtree.arrange but that gives me the same error. Also, the ancestry documentation said that .arrange works on scoped classes only. I don't know what that means. I would appreciate some help on making the page work so the comments show properly ordered with the replies after their parent comments. If this is the wrong approach for threaded comments(replies and all), I would appreciate some guidance on what to research next.
.reject(&:new_record?) this will return an array. The error sounds like arrange is a scope on ActiveRecord. So move the reject to the end and it should work.
#scoreboard.comments.arrange(:order => :created_at).reject(&:new_record?)
In regards your comment nesting, I have implemented this before, and found the Railscasts recommendation of a helper to be extremely weak.
Passing parent_id to a comment
Instead, you're better using a partial which becomes recursive depending on the number of children each comment has:
#app/views/scoreboards/show.html.erb
<%= render #comments %>
#app/views/scoreboards/_comment.html.erb
<%= link_to comment.title, comment_path(comment) %>
<div class="nested">
<%= render comment.children if comment.has_children? %>
</div>

Rails 4: accepts_nested_attributes_for and mass assignment

I am trying to reproduce railscast #196 in Rails 4. However, I'm experiencing some problems.
In my example I try to generate a Phonebook - each Person could have multiple PhoneNumbers
These are important parts of my controller:
class PeopleController < ApplicationController
def new
#person = Person.new
3.times{ #person.phones.build }
end
def create
#person = Person.create(person_params)
#person.phones.build(params[:person][:phones])
redirect_to people_path
end
private
def person_params
params.require(:person).permit(:id, :name, phones_attributes: [ :id, :number ])
end
end
and this is my new view
<h1>New Person</h1>
<%= form_for :person, url: people_path do |f| %>
<p>
<%= f.label :name %> </ br>
<%= f.text_field :name %>
</p>
<%= f.fields_for :phones do |f_num| %>
<p>
<%= f_num.label :number %> </ br>
<%= f_num.text_field :number %>
</p>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
needless to say i have has_many :phones and accepts_nested_attributes_for :phones in the my person model and belongs_to :person in the phone model.
I have the following issues:
Instead of 3 phone-number-fields there is just one in the new form
When I submit the form I get an error:
ActiveModel::ForbiddenAttributesError
in the line
#person.phones.build(params[:person][:phones])
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"l229r46mS3PCi2J1VqZ73ocMP+Ogi/yuYGUCMu7gmMw=",
"person"=>{"name"=>"the_name",
"phones"=>{"number"=>"12345"}},
"commit"=>"Save Person"}
In principle I would like to do this whole thing as a form object, but I think if I don't even get it with accepts_nested_attributes, I have no chance to do it as a form object :(
In order to get three phones in the view change form_for :person to form_for #person (you want to use the object you've built here) as follows:
<%= form_for #person, url: people_path do |f| %>
This should fix the ForbiddenAttributes error as well.
And your create action could be:
def create
#person = Person.create(person_params)
redirect_to people_path
end
Update:
<%= form_for :person do |f| %> creates a generic form for the Person model and is not aware of the additional details you apply to a specific object (in this case #person in your new action). You've attached three phones to the #person object, and #person is not the same as :person which is why you didn't see three phone fields in your view. Please reference: http://apidock.com/rails/ActionView/Helpers/FormHelper/form_for for further details.

Ruby on Rails Association Form

So i'm making a web app using ROR and I can't figure out what the right syntax for this form is. I'm currently making an association type of code for comments and posts.
<%= form_for #comment do |f| %>
<p>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.label :comment %><br />
<%= f.text_area :comment %>
</p>
<p>
<%= f.submit "Add Comment" %>
</p>
<% end %>
Your form is fine, except the first line (and you don't need a hidden field for the user_id, thats done through your relationship):
<%= form_for(#comment) do |f| %>
Should be:
<%= form_for([#post, #comment]) do |f| %>
Now you render a form for creating or updating a comment for a particular post.
However, you should change your model and controller.
class Post
has_many :comments
end
class Comment
belongs_to :post
end
This will give you access to #post.comments, showing all comments belonging to a particular post.
In your controller you can access comments for a specific post:
class CommentsController < ApplicationController
def index
#post = Post.find(params[:post_id])
#comment = #post.comments.all
end
end
This way you can access the index of comments for a particular post.
Update
One more thing, your routes should also look like this:
AppName::Application.routes.draw do
resources :posts do
resources :comments
end
end
This will give you access to post_comments_path (and many more routes)

Rails Linked Models undefined method for nil:NilClass

I've been following this, making modifications where necessary.
I have equivalents of #post and #comment. My comments are 'questions'. I've tried following this procedure again but to the comments (rather than posts). Comments on comments if you like, these are answers/responses to the questions.
I've not been successful in getting this to work.
undefined method 'answers' for nil:NilClass`
is the error in answers/_form.html.erb which is rendered as part of a comment, which is in turn rendered as part of my post equivalent.
<%= form_for([#question, #question.answers.build]) do |f| %> is the line that raises the exception. I'm thinking the problem is that #question isn't being given a value for some reason?
Either that or my routes file isn't right (would be my guess at least).
resources :posts-equivalent do
...
resources :questions
end
resources :questions do
resources :answers
end
This has had me stumped all day - any ideas would be appreciated. If you'd like to see any more code drop a comment and I'll update it here.
Additional Code:
Error Message:
Showing .../app/views/answers/_form.html.erb where line #1 raised:
undefined method `answers' for nil:NilClass
Extracted source (around line #1):
1: <%= form_for([#question, #question.answers.build]) do |f| %>
Answers Controller:
class AnswersController < ApplicationController
def create
#question = Question.find(params[:question_id])
#answer = #question.answers.create(params[:answer])
redirect_to concept_path(#question.concept)
end
end
It looks like your AnswersController is not assigning a value to the #question instance variable. It's not clear what action you are running, but I'm assuming it's either new or edit based on the fact that you have a form. You should have something like this in those actions:
class AnswersController < ApplicationController
def new
#question = Question.find(params[:question_id])
#answer = #question.answers.new
end
def edit
#question = Question.find(params[:question_id])
#answer = #question.answers.find(params[:id])
end
end
Then you can have a single form shared between new and edit:
<%= form_for([#question, #answer]) %>
In the end I sorted this in a different manner. Each question was only ever going to have one answer and so I just gave question an answer attribute and assigned that.
On the post page:
...
<%= render #questions %>
<%= render "questions/form" %>
...
In _form.html.erb
<%= form_for([#concept, #concept.question.build]) do |f| %>
<%= f.hidden_field :contributor_id, :value => session[:contributor_id] %>
<div class="field">
<%= f.text_field :question, :placeholder => "Follow-up Question" %>
</div>
<% end %>
In the _question.html.erb
<%= form_for(question) do |f| %>
<div id="question">
"<%= question.question %>" asked by <%= question.contributor.email %> | <%= question.created_at %>.
<div id="answer">
- Answer: <%= question.answer %>
<%= f.text_field :answer, :placeholder => "Respond..." %>
</div>
</div>
<% end %>
Add this to the questions controller:
def update
#question = Question.find(params[:id])
#question.update_attributes(params[:question])
#concept = Concept.find(#question.concept.id)
redirect_to concept_path(#concept)
end
And that worked for me, I know it's not what I originally asked but that fixed my problem. Thanks to those who did try to help.

Use form_for for non-attribute values

I have a form_for like so:
<%= form_for(#post) do |f| %>
I would like to submit a parameter that is not an attribute of the model
<%= f.text_field :label%>
I have a Label model (labels has_many :posts and posts has_many :labels) and in the create action of the posts_controller I want to create a new Label object based off the label text_field. With the above text_field I get:
undefined method `label'for #<Object>
How can I go about achieving this?
Thanks in advance!
If your attribute doesn't exist into database table, and still you want to use it, then you have specify that attribute into model by following way
attr_accessor :name, :email, :content
You can try fields_for method for the creation of associated objects.
<%= form_for(#post) do |f| %>
<%= fields_for #post.labels do |label| %>
<%= label.text_field :name %>
<%= label.text_area :description %>
<% end %>
<% end %>
To display default number of labels you need to build the associated objects in the controller.
In your posts controller add
def new
#post = Post.new
#To build 2 labels use
2.times do
#post.labels.build
end
end
To accept the nested attributes add below code in your Post model.
class Post < ActiveRecord::Base
accepts_nested_attributes_for :labels
end
Someone posted this then deleted...

Resources