I am trying to create a nested resource so that products can have notes associated with them. I have set up the associations within the model etc, but when I try to use the form to create a new note, I get the following error:
NoMethodError in Notes#create
Showing /Users/myusername/myapp/app/views/notes/_form.html.erb where line #2 raised:
undefined method `notes_path' for #<#<Class:0x00007fb3630b1ad0>:0x00007fb361eab868>
This is the line it is referring to:
<%= simple_form_for [#product, #note] do |f| %>
This are the new & create actions in the notes controller:
def new
#product = Product.find(params[:product_id])
#note = #product.notes.build
end
def create
#note = Note.new(product: #product)
respond_to do |format|
if #note.save
format.html { redirect_to product_notes, notice: 'Note was successfully created.' }
else
flash.now[:error] = "It doesnt work"
render 'new'
end
end
end
and the form partial:
<%= simple_form_for [#product, #note] do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<div class="form-inputs">
<%= f.input :content %>
<%= f.input :author %>
<%= f.check_box :visible %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
I am going round in circles with making changes, and cannot seem to find any documentation on nested resources that isn't deprecated. Can anybody assist, please?
Edited to add:
I changed my controller action to something based on PGill's answer and can now get the page to load without an action controller error. However, it now re-renders the new note form, with errors saying that the form fields cannot be blank. They were not blank when I submitted them - what's happening to cause this?
Updated controller action:
def create
#product = Product.find(params[:product_id])
#note = #product.notes.new
respond_to do |format|
if #note.save
format.html { redirect_to product_notes_path(#product), notice: 'Note was successfully created.' }
else
format.html { render :new, notice: 'Note failed to be created.' }
end
end
end
When I was previously getting errors, it had this as the request parameters, so they are getting passed?
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"lotsofletters",
"note"=>{"content"=>"test", "author"=>"test", "visible"=>"0"},
"commit"=>"Create Note",
"product_id"=>"1"}
Referring to your edit; of course you should get empty fields errors because you are creating a new object #note without providing any attributes for it:
#note = #product.notes.new
it should be like
#note = #product.notes.build(params[:note])
also take care to provide a sanitizer for note in notes controller :
private
def note_params
params.require(:note).permit(:content, :author, :visible, :product_id)
end
so your code in create will look like:
def create
#product = Product.find(params[:product_id])
#note = #product.notes.build(note_params)
respond_to do |format|
if #note.save
format.html { redirect_to product_notes_path(#product), notice: 'Note was successfully created.' }
else
flash.now[:error] = "It doesnt work"
render 'new'
end
end
end
private
def note_params
params.require(:note).permit(:content, :author, :visible, :product_id)
end
#product is nil in create
Your form is failing validations and rendering new
update the create action to
def create
#product = Product.find(params[:product_id])
#note = #product.notes.new
respond_to do |format|
if #note.save
format.html { redirect_to product_notes_path(#product), notice: 'Note was successfully created.' }
else
flash.now[:error] = "It doesnt work"
render 'new'
end
end
end
redirect_to should be product_notes_path(#product) notes#index
Related
I'm currently trying to implement polymorphic comments within my app, but I'm running into problems with converting my partial form.
I was following this tutorial, step-by-step-guide-to-polymorphic-associations-in-rails, but it didn't go over this section.
Mainly, I have an Image that is commentable, and a partial at the bottom to allow users to comment on the Image.
However, when submitting the form, it can't find the #commentable object as params[:id] and params[:image_id] are both nil.
I'm having problems understanding how I'm supposed to pass this information, as the partial knows this information, but the controller does not.
// images/show.html.erb
<div class="container comment-form" >
<%= render 'comments/form', comment: #image.comments.build %>
</div>
// comments/_form.html.erb
<%= bootstrap_form_for(comment) do |f| %>
<%= f.text_area :message, :hide_label => true, :placeholder => 'Add a comment' %>
<%= f.submit 'Reply', :class=> 'btn btn-default pull-right' %>
<% end %>
// comments_controller.rb
def create
#commentable = find_commentable
#comment = #commentable.comments.build(comment_params) <<<<<
respond_to do |format|
if #comment.save
format.html { redirect_to (comment_path #comment), 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
Error on #comment = #commentable.comments.build(comment_params)
undefined methodcomments' for nil:NilClass`
I also noticed that there is no id in the request parameters.
Parameters:
{"utf8"=>"✓", "authenticity_token"=>"xxxxxx", "comment"=>{"message"=>"nice photo"}, "commit"=>"Reply"}
Thanks for your help.
When you pass a record to a form builder rails uses the polymorphic route helpers* to lookup the url for the action attribute.
To route to a nested resource you need to pass the parent and child(ren) in an array:
bootstrap_form_for([#commentable, #comment])
# or
bootstrap_form_for([#comment.commentable, #comment])
This would give the path /images/:image_id/comments for a new record and /images/:image_id/comments/:id if it has been persisted.
You are attempting to build your comment twice. Once in the show.html, with comment: #image.comments.build and then again in your create method with #comment = #commentable.comments.build(comment_params) <<<<<
The tutorial you linked to included the private method below. If your goal is to create a comment that belongs to your Image object, the method below would look for a param with your image_id, and would return Image.find(params[:image_id])
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
You could change your show.html to pass in your image_id as a hidden param with:
<div class="container comment-form" >
<%= render 'comments/form', image_id: #image.id %>
</div>
I want to add some extra parameters (categories) when filling my standard form for Event model. They are not in my events table (I have table categories_events and hmbtm in both Events and Category models). Here is my code for _form :
<% #categories.each do |category| %>
<div class="field">
<%= check_box_tag(:category, category.id) %>
<%= label_tag( :category, "#{category.name}" ) %>
</div>
<div class="actions">
<%= f.submit %>
<% end %>
I'm passing categories in new action - it's simple Category.all
Here is my code in events controller
def new
#event = Event.new
#categories = Category.all
end
def create
#event = Event.new(event_params)
#category_id = Category.find(params[:category])
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render action: 'show', status: :created, location: #event }
else
format.html { render action: 'new' }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
Later I want to put category_id and event_id into categories_events but I have NoMethodError
undefined method `category_id' for #<Event:0x374c268>
and #event.safe is the problem
Parameters look like this
{"utf8"=>"✓",
"authenticity_token"=>"stL+sdIhxttrk3KjkLJsuCXubjaDpNBbrLYtpjv8clw=",
"event"=>{"name"=>"asdsa",
"place"=>"asdas",
"description"=>"dsadsa"},
"commit"=>"Create Event",
"category"=>"2"}
I think that the problem is in too many parameters in new(event_params) but looking at brackets in parameters tells me that it shouldn't be a problem to make it acceptable for rails.
Error stacktrace:
http://pastebin.com/kQK1fni6
Updated event_params
def event_params
params.require(:event).permit(:name, :place, :description, :category_ids)
end
Adjust your check_box_tag to the following:
<%= check_box_tag("event[category_ids][]", category.id, #event.categories.include?(category)) %>
<%= label_tag("event[category_ids][]", category.name) %>
One more thing, you'll have to add category_ids to your whitelisted attributes.
To resolve Couldn't find Category without an ID
Replace
#category_id = Category.find(params[:category_ids])
with
#category_id = Category.find(params[:event][:category_ids])
If you check the params hash you'll see that due to the update in checkbox code(as suggested by H-man), category_ids would be part of params[:event] keys value.
I am new to rails.I have some confusion about about rails object life cycle.In rails we have the bellow code.
class UsersController < ApplicationController
# GET /users
# GET /users.json
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
# GET /users/1
# GET /users/1.json
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
end
# GET /users/new
# GET /users/new.json
def new
#user = User.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
# GET /users/1/edit
def edit
#user = User.find(params[:id])
end
# POST /users
# POST /users.json
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PUT /users/1
# PUT /users/1.json
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_url }
format.json { head :no_content }
end
end
end
then in the form we have
<%= form_for(#user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
my confusion is in the new action in controller i have #user = User.new
and again in the create #user = User.new(params[:user]).
then in form i have <%= form_for(#user) do |f| %> .
My question is here at the form the #user object actually means waht?
I mean does this #user is going to hit the new action or create action.
If it is going to hit the create action then how this is happening because the form is actually comes from the new action so i can't figure it out how its hitting to the create action .
i know its very simple question.But i dont know how its happening as i am new to rails.
Please help me to make me understand the object flow.
thanks in advance.
The "new" action makes a new object and shows a form for editing it. That form submits to the "create" action because the object has not been saved yet.
If you did
form_for #user
and #user was a previously-saved object, the form would submit to the update action instead.
form_for is a bit magical, like a lot of rails: it does two things:
sets the "action" attribute of the form to point at either "/users" (for create) and "/users/:id" (for update)
in the case of update (ie for objects that already have an id) it also adds a hidden field which triggers the update action: this hidden field will look like this: <input type="hidden" value="put" name="_method">.
Have a look at form_for in your rails api.
It's hitting create action because of the proper form URL. When you run rake routes command, you'll see that POST /users leads to users#create action - and that's the URL in the new form. URL is set (and form fields are generated) properly by Rails because you pass User instance to the form.
I have a form that saves data, but it gets routed to the wrong URL.
If my form is in
localhost:3000/users/1/styles/1
And when I submit the form, I get redirected to this:
localhost:3000/styles/1
and then I get an error:
Couldn't find User without an ID
views/comments/_form.html.erb
<%= form_for [#commentable, #comment] do |f| %>
<%= f.text_area :content, rows: 3 %>
<%= f.submit %>
<% end %>
styles_controller.rb
def show
#user = User.find(params[:user_id])
#style = #user.styles.find(params[:id])
#commentable = #style
#comments = #commentable.comments
#comment = Comment.new
end
comments_controller.rb
before_filter :get_commentable
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(params[:comment])
#comment.user = current_user
if #comment.save
redirect_to #commentable, notice: "Comment created."
else
render :new
end
end
private
def get_commentable
#commentable = params[:commentable].classify.constantize.find(commentable_id)
end
def commentable_id
params[(params[:commentable].singularize + "_id").to_sym]
end
routes.rb
resources :styles do
resources :comments, :defaults => { :commentable => 'style' }
end
Please let me know if there's other information that is needed. Why am I getting rerouted to a different url? My comment does save into my database.
Thank you
If you want to go back to localhost:3000/users/1/styles/1 after creating comment, you should change
if #comment.save
redirect_to #commentable, notice: "Comment created."
else
to
if #comment.save
redirect_to [User.find(params[:user_id]), #commentable], notice: "Comment created."
else
Edit: Should use User that owns the style not current user
So I'm trying to get the errors from my form that is rendered as a partial inside my root_path. After I attempt to post it, and it fails (or succeeds), I want to redirect back to the root_path. However, redirect_to decides to not save any information for the validation.
Wondering how to do this.
class PostsController < ApplicationController
def new
#post = Post.new
end
def create
#nom = current_user.noms.build(params[:nom])
if #nom.save
flash[:success] = "Nom created!"
redirect_to root_path
else
flash[:error] = #nom.errors
redirect_to root_path
end
In my Home/Index, I render the partial for the form of the post.
= form_for [current_user, #post] do |f|
= f.text_field :name
= f.select :category
= f.text_area :description
= f.submit "Post", class: "btn btn-primary"
- #post.errors.full_messages.each do |msg|
%p
= msg
It should be keeping the errors at the bottom of the form after it redirects to the root_path.
I'd also like to keep the information that was there after the validation failed.
You should not use redirect in this case, instead use render:
class PostsController < ApplicationController
#..
def create
#nom = current_user.noms.build(params[:nom])
if #nom.save
flash[:success] = "Nom created!"
redirect_to root_path
else
flash[:error] = #nom.errors
render :template => "controller/index"
end
end
Replace controller/index with names of you controller and action
Also check this question
This seemed to work for me
format.html { redirect_to :back, flash: {:errors => "Document "+#requested_doc.errors.messages[:document][0] }}
I don't know if this could cause any other exception issues.
You can not use redirect_to for showing the error messages of an object because while redirecting it discards your object which does have linked with the error_messages & took a new object to redirect the path.
So in that case, you only have to use render.
respond_to do |format|
format.html {
flash[:error] = #account.errors.full_messages.join(', ')
render "edit", :id => #account._id, sid: #account.site._id
}
end