Error handeling w/ form_for in associated resource - ruby-on-rails

I can't seem to get the flow to work right here. I have a Ruby on Rails (2.3.9) application. For the purposes of this question we have only a couple of resources. Boxes and Messages.
Box has_many :messages
Message belongs_to :box
I have created a view located at /boxes/1/new_message where I have the below form_for code. I can successfully create a message from this view. The problem arrises when my validations kick in.
In this case, message.body can't be blank and is validated by message.rb. Once this validation happens, it kicks the user over to the Message.new action and upon successfully filling in the message.body the app can no longer find the #box.id to place in message.box_id.
I have tried just about everything I can think of by not sure how to allow a users to receive a validation and still successfully create a message for a box. See my code below for reference.
/views/boxes/new_message.html.erb
<% form_for [#box, Message.new] do |f| %>
<%= f.error_messages %>
<%= f.label :message_title %>
<%= f.text_field (:title, :class => "textfield-message grid_12 alpha") %>
<%= f.label :message_body %>
<%= f.text_area (:body, :class => "textarea-message grid_12 alpha ") %>
<%= f.submit "Add a Message", :class => 'input boxy' %>
<% end %>
messages_controller.rb
def create
#message = Message.new(params[:message])
#box = Box.find(params[:box_id])
#message = #box.messages.build(params[:message])
#message.user = current_user
respond_to do |format|
if #message.save
flash[:notice] = 'Message was successfully created.'
format.html {redirect_to #box }
else
format.html { render :action => "new" }
format.xml { render :xml => #flash.errors, :status => :unprocessable_entity }
end
end
end
def new
#message = Message.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #message }
end
end

I believe your
#box = Box.find(params[:box_id])
should be
#box = Box.find(params[:id])

Related

Ruby on Rails Form Submission Error and Success Messages

I'm creating a landing page and I have two forms on my root page (trying to create a landing page). Very new to ruby on rails so forgive me because I'm going to explain this terribly.
The landing page controller (landing_controller) looks like this:
class LandingController < ApplicationController
def index
#email = Email.new
#design = Design.new
end
end
The emails_controller (emails_controller) looks like this:
class EmailsController < ApplicationController
protect_from_forgery with: :exception
def new
#email = Email.new
end
def create
#email = Email.new(params[email_params])
respond_to do |format|
if #email.save
format.html { redirect_to(root_path, :notice => 'Thank You For Subscribing!') }
format.json { render json: Email.create(email_params) }
else
format.html { redirect_to(root_path)}
format.json { render :json => #email.errors, :status => :unprocessable_entity }
end
end
end
private
def email_params
params.require(:email).permit(:username, :email)
end
end
and the designs controller (designs_controller) looks pretty much the same as emails_controller.
Then I have some validation in the email.rb model:
class Email < ActiveRecord::Base
validates :username, :presence => true
validates :email, :presence => true
end
And again the design.rb looks pretty much the same.
The form I have on the landing page (root_path) index looks like this:
<%= form_for #email, url: emails_path, html: {class: "email-form"} do |f| %>
<% if #email.errors.any? %>
<h3>Error</h3>
<ul>
<% #email.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
<h2>Receive Notifications</h2>
<%= f.text_field :username, :class => 'email-box', :placeholder => "First Name", :autocomplete => :off %>
<%= f.text_field :email , :class => 'email-box', :placeholder => "Email", :autocomplete => :off %>
<%= f.submit "Subscribe", :class => 'email-submit' %>
<p class="info">- We'll update ​you ​when ​we launch our new website</p>
<% end %>
When I submit the form breaking the validation I get no errors and if I submit the form following the validation rules I don't know if it creates a new entry in the database. If anyone can help I'd be very appreciative.
you need to render the landing controller index action rather than redirecting to it. because on redirection, it does #email = Email.new and all the errors are gone for email. try this as create action in your emails controller
def create
#email = Email.new(email_params)
respond_to do |format|
if #email.save
format.html { redirect_to root_path, notice: 'Thank You For Subscribing!' }
format.json { render json: Email.create(email_params) }
else
#design = Design.new
format.html { render "landing/index" }
format.json { render :json => #email.errors, :status => :unprocessable_entity }
end
end
end
for success or errors messages, put this in your application.html.erb
<% if flash[:error].present? %>
<p class='flash-error'><%= flash[:error] %></p>
<% end %>
<% if flash[:notice].present? %>
<p class='flash-notice'><%= flash[:notice] %></p>
<% end %>

get value from form_for

I'm using rails 4.0.1
<%= form_for #event, :html => { :multipart => true} do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %><br>
<%= f.text_area :content %>
</div>
<div class="field">
<%= f.label :place_id %><br>
<%= f.collection_select(:place_id, #places, :id, :title) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And I want to check for current_user.id and Place.user_id (it stores creator id). In Events cotroller i'm trying to use:
def create
#places = Place.all
#event = Event.new(event_params)
#event.user_id = current_user.id
#curplace = Place.find_by(id: params[:place_id])
#event.content = #curplace.id
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
But i got an error. I think i'm not getting this Place_id param right or anything else?
Further to the comment from Ankush Kataria, the form_for helper basically creates a form which combines all the params into a hash, as opposed to form_tag, which just makes the params independently
As you've discovered, this means your params will be accessed by:
#form_for
params[:variable][:param]
#form_tag
params[:param]
form_for
The reason why this is important is because if you're using the RESTful routes interface, you'll be able to create / edit / update a variety of records
form_for basically keeps consistency throughout this process, pre-populating your forms with the various values, and keeping your code DRY
To call a form_for helper, you have to define the #varaible the form will populate. This #variable needs to be an ActiveRecord object, and is why you have to build it in the new action before your form shows
form_tag
form_tag is much more independent of the form_for helper, doesn't require any #variable, and creates the params individually
You'd use a form_tag for the likes of a contact us form or similar
Your Code
Your form looks good, but your create action can be dried up:
def create
#places = Place.all
#event = Event.new(event_params)
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
private
def event_params
params.require(:event).permit(:title, :content).merge(user_id: current_user.id, place_id: params[:event][:place_id])
end
You are right that params[:place_id] isn't returning the value you expect. It only returns nil. To get the :place_id that's submitted by the form, you have to do this:
#curplace = Place.find(params[:event][:place_id])
Just replace the old line with the code above. It's because your form submits the data in the fields inside an :event key in the params hash since you're using the form_for helper method provided by Rails. That is its default behavior unless you change the 'name' attribute's value in the input fields.
Hope that helps!

NoMethodError in Products#create

I am totally new to programming and I am having trouble. About 10 days ago I started the UT-Rails course on ureddit.com hosted by Richard Schneeman. So far it has been going very well, but I am on week 5 and having trouble. You'll have to excuse me if I don't use the right terminology, as it has been a lot to take in.
https://github.com/zkay/move_logic_to_controllers is the tutorial I am following at the moment.
I am up to step 2. I have replaced the text in app/views/products/new.html.erb with the following:
<%= form_for(#product) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :price %><br />
<%= f.text_field :price %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
However, when I try to add a new product per the tutorial, the rejection I get back is:
NoMethodError in Products#create
Showing C:/Sites/move_logic_to_controllers/app/views/products/create.html.erb where line #3 raised:
undefined method `name' for nil:NilClass
Extracted source (around line #3):
1: <h2>Product Created Successfully<h2>
2:
3: <%= #product.name %> added to the website, it costs: $<%= #product.price %>
Rails.root: C:/Sites/move_logic_to_controllers
If I remove the .name and .price calls the page works, but it doesn't display any of the data I submitted.
In app/controllers/product_controller.rb I have the following:
class ProductsController < ApplicationController
def index
#products = Product.includes(:user).all
end
def new
#product = Product.new
end
respond_to do |format|
if #product.save
format.html { render :action => "create" }
format.json { render :json => #product }
else
format.html { render :action => "new" }
format.json { render :json => #product.errors, :status => :unprocessable_entity }
end
end
end
Sorry if this is long winded. I appreciate any help.
it should be <%= #products.name %>
/app/views/products/create.html.erb
You don't want to use create.html.erb.
class ProductsController < ApplicationController
def index
#products = Product.includes(:user).all
end
def new
#product = Product.new
end
def create
#product = Product.new(params[:product])
if #product.save
redirect_to products_path, notice: "You added product"
else
flash[:error] = "Something wrong!"
render :new
end
end
end
If you use Rails 4 use:
def create
#product = Product.new(product_params)
if #product.save
redirect_to products_path, notice: "You added product"
else
flash[:error] = "Something wrong!"
render :new
end
end
private
def product_params
params.require(:product).permit(:name, :price)
end

Rails does not display error messages on a form in a custom method

I've created a custom method called checkout in my app. I create an order (which is done my adding products to my "cart"), assign it to my client, and then I head to my checkout screen where I confirm the items and enter their customer order number and complete the order (submit).
Everything works great except that it doesn't display error messages. I'm able to display a flash error notice (seen in complete_order method) when things go wrong but it doesn't specify the details like a normal form would. The error messages should appear if the customer order number is not unique for that client.
Below is the custom method (checkout) related code.
Order Model:
validates_uniqueness_of :customer_order_number, :scope => :client_id
Orders_controller:
def checkout
#order = current_order
end
def complete_order
#order = current_order
respond_to do |format|
if #order.update_attributes(params[:order])
#order.complete #sets submitted datetime and state to 'complete'
flash[:notice] = 'Thank you! Your order is being processed.'
format.html { redirect_to( products_path ) }
format.xml { head :ok }
else
flash[:error] = 'Please review your items' #added to confirm an error is present
format.html { redirect_to( checkout_path ) }
format.xml { render :xml => #order.errors, :status => :unprocessable_entity }
end
end
end
And the form in the checkout view:
<% form_for #order, :url => { :controller => "orders", :action => "complete_order" } do |f| %>
<%= f.error_messages %>
<%= f.text_field :customer_order_number, :label => "Purchase Order Number" %>
<p>
<%= f.submit 'Complete Order', :confirm => 'Are you sure?' %> <small> or <%= link_to 'cancel', current_cart_path %></small>
</p>
<% end %>
Any idea how I can display the specific error messages?
Change redirect_to to render in else condition otherwise checkout method get called again & no error will displayed.
else
format.html { render :action => 'checkout' }

validates_presence_of not working properly...how to debug?

In my Review model, I have the following:
class Review < ActiveRecord::Base
belongs_to :vendor
belongs_to :user
has_many :votes
validates_presence_of :summary
end
I submit a new entry as follows in the URL:
vendors/9/reviews/new
The new.html.erb contains a form as follows:
<%= error_messages_for 'review' %>
<h1>New review for <%= link_to #vendor.name, #vendor%></h1>
<% form_for(#review, :url =>vendor_reviews_path(#vendor.id)) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :summary %><br />
<%= f.text_area :summary, :rows=>'3', :class=>'input_summary' %>
<%= f.hidden_field :vendor_id, :value => #vendor.id %>
</p>
<p>
<%= f.submit 'Submit Review' %>
</p>
<% end %>
When I leave the field for :summary blank, I get an error, not a validation message:
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.name
Extracted source (around line #3):
1: <%= error_messages_for 'review' %>
2:
3: <h1>New review for <%= link_to #vendor.name, #vendor%></h1>
I don't understand what is happening, it works if :summary is populated
def new
#review = Review.new
#vendor = Vendor.find(params[:vendor_id])
#review = #vendor.reviews.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #review }
end
end
def create
#review = Review.new(params[:review])
##vendor = Vendor.find(params[:vendor_id]) #instantiate the vendor from the URL id -- NOT WOKRING
##review = #vendor.reviews.build #build a review with vendor_id -- NOT working
#review = #current_user.reviews.build params[:review]#build a review with the current_user id
respond_to do |format|
if #review.save
flash[:notice] = 'Review was successfully created.'
format.html { redirect_to review_path(#review) }
format.xml { render :xml => #review, :status => :created, :location => #review }
else
format.html { redirect_to new_review_path(#review) }
format.xml { render :xml => #review.errors, :status => :unprocessable_entity }
end
end
end
My guess is that when it fails it is going to redirect_to new_review_path(#review) and so doesn't know the vendor it. How can I redirect to vendor/:vendor_id/reviews/new instead?
You probably don't have #vendor member variable set - but to fix this, it would be more correct to use not #vendor directly, but through your #review variable instance.
If you are creating new review, you already have #review member variable created, and you simply are populating fields in it - so, you need to set the vendor for #review (unless it's optional)... it would be more correct to use #review.vendor.name instead.
(If vendor is optional, then you obviously must catch all vendor.nil? cases.)
What code do you have in the new and create actions in your ReviewsController?
I suspect that your new Review is failing validation because the summary field is blank and then when the form is redisplayed on validation failure, the #vendor instance variable is nil.
You need to make sure that #vendor is assigned a value for both code paths.
I think you need to render :action => 'new' instead of your redirect_to new_review_path(#review). This will keep your error_messages on the #review object. By redirecting you are losing the old object and creating a new one.
As others has said, you also need to make sure you re-populate the #vender variable in your create method before rendering the view.
PS. I like to use the ardes resources_controller plugin for bog standard controller actions like these, makes life a lot easier for me and it handles nested resources really well.

Resources