I'm trying to create a way to have users comment on my posts. Currently I have All user posts showing up on my home page and then in the user profile only the current users posts. I would like to have it so that the comments appear only on the posts in the users profile. I tried to add a comment form in the user profile but I got an undefined method `comments' for nil:NilClass error.
My comments_controller looks like
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post])
#comment = #post.comments.create(params[:comment])
redirect_to post_path(#post)
end
I have a partial (_comment_form.html.erb) that I am rendering in the user profile which looks like
<h2>Add a comment:</h2>
<%= form_for ([#post, #post.comments.build]) do |f| %>
<div class="field">
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</div>
<div class="field">
<%= f.label :body %><br />
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My Comment model looks like
class Comment < ActiveRecord::Base
belongs_to :post
end
My Post model looks like
class Post < ActiveRecord::Base
attr_accessible :content
belongs_to :user
validates :content, :presence => true
validates :user_id, :presence => true
validates :user, :presence => true
validates :title, :presence => true
has_many :comments
default_scope :order => 'posts.created_at DESC'
end
My User Profile Looks like show.html.erb
<table class="profile" summary="Profile information">
<tr>
<td class="main">
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
<% unless #user.posts.empty? %>
<table class="posts" summary="User posts">
<%= render #posts %>
<%= render 'comments/comment_form' %>
</table>
<% end %>
</td>
<td class="sidebar round">
<strong>Name</strong> <%= #user.name %><br />
<strong>URL</strong> <%= link_to user_path(#user), #user %><br />
<strong>Tasks</strong> <%= #user.posts.count %>
</td>
</tr>
</table>
It might be that you haven't initialized #post in the new method of your controller and it's being used as nil. Always construct an empty model for your new form if it's practical:
def new
#post = Post.new(params[:post])
end
#post = Post.find_by_id(params[:post_id])
Are you initializing the #post in the show action of your PostsController? That would be required because you are redirecting from the create action of your CommentsController.
<%= render #posts %>
This line should reference #post instead. Note the trailing s, compared to all the other references to it in your code.
Can you see log/development.log to see where the error occurred? It wasn't clear from the question. But judging from your code, there are two possible locations:
#comment = #post.comments.create(params[:comment])
here is unlikely because the last line of code is Post.find which will raise a RecordNotFound if the id is not found
<%= form_for ([#post, #post.comments.build]) do |f| %>
This is very likely, can you do a puts #post.inspect and check your development.log to see if that is null. Assuming that it is null, you need to instantiate a Post object wherever you rendered _comment_form.html.erb
Related
I wanna update the nested attribute but failed, for example there is a Article, and a book has many comments. when I find the comment I have written has some mistakes, so I wanna modify it.
here is my code.
In code_snippet.rb:
class CodeSnippet < ApplicationRecord
has_many :annotations, dependent: :destroy
accepts_nested_attributes_for :annotations ,update_only: true ,reject_if: :all_blank, allow_destroy: true
end
In annotation.rb:
class Annotation < ApplicationRecord
belongs_to :code_snippet
end
In code_snippet_controller.rb:
def edit
#code_snippet = CodeSnippet.find(params[:id])
end
def update
#code_snippet = CodeSnippet.find(params[:id])
if #code_snippet.update(code_snippet_params)
redirect_to #code_snippet
else
render 'edit'
end
end
private
def code_snippet_params
params.require(:code_snippet).permit(:snippet)
end
In annotation.rb:
def edit
#code_snippet = CodeSnippet.find(params[:code_snippet_id])
#annotation = #code_snippet.annotations.find(params[:id])
end
def update
#code_snippet = CodeSnippet.find(params[:id])
#annotation = #code_snippet.annotations.find(params[:id])
if #annotation.update(annotation_params)
redirect_to #code_snippet
else
render 'edit'
end
end
In 'views/code_snippets/show.html.rb'
<div>
<h2>Annotations</h2>
<%= render #code_snippet.annotations %>
</div>
In 'views/annotations/_annotation.html.erb'
<p>
<strong>User:</strong>
<%= annotation.user %>
</p>
<p>
<strong>Line:</strong>
<%= annotation.line %>
</p>
<p>
<strong>Body:</strong>
<%= annotation.body %>
</p>
<p>
<%= link_to "Edit", edit_code_snippet_annotation_path(annotation.code_snippet,annotation) ,controller: 'annotation'%>
</p>
In 'views/annotations/edit.html.erb':
<%= form_for(#code_snippet) do |f| %>
<%= f.fields_for :annotation,method: :patch do |builder| %>
<p>
<%= builder.label :user %><br>
<%= builder.text_field :user %>
</p>
<p>
<%= builder.label :line %><br>
<%= builder.text_field :line %>
</p>
<p>
<%= builder.label :body %><br>
<%= builder.text_area :body %>
</p>
<p>
<%= builder.submit %>
</p>
<% end %>
<% end %>
what I wanna update the annotation without change the codesnippets. what should I do to change my code.
So.... There's a lot going on here so I'm going to start by suggesting a careful look at the docs
Firstly, let's look at your form:
CodeSnippet has_many :annotations
So your fields_for statement should be for :annotations, not :annotation. The fields for statement also should not take a method options key.
Next your code_snippets_controller:
As indicated by the docs, the parameter sent back from the nested attributes form will be under the key: annotations_attributes and will contain a hash of arrays.
You need to allow this attribute, and any attribute you wish to pass onto the annotation model with strong parameters:
params.require(:code_snippet).permit(annotations_attributes: [:some, : permitted, :params])
I believe this is all you need to get your example working. But, if you run into more troubles, I recommend a spending some quality time with a few binding.pry statements to introspect on the actual behaviour of your code.
I'm trying to create a reviews model for company pages. For this I have:
Models
user.rb
has_many :reviews
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :reviews
end
My reviews controller is:
def create
#company = Company.find_by_slug(params[:id])
#review = #company.reviews.create(params[:review])
#review.save
redirect_to company_path(#company)
end
and I have this code in the company show page:
<% #company.reviews.each do |review| %>
<p>
<strong>Title:</strong>
<%= review.title %>
</p>
<p>
<strong>Avantage:</strong>
<%= review.avantage %>
</p>
<p>
<strong>Inconvenient:</strong>
<%= review.inconvenient %>
</p>
<% end %>
</br>
<%= form_for([#company, #company.reviews.build]) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :avantage %><br>
<%= f.text_area :avantage %>
</div>
<div class="field">
<%= f.label :inconvenient %><br>
<%= f.text_area :inconvenient %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
However, when I go to a specific company page and try to create a review for this company I'm getting this error message undefined method reviewsfor nil:NilClass
Instead of #company = Company.find_by_slug(params[:id]) use this code #company = Company.friendly.find(params[:company_id])
There are a couple of things you may find useful:
If you're using Rails 4, you may encounter a further problem. In the third line of your create method, you are using unsecure params directly in a .create call. Check out the Rails Guide page on "strong params".
If you implement strong parameters as mentioned above, you should probably deliberately omit the company_id field from the list of permitted params.
Assuming your users are allowed to write a review for any company in your system, it might be simpler for you to embed the company_id as a hidden field in your form. This would allow you to also simplify the controller method. For example:
# _form.html.erb
<%= form_for(#review) do |f| %>
<%= f.hidden_field :company_id, value: #company.id %>
...bla bla bla
<% end %>
Then, in your reviews_controller...
# reviews_controller.rb
def create
#review = Review.new(approved_params)
if #review.save
flash[:success] = 'Review created!'
else
flash[:error] = "Review wasn't saved"
end
#company = #review.company
redirect_to #company
end
def approved_params
params.require(:review).permit(:title, :avantage, :inconvenient, :company_id)
end
In your companies_controller, you should add this to your show method
# companies_controller.rb
def show
#company = Company.find(params[:id]
# add this line below...
#review = Review.new
end
I hope this helps.
Having an issue getting a value from a form to the controller. I am using rails 4.0.
My view looks like this (new.html.erb)
<h1> POST A NEW LISTING </h>
<% if current_user.nil? %>
<h2>You must be logged in to view this page </h2>
<% else %>
<%= form_for [#user, #listing] do |f| %>
<%= f.label :title, 'Title' %> <br />
<%= f.text_field :title %>
<%= f.label :general_info, 'General Information' %> <br />
<%= f.text_area :general_info %>
<%= f.label :included, 'Included' %> <br />
<%= f.text_field :included %>
<%= f.label :length, 'Length' %> <br />
<%= f.text_field :length %>
<%= f.label :price, 'Price' %> <br />
<%= f.text_field :price %>
<%= fields_for #tagging do |u| %>
<%= u.label :tag, 'Tag' %> <br />
<%= u.text_field :tag %>
<% end %>
<%= f.submit "submit" %>
<% end %>
<% end %>
I am trying to add tags. I have 2 models to handle the tags:
models -> tag.rb
class Tag < ActiveRecord::Base
has_many :taggings
has_many :listings, through: :taggings
end
models -> tagging.rb
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :listing
end
tags keep track of the tag names themselves, while taggings keeps track of the connection to the listings.
When a user submits the form they will type in a string tag such as: "exampletag". I then need to search my tag model to get the tag_id of that specific tag. If it exists I need to put the tag_id and listing_id into taggings. Currently I have the listing_id correct, but I am having a problem even accessing the :tag symbol from the form.
This is what I have so far. Not that currently :tag_id is hardcoded in because I cant get #current_tag to return the information I need.
listings_conroller.rb #create
def create
#user = User.find(current_user.id)
#listing = #user.listings.build(listing_params)
#save before we get the listing ID
if #listing.save
#current_tag = Tag.where(:name => params[:tag])
#taggings = Tagging.new(:tag_id => 1, :listing_id => #listing.id)
if #taggings.save
flash[:success] = "Success"
redirect_to root_path
else
render :action => 'new'
end
else
render :action => 'new'
end
end
I thought that #current_tag = Tag.where(:name => params[:tag]) would return the correct listing but it seems to be returning null when I submit the form with a name which is in the database.
got it!
Since tags is nested under taggings I needed to access the param as:
params[:tagging][:tag]
instead of params[:tag]
I am using single tablable inheritance(STI) to create different types of articles.
But now I have problem while creating articles.(I can do it only in console).
Here are my models
Article.rb
class Article < ActiveRecord::Base
attr_accessible :content, :title
validates :title, :presence => true
end
And TutorialArticle.rb
class TutorialArticle < Article
attr_accessible :author
validates :author, :presence => true
end
Here is my _form
<%= form_for(#article) do |f| %>
<%= f.hidden_field :type %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<%= render :partial => "edit" + f.object.type.downcase, :locals=>{:f=>f} %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
But now I have a problem in article_controller.rb in create method
def create
# create the desired subtype based on the hidden field :type in the form
#article = Object.const_get(params[:article][:type]).new(params[:article])
if #article.save
flash[:notice] = "Successfully created post."
redirect_to #article
else
render :action => 'new'
end
end
Now when I fill in the form and press Create Article button, I get the folloiwing error
undefined method '[]' for nil:NilClass
I even tried to hard code to understand what is wrong
and if I try to change #article = Object.const_get(params[:article][:type]).new(params[:article])
to
#article = TutorialArticle.new(params[:article])
my create method doesn't save the article. it just redirects to create new article page.
Could you please help me to solve the problem?
Ok, the printing of params helped. Add this to your TutorialArticle model:
def self.model_name
return Article.model_name
end
This will make your forms pass article instead of tutorial_article, and the rest should work as expected. You'll need this override in all your other Article subclasses.
I have created an simple rails project. All worked fine until I tried to add a new model Paintings that belongs_to treatment and an Patient that has_many Paintings through Treatment. So somehow the nested form I created does not show up, I believe it has to do with the controller! Thanks, and greetings from Germany!
Treatments controller:
class TreatmentsController < ApplicationController
def create
#patient = Patient.find(params[:patient_id])
#treatment = #patient.treatments.create(params[:treatment])
redirect_to patient_path(#patient)
end
def destroy
#patient = Patient.find(params[:patient_id])
#treatment = #patient.treatments.find(params[:id])
#treatment.destroy
redirect_to patient_path(#patient)
end
end
And the form for treatments with nested fields_for that doesn't show up:
<%= form_for([#patient, #patient.treatments.build]) do |f| %>
<div class="field">
<%= f.label :content %>
<%= f.text_area :content, :cols => "30", :rows => "10" %>
</div>
<div class="field">
<%= f.label :category_id %>
<%= f.collection_select :category_id, Category.find(:all), :id, :typ %>
</div>
<%= f.fields_for :paintings do |ff| %>
<div class="field">
<%= ff.label :name, 'Tag:' %>
<%= ff.text_field :name %>
</div>
<% end %>
<div class="field">
<%= f.submit nil, :class => 'btn btn-small btn-primary' %>
</div>
<% end %>
UPDATE:
Show Site:
<% #patient.treatments.each do |treatment| %>
<tr>
<td><%= treatment.category.try(:typ) %></td>
<td><%= treatment.content %></td>
<td><%= treatment.day %></td>
<td><div class="arrow"></div></td>
</tr>
<tr>
Please try
= f.fields_for :paintings, Painting.new do |p|
Even the question is quite old, but you are missing the new that is crucial to this question. The methods destroy and create doesn't have anything with this issue. If you have a new method, which looks something like this:
class TreatmentsController < ApplicationController
def new
#patient = Patient.new
end
end
Then the solution would be do modify the new method to "build" the paintings like this:
class TreatmentsController < ApplicationController
def new
#patient = Patient.new
#patient.paintings.build
end
end
Try doing following in new action in controller
#patient.treatments.build
Check out build_association part http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to
You should also read about nested attributes.
Use those for reference
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Dumb error, but I was using:
<% f.fields_for :partner do |fp| %>
instead of:
<%= f.fields_for :partner do |fp| %>
is case you have an association where it is optional:
in the controller:
#associated_model = #model.associated_model || #model.build_associated_model
in the view:
<%= form.fields_for :associated_model do |am| %>