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.
Related
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 %>
I'm trying to set up a one to many relationship between one model to another in rails, and it has worked in the console, but I can't seem to implement it properly in views so that I am able to create more pictures for a single event (more explained below)
The background of the web application is, a single event has many event pictures, and many event pictures belong to a single event. I'm trying to set this up properly within the console and schematics, it seems to have worked. I was unable to get it working when I tried implementing it with views.
<%= form_with(model: [ #event, #event.event_pictures.build ], local: true) do |form| %>
<p>
<%= form.label :answer %><br>
<%= form.text_field :answer %>
</p>
<p>
<%= form.label :hint %><br>
<%= form.text_field :hint %>
</p>
<p>
<%= form.label :event_pics %>
<%= form.file_field :event_pics, multiple: true %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
The code above is my form which is the main cause of the issue. The error is shown below.
undefined method `event_pictures' for nil:NilClass
Console stuff works.. in Pastebin (https://pastebin.com/ESYAfMzE)
Controller
def new
render 'new'
end
def create
#event = Event.find(params[:event_id])
#event_picture = #event.event_pictures.create(event_picture_params)
redirect_to event_path(#event_picture)
end
**Button to create new picture, in Event Index, so you can see the event, before clicking on a button for a new picture. **
<%= button_to "new picture", {:controller => :event_picture, :action => 'new', :event_id => event.id},
:method => :get,
class: "btn btn-warning" %>
This is fairly frustrating as I've also looked into the possibility of using fields_for instead of form_with, and checked my schema.rb and migrations a number of times to make sure it is properly linked. At this point, I'd really appreciate any help at all as I've gone hours looking at this problem.
Thank you very much.
I see, in your new action, you need to setup the #event instance variable:
def new
#event = Event.find(params[:event_id])
end
I have a simple setup:
class EventsController < ApplicationController
def edit
#user = User.find(params[:user_id])
#event = Event.find(params[:id])
end
end
events\edit.html.erb:
<h1>Edit <%= #user.name %>'s event</h1>
<%= render 'form' %>
events\_form.html.erb:
<%= form_for [#user, #event] do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.submit %>
<% end %>
To my biggest surprise this code is working and I am not getting any errors. Form partial knows about #user and #event!
I always thought I have to pass parameters as locals in order to access them in the partial, so the render from the view have to be:
<%= render 'form', user: #user, event: #event %>
And form_for line should be changed to:
<%= form_for [user, event] do |f| %>
Am I missing something here? Is it one of those days when I confused my self so much that I should probably get some sleep?
I am running this on Rails 4.1.4 and events are nested resources of user if that changes anything.
Your parameter is an instance variable. As such it is available to any partials rendered in the view.
You can check out more about rendering on the rails guides: http://guides.rubyonrails.org/getting_started.html#rendering-a-partial-form
It's good practice to pass in variables to partials as locals, as its easier to reuse the partial in other actions:
http://guides.rubyonrails.org/layouts_and_rendering.html#passing-local-variables
Also if you try to access a local variable you didn't pass into the partial, your view with explode, while an instance variable will just be nil. Having the view explode is, in my opinion, easier to debug.
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).
I am creating a very simple book review site and it needs the ability to allow the user to add little comments about a book. Now I have my two tables, one for the book and one for the comments and now need a way to transfer data between the two because i find the way rails handles things quite puzzling.
So my book model contains "has_many :comments"
and me comment model has: "belongs_to :book"
the view i am using to both view and add comments is "/views/book/viewbook.html.erb"
this shows the book and all its details, at the bottom is a section where the user can add their own comments, it looks like this:
<%= form.text_field :title %>
<%= form.text_area :body %>
<%= submit_tag "Add Comment", :class => "submit" %>
now i know this cannot work because that above ":title" and ":body" would be in the book model but i need them to be send to the comment model because these are in the comment DB. How do i pass this data to the comment database. I've tried ":comment.title" and other various things but still cannot workout how to pass this data.
Any thoughts would be greatly appreciated.
(I apologize if this question is very stupid or has not been explained to well, my lecturer set this assignment and rails is not a language i have ever used.)
You define what the form is for in the opening form tag:
<% form_for :comment do |form| %>
<%= form.text_field :title %>
<%= form.text_area :body %>
<%= submit_tag "Add Comment", :class => "submit" %>
<%= end %>
The idea is that the form is an empty comment object. Controllers communicate between models and views, so your controller should have an action to process the form that knows to save the comment object into the comment model. You'll also want to specify which book the comment is for. There are a lot of ways to handle this (hidden fields, nested RESTful resources, etc.).
Maybe start here: http://guides.rubyonrails.org/action_controller_overview.html
It sounds like you need to use nested object forms. This is a new feature in Rails 2.3:
http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes
So i have modified it using your great suggestions and now only one problem has come-up. Whilst i can now successfully store the comments title, body, time and the person who left the comment to the DB i still cant work out how to use store the book.id. Here is the view:
<% form_for :comment, :url => { :action => "addcomment" } do |form| %>
<%= form.hidden_field :user_id, :value => session[:user_id] %>
<%= form.hidden_field :book_id, :value => #book.id %> <!-- WONT WORK -->
<%= form.label "Title" %><%= form.text_field :title %><br />
<%= form.label "Comment" %><%= form.text_area :comment %>
<%= submit_tag "addComment", :class => "submit" %>
<% end %>
Here is my controller that can now successfully store the details, apart from the book.id
def addcomment
#newcomment = Comment.new(params[:comment])
if #newcomment.save
flash[:notice] = "Comment Was Added To The System."
redirect_to :action => "index"
end
end
i though that "#book.id" would work because in that view i am also showing the books details using things like "#book.title" and "#book.authour" and that seems to work, but not for the ID field though.
So can now successfully post comments and store them with the correct details, now im on to displaying a list of comments of that particular book using the "book_id" value. Here is what i thought would work, i also have code like this in the search part of my app so i thought it would well:
def view
#book = Book.find(params[:id])
#reviews = Comment.find_by_book_id(#book.id)
end
With the corresponding view:
<% if #reviews %>
<% for review in #reviews %>
<%= form.label "Title: " %><%h review.title %> <br />
<%= form.label "Review:" %><%h review.comment %>
<% end %>
<% end %>
Now that should get the comments that have the "book_id" of the book i am viewing and the display each one using the for loop. Doesnt quite work though, it spits out an error message saying the following:
#undefined method `each' for #<Comment:0xb682c2f4>
#Extracted source (around line #27)
And line 27 is
<% for review in #reviews %>
To find the comments for a book it's just:
#book = Book.find(params[:id])
like you've already done, then:
#book.comments
for that books comments. So:
<% for review in #book.comments %
<%= form.label "Title: " %><%h review.title %> <br />
<%= form.label "Review:" %><%h review.comment %>
<% end %>
You don't have to find the comments in the controller.