Rails call another controller's action - ruby-on-rails

I have 2 models, Posts and Comments. I would like to create a button on the Post show view which directs it to Comment new action. So I create a new action in Post:
def comment
#post = Post.find(params[:id])
redirect_to new_comment_path
end
I want to save the post_id in the Comment models, so I create d hidden field in the new comment form:
<div class="field">
<%= f.hidden_field :post_id, :value => #post.id %>
<%= f.label :body %><br />
<%= f.text_field :body %>
</div>
But error appeared: "Called id for nil".
I am very new, can anyone help? Or should I use other approach?

Well you are missing to pass the value,
I had tried out this way and it works, for your example.
edited:
redirect_to :controller=>'comments', :action=>'new_comment', :post_id=>#post.id
receive as #post_id = params[:post_id]

Related

How to get Rails form_for to point towards different controller

I have a form for creating new comments. This code exists in a page that is under a different controller (let's say it's app/views/posts/show.html.erb).
<%= form_for Comment.new do |f| %>
<%= f.label :content %>
<%= f.text_field :content %><br/>
<%= f.submit %>
<% end %>
The form works if I have Comment.new like above, but I want to use an instance variable like form_for #comment, similar to the first code snippet in this link: https://api.rubyonrails.org/v5.2.3/classes/ActionView/Helpers/FormHelper.html
In order to do so, I thought I need to define a new function like this and assign an empty comment. I tried putting this code in both the posts_controller and comments_controller.
def new
#comment = Comment.new
end
But when I replace Comment.new with #comment, I get this error: ActionView::Template::Error (First argument in form cannot contain nil or be empty):
This leads me to believe that neither of the new methods are being called. What am I doing wrong here?
My routes.rb looks like this:
Rails.application.routes.draw do
root to: 'posts#show'
resources :messages
end
if you are using show page (app/views/posts/show.html.erb) to display form
add this line in the show action of posts controller
# posts_controller
def show
#comment = Comment.new
end
and if you also want to submit your form other than the comment's create action mention the url in form_for tag
<%= form_for #comment, url: posts_path do |f| %>
<%= f.label :content %>
<%= f.text_field :content %><br/>
<%= f.submit %>
<% end %>

param is missing or the value is empty: status

When I try to submit a new status I get the below. I have a registration form that is working fine, not sure what is going on here. Appreciate the help.
def status_params
params.require(:status).permit(:type_id, :user_id)
end
and
def create
#status = Status.new(status_params)
respond_to do |format|
if #status.save
The request
{"utf8"=>"✓",
"authenticity_token"=>"gZPV4FfSm2eb+pGPbAAqI4zA/LHJiAsRkHdJar/aU3G63oBiaLr55zPoRv3K+7EmelN2Nofj/CTZ+qPtoYih5w==",
"type"=>{"user_id"=>"3"},
"type_id"=>"Ocular",
"commit"=>"Create Status"}
My View
<div class="field">
<%= f.label :user_id %><br>
<%= select(:user_id, #user_options) %>
</div>
<div class="field">
<%= f.label :type_id %><br>
<%= select_tag :type_id, options_for_select(mg_types) %>
</div>
Your params are not in status hash. So,
Try this -
def status_params
params.permit(:type_id, type[:user_id])
end
I can see multiple problems here:
The posted parameters are not wrapped in a status hash.
You are posting an attribute hash called type that contains a user_id, which is inconsistent with your whitelisted attributes.
You are also posting an attribute called type_id that contains a String (and not an id).
Posting both type and type_id conflicts because Rails will set these attributes on the Status model and one will probably override the other.

Rails 4.2 form error handling outside controller default view

I'm following the official starter guide from rails guides. It's a great tutorial but I'm having doubts about how to handle form errors display for the comments form inside the article view.
<%= form_for([#article, #article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
I don't know if this is something so simple to see that the guides don't bother to explain about it or is something more complex that it looks like.
What is the best process to display form errors after submission in a outside view in rails 4.2?
Also, as an aside note. What is the form workflow between different views and controllers?
Thanks in advance.
In your create action you can do:
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.build(params[:comment])
#comment.user = current_user
if #comment.save
redirect_to article_path(#article)
else
render 'new', locals: { errors: #comment.errors.full_messages }
end
end
The idea is that in your create action you try to save a new record. If everything is fine, the user will be redirected to the #show action of the article. Otherwise, Rails will render again the 'new' template. This time errors will be passed to your view. So you should add in your view validation for errors object and iterate over them (it's array of Strings) to display all messages in case that there are error messages.

Please explain form_for method in Rails

This isn't really a troubleshooting question but rather a request for an explanation. I'm having a hard time understanding the workings of the form_for method. Could someone explain to me what this method does in this situation. Here is my code for creating a form for the comments feature on a blog application. My code works, so i just want to understand WHY it works and How it works. Thanks!!
Here is my new comment form:
<%= form_for([#post, #post.comments.build]) do |c| %>
<p>
<%= c.label :content, class: "col-md control-label" %><br>
<%= c.text_area :content, rows: "10", class: "form-control" %>
</p>
<p>
<%= c.submit %>
</p>
<% end %>
And here is my code for the comments Controller:
class CommentsController < ApplicationController
def new
#post = Post.find(params[:post_id])
end
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
#comment.user_id = current_user.id
#comment.save
#redirect_to post_path(#post)
redirect_to posts_path
end
private
def comment_params
params.require(:comment).permit(:content)
end
end
In particular, what does the "[#post, #post.comments.build]" parameter of form_for do?
First off, there's nothing you can do with form_for that you couldn't do with form_tag (and some extra typing).
What form_for allows you to do is easily create forms that fit with the rails conventions in terms of urls & parameter naming.
The first argument to form_for is the resource that is being edited or created. At it's simplest this might be just #post. The array form is for namespaces or nested resources.
Your example of [#post, #post.comments.build] means that this is a form for a new comment (the last element of the array is an unsaved instance of Comment) that is nested under that specific post. This will result in the form doing a POST request to /posts/1234/comments (assuming the post has an id of 1234). The corresponding nested route needs to exist for this to work.
The second thing form_for does for you is allow you to write c.text_area :content and have that automatically use the correct parameter name (comment[content]) and have the value prefilled with the current value of the comment's content attribute.
The form_for will do a post to a specific resource and help to draw the inputs.
Example 1
form_for(#post) will do a post to myapp/posts/create and draw the posts fields
Example 2
form_for([#post, #post.comments.build]) will do a post to myapp/posts/:post_id/comments/create and draw the comments fields
here [#post, #post.comments.build] means this form is for a new comment, and form will do a POST request to /posts/post_id/comments (post_id is a #post.id)

Rails: what is the “correct” (idiomatic) way to pass a parameter from new to create in a RESTful controller?

I have a very common situation and a solution, but I would like to ask the Rails experts out there if it can be improved.
I have a very typical RESTful controller where the user supplies some of the object attributes upon creation. There is a thing model, a ThingsController and various views, including new, create, and a _form partial.
A thing has two attributes,
a color, which is set when they hit a link to create it (i.e. a “Create Red Thing” link that encodes the ID of red as a URL parameter)
a description, which is entered by the user in the form view
I’m happy with the approach for dealing with an attribute like the description that that a user specifies in the form, but less confident about the way that I handle an attribute that is passed through via the URL parameters associated with the first click.
This is what I am doing right now (note that I have omitted error checking to simplify matters). First, my new and create methods in the controller are as follows:
def new
#thing = Thing.new
#thing.color = Color. find(params[:color])
end
def create
#thing = Thing.new(params[:thing])
#thing.color = Color. find(params[:color])
if #thing.save
flash[:notice] = "Successfully created thing."
redirect_to somewhere_url
else
render :action => 'new'
end
end
The new view just invokes the _form partial, which looks as follows:
<% form_for #thing do |f| %>
<%= f.error_messages %>
<%= hidden_field_tag "color", #thing.color.id %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.submit "Submit" %>
<% end %>
It seems a little messy to be passing the color ID through to the create method as a URL parameter by putting a hidden field in the form. Does this look reasonable, or is there another approach that would be better?
Normally in this situation, I would put a hidden field in the form, which will hold the color_id. The nice thing about this way is that you can get by with just setting the color of the object before you render the form. So your controller changes to:
def new
#thing = Thing.new
#thing.color = Color. find(params[:color])
end
def create
#thing = Thing.new(params[:thing])
if #thing.save
flash[:notice] = "Successfully created thing."
redirect_to somewhere_url
else
render :action => 'new'
end
end
and your form will change to
<% form_for #thing do |f| %>
<%= f.error_messages %>
<%= f.hidden_field :color_id %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.submit "Submit" %>
<% end %>
The color is then passed through both forms, and you only need to retrieve the color in the first form. (Don't forget to add validations your Thing model to make sure it has a valid color though).

Resources