My simple form is not doing a POST. I've been looking at this and haven't been able to see what is wrong (I'm sure it's in my routes). Here's what I have:
view:
views/buyers/new.html.erb
<%= form_for(#buyer) do |f| %>
<%= f.text_field :phone %><br />
<%= f.text_field :make %><br />
<%= f.text_field :model %><br />
<%= f.submit %>
<% end %>
controller:
BuyersController
def new
#title = "Welcome to Car Finder"
#buyer = Buyer.new
end
def create
#buyer = Buyer.new(params[:buyer])
if #buyer.save!
redirect_to success
else
redirect_to :back
end
end
routes:
resources :buyers
rake routes:
buyers GET /buyers(.:format) buyers#index
POST /buyers(.:format) buyers#create
new_buyer GET /buyers/new(.:format) buyers#new
edit_buyer GET /buyers/:id/edit(.:format) buyers#edit
buyer GET /buyers/:id(.:format) buyers#show
PUT /buyers/:id(.:format) buyers#update
DELETE /buyers/:id(.:format) buyers#destroy
When I submit the form, it stays on the same page, never going to the create action. Below is from the log
Started GET "/?..[params]..."
Processing by BuyersController#new as HTML
Thanks for any help you can give
It is probably wise to restart your server. You're issue may lie in validations you have at your persistence level or in your buyer.rb file. Add this to the _form.html.erb:
<% if #buyer.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#buyer.errors.count, "error") %> prohibited this from being saved: </h2>
<ul>
<% #buyer.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Try to complete the request again. See if any errors are being thrown. Fix those.
Related
I have Challenges containing Puns, and it is possible to vote on puns. On the Challenge Show page, all puns are rendered and show their votes count. This is currently on the view page:
<%= render #challenge.puns.reverse %>
<br>
<div id="form">
<%= render "puns/form" %>
</div>
I want the puns form to appear above the items (puns) already submitted. But if swap them around, like this:
<div id="form">
<%= render "puns/form" %>
</div>
<%= render #challenge.puns.reverse %>
I get a controller error and pun.id is not suddenly not available and the voting link breaks.
No route matches {:action=>"upvote", :challenge_id=>"9", :controller=>"puns", :id=>nil}, missing required keys: [:id]
Here is the puns/form part that is causing the issue
<% if signed_in? %>
<% if current_user.voted_for? pun %>
<%= pun.votes_for.size %>
<span class="pun_text"><%= link_to pun.pun_text, challenge_pun_path(#challenge, pun.id) %></span>
<% else %>
<%= link_to like_challenge_pun_path(#challenge, pun.id), method: :put do %>
<span class="heart_like">❤</span> <%= pun.votes_for.size %>
<% end %>
<span class="pun_text"><%= link_to pun.pun_text, challenge_pun_path(#challenge, pun.id) %></span>
<% end %>
<% end %>
It is the like_challenge_pun_path that throws an error but I cannot understand why. I am declaring #challenge again here, so it should be able to get the id.
Here is the form for the puns:
<%= form_for([#challenge, #challenge.puns.build]) do |f| %>
<span class=".emoji-picker-container">
<%= f.text_area :pun_text, placeholder: "Add pun", data: { emojiable: true } %>
</span>
<%= f.submit %>
<% end %>
Also, here is my routes setup
resources :challenges do
resources :puns do
member do
put "like", to: "puns#upvote"
put "dislike", to: "puns#downvote"
end
end
end
and the corresponding action to upvote
def upvote
#pun = #challenge.puns.find(params[:id])
#pun.upvote_by current_user
redirect_to #challenge
end
Can anyone help?
I think the code is for the puns collection.
I assume the issue is that in the form you have something like:
#challenge.puns.build
So in #challenge.puns collection appears not persisted record (without id), so path for this model cannot be generated.
As a quick solution I suggest:
<%= render #challenge.puns.reverse.select(&:persisted?) %>
UPDATE:
As I assumed you have
<%= form_for([#challenge, #challenge.puns.build]) do |f| %>
You can also try:
<%= form_for([#challenge, Pun.new]) do |f| %>
Or solve it in the controller. But need to see controller code for it.
I have added a custom action in my controller called transplant. I simply want to render a dropdown form to select where to be located based on the 'tray_id'
my routes look like this:
resources :plants do
member do
get 'transplant'
end
resources :plantdats, :plant_cycles, :tasks
end
My controller looks like this:
before_action :set_plant, only: [:show, :edit, :update, :destroy, :transplant]
def transplant
if #plant.update(plant_params)
redirect_to #plant
flash[:success] = "Transplanted successfully"
end
end
def set_plant
#plant = Plant.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def plant_params
params.require(:plant).permit(:title, :notes, :category_id, :tray_id, images_files: [])
end
Here is my button calling the action
<%= link_to 'TRANSPLANT', transplant_plant_path(#plant), class: "btn btn-raised btn-info hoverable" %>
Here is my transplant page _transplant.html.erb
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="jumbotron">
<%= form_for(#plant, html: {class: 'form-horizontal'}) do |f| %>
<% if #plant.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#plant.errors.count, "error") %> prohibited this grow from being saved:</h2>
<ul>
<% #plant.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label 'NAME' %>
<%= f.hidden_field :tray_id, value: params[:tray_id] %>
<% if params[:tray_id].nil? %>
<%= f.collection_select(:tray_id, Tray.all, :id, :title) %></br></br>
<% end %>
<%= f.submit class: 'btn btn-raised hoverable btn-success' %>
<% end %>
</div>
</div>
</div>
EDIT
After implementing the route to post 'transplant
and changing my link code to
<%= link_to "TRANSPLANT", transplant_plant_path(#plant, tray_id: #plant.tray_id), method: "post", class: "btn btn-raised hoverable" %>
I still get the same error. It points right to the plant_params code in my controller.
These are the params that are being passed:
{"_method"=>"post",
"authenticity_token"=>"fhSKt2DpgTwt1J4HsoBqYFSs0B9+pgSvDDxrS/u6yo4c3gvSxYlrrFDmhbPXq+cMho/eTHY+194WZ8zpcb1txA==",
"id"=>"1",
"format"=>"1"}
Im simply trying to update the :tray_id
Ive been at this all day, can anyone help with the error that Im getting?
You should probably provide your code for your transplant action and view. Based on what you provided, it seems like you're trying to build a link, which changes the tray of a plant when clicked. If that's the case, transplant should probably be a POST route instead of GET. Also, you probably want to provide the tray_id in your post link like this:
<%= link_to "TRANSPLANT", transplant_plant_path(#plant, tray_id: {{your id}}), method: "post", class: "..." %>
Then you can get tray_id in your transplant through params[:tray_id] and re-associate your plant and tray
Essentially what I was trying to do was not easily done and my approach needed to change. I simply rendered the transplant form in my view and it works fine now. Thanks again :)
Check your routes you just have defined get route and your request is post after the form submission
Two models, Organization and User, have a 1:many relationship. I have a combined signup form where an organization plus a user for that organization get signed up.
The problem I'm experiencing is: When submitting invalid information for the user, it renders the form again, as it should, but the error messages (such as "username can't be blank") for the user are not displayed. The form does work when valid information is submitted and it does display error messages for organization, just not for user.
How should I adjust the code below so that also the error messages for user get displayed?
def new
#organization = Organization.new
#user = #organization.users.build
end
def create
#organization = Organization.new(new_params.except(:users_attributes)) #Validations require the organization to be saved before user, since user requires an organization_id. That's why users_attributs are above excluded and why below it's managed in a transaction that rollbacks if either organization or user is invalid. This works as desired.
#organization.transaction do
if #organization.valid?
#organization.save
begin
# I executed next line in debugger (with invalid user info), which correctly responds with: ActiveRecord::RecordInvalid Exception: Validation failed: Email can't be blank, Email is invalid, Username can't be blank, etc.
#organization.users.create!(users_attributes)
rescue
# Should I perhaps add some line here that adds the users errors to the memory?
raise ActiveRecord::Rollback
end
end
end
if #organization.persisted?
flash[:success] = "Yeah!"
redirect_to root_url
else
#user = #organization.users.build(users_attributes) # Otherwise the filled in information for user is gone (fields for user are then empty)
render :new
end
end
The form view includes:
<%= form_for #organization, url: next_url do |f| %>
<%= render partial: 'shared/error_messages', locals: { object: f.object, nested_models: f.object.users } %>
<%= f.text_field :name %>
# Other fields
<%= f.fields_for :users do |p| %>
<%= p.email_field :email %>
# Other fields
<% end %>
<%= f.submit "Submit" %>
<% end %>
The error messages partial is as follows:
<% object.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
Update: Following the steps from Rob's answer I arrived at the errors partial below. This still does not display error messages for User. I added debugger responses inside the code below and for some reason nested_model.errors.any? returns false, while the debugger inside the controller (see above) does return error messages for user.
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
</ul>
</div>
<% end %>
<% if defined?(nested_models) && nested_models.any? %>
# Debugger: responds with "local-variable" for "defined?(nested_models)" and for "nested_models.any?" returns true.
<div id="error_explanation">
<ul>
<% nested_models.each do |nested_model| %>
# Debugger: "nested_model" has the same values as "nested_models.any?", as you would expect. But for "nested_model.errors.any?" it returns false, which it shouldn't.
<% if nested_model.errors.any? %> #Initially had "unless nested_model.valid?" but then errors for User are immediately displayed on loading the form page (new method).
<ul>
<% nested_model.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
</ul>
<% end %>
<% end %>
</ul>
</div>
<% end %>
Try adding validates_associated :users under your has_many :users association in Organization.
http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_associated
Did you code successfully create a person during the rescue block?
rescue ActiveRecord::RecordInvalid => exception
# do something with exception here
raise ActiveRecord::Rollback
#organization.users.build if #organization.users.blank?
render :new and return
This code looks like it will create a new empty User regardless of incorrect validations. And render new will simply return no errors because the user was successfully created, assuming Organization has no Users.
The control flow of this method has a few outcomes, definitely needs to be broken down some more. I would use byebug and walk through the block with an incorrect Organization, then incorrect name. Then an empty Organization with incorrect User attributes.
organization has_many :users and user belongs_to :organization
organization.rb
accepts_nested_attributes_for :users
new.html.erb
<%= form_for #organization, url: next_url do |f| %>
<%= render 'shared/error_messages', object: #organization %>
<%= f.text_field :name %>
# Other fields
<%= f.fields_for(:users,#organization.users.build) do |p| %>
<%= p.email_field :email %>
# Other fields
<% end %>
<%= f.submit "Submit" %>
<% end %>
In controller
def create
#organization = Organization.new(new_params)
if #organization.save
flash[:success] = "Yeah!"
redirect_to root_url
else
render :new
end
end
This is very related to this question. The key is that <%= render 'shared/error_messages', object: f.object %> is, I assume, only calling the .errors method on the object it is passed (in this case, organization).
However, because the user errors reside with the user object, they won't be returned and therefore will not be displayed. This requires simply changing the view logic to also display the results of .errors on the various user models. How you want to do so is up to you. In the linked thread, the accepted answer had the error message display code inline instead of in a partial, so you could do it that way, but it would be somewhat redundant.
I would modify my shared/error_messages.html.erb file to check for another passed local called something like nested_models. Then it would use that to search the associated models and include the errors on that. We just would need to check whether it is defined first so that your other views that don't have a nested model won't cause it to raise an error.
shared/error_messages.html.erb
<% if object.errors.any? %>
<div class="error-messages">
Object Errors:
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% if defined?(nested_models) && nested_models.any? %>
Nested Model(s) Errors:
<ul>
<% nested_models.each do |nested_model| %>
<% unless nested_model.valid? %>
<li>
<ul>
<% nested_model.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</li>
<% end %>
<% end %>
</ul>
<% end %>
</div>
<% end %>
Then you would just need to change a single line in your view:
<%= render partial: 'shared/error_messages', locals: { object: #organization, nested_models: #organization.users } %>
Looks like you have a lot of untestable logic in your controller. Looks like for you logic will be better to use simple FormObject pattern.
https://robots.thoughtbot.com/activemodel-form-objects
I'm creating a little newsletter application, with 'double opt-in restrictions', when I simply fill in my form (subscription page) and submit the form I get redirected to my subscribed page (which is all normal) however my form appends a querystring to my action attribute of my form (http://localhost:3000/newsletter/subscribe?format=)
routes:
match 'newsletter/subscription' => 'newsletter_subscriptions#subscription'
post 'newsletter/subscribe' => 'newsletter_subscriptions#subscribe'
controller:
class NewsletterSubscriptionsController < ApplicationController
respond_to :html
# GET /newsletter/subscription
def subscription
respond_with (#subscription = NewsletterSubscription.new)
end
# POST /newsletter/subscribe
def subscribe
# If there's already an unconfirmed record with the submitted email, use that object otherwise create a new one based on the submitted email
sub_new = NewsletterSubscription.new
sub_new.email = params[:newsletter_subscription]['email']
sub_old = NewsletterSubscription.find_by_email_and_confirmed sub_new.email, 0
#subscription = sub_old || sub_new
if #subscription.save
Newsletter.delay.subscribed(#subscription) # with delayed_job
else
render :action => "subscription"
end
end
...
end
view (newsletter_subscription/subscription.html.erb):
<h1>New newsletter_subscription</h1>
<%= form_for(#subscription, :url => newsletter_subscribe_path(#subscription)) do |f| %>
<% if #subscription.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#subscription.errors.count, "error") %> prohibited this newsletter_subscription from being
saved:</h2>
<ul>
<% #subscription.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<br/>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
PS: I would be pleased if someone could evaluate my ruby code please (posted above), I'm still learning a lot and would like to see some 'guidelines' or feedback, I think I still can learn a lot.
Try removing the #subscription argument you're passing into newsletter_subscribe_path. Since there isn't an :id in the route and it's a new object, passing it doesn't really make sense. I'm assuming that's what is being interpreted as the format.
<%= form_for(#subscription, :url => newsletter_subscribe_path) do |f| %>
As for improvements you can make to the code, the biggest thing I see is moving the old/new subscription logic into the model.
# in NewsletterSubscription
def self.with_email(email)
find_by_email_and_confirmed(email, 0) || new(:email => email)
end
# in controller
#subscription = NewsletterSubscription.with_email(params[:newsletter_subscription]['email'])
if #subscription.save
#...
Also respond_to and respond_with aren't really necessary here since you're just dealing with HTML views. You can remove that.
Rails 3.0 deprecated f.error_messages and now requires a plugin to work correctly - I however want to learn how to display error messages the (new) native way. I am following the getting started guide, which uses the deprecated method when implementing the comments form. For example:
<h2>Add a comment:</h2>
<%= form_for([#post, #post.comments.build]) do |f| %>
<%= f.error_messages %>
<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 %>
Here is the correct way to do it (as generated by the scaffold):
<%= form_for(#post) do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
. . .
I understand that I use the #post variable in the latter example, but what variable do I reference in the former to get the error messages for comment creation?
The best and clean way to implement error_messages in your form is by implementing error_messages in a FormBuilder.
For example, here is the error_messages method I've implemented for my last project.
By implemeting your own FormBuilder you can follow the rules and styles of your webdesigner...
Here is an example that will output the errors list in ul/li's with some custom styles :
class StandardBuilder < ActionView::Helpers::FormBuilder
def error_messages
return unless object.respond_to?(:errors) && object.errors.any?
errors_list = ""
errors_list << #template.content_tag(:span, "There are errors!", :class => "title-error")
errors_list << object.errors.full_messages.map { |message| #template.content_tag(:li, message) }.join("\n")
#template.content_tag(:ul, errors_list.html_safe, :class => "error-recap round-border")
end
end
Then in my forms :
= f.error_messages
And that's all.
I'm pretty sure all you'd need to do is reference #post.comments
So you could do something like:
<% #post.comments.each do |comment| %>
<% if comment.errors.any? %>
<% comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
<% end %>
<% end %>
Or just pull all the errors out out:
comment_errors = #post.comments.map(&:errors)
and then loop through them in your display logic to output each of the comment errors.
This functionality exists as a standalone gem dynamic_form.
Add the the following to your Gemfile
gem 'dynamic_form'
From the github page:
DynamicForm holds a few helpers method to help you deal with your Rails3 models, they are:
input(record, method, options = {})
form(record, options = {})
error_message_on(object, method, options={})
error_messages_for(record, options={})
It also adds f.error_messages and f.error_message_on to your form builders.
Here is my solution to the whole error scene.
I created a partial which simply uses a model variable which one would pass when rendering it:
<%# app/views/errors/_error.html.erb %>
<%= content_for :message do %>
<% if model.errors.any? %>
<ul>
<% model.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<% end %>
You can easily add dynamic html class and/or id names based on the name of the model, as well as generic ones.
I have things setup where my error messages render in all the same place in a layout file:
<%# app/views/layouts/application.html.erb %>
<%= yield :message %>
If one didn't want that functionality, removing the content_for in the partial would do the trick.
Then in really any view you want you can simply write:
<%= render 'errors/error', model: #some_model %>
One could further expand this by creating a partial which takes a collection and leverages the error partial above:
<%# app/views/errors/_collection.html.erb %>
<% collection.each do |model| %>
<%= render 'errors/error', model: model %>
<% end %>
Render it with:
<%= render 'errors/collection', collection: #some_model.some_has_many_association %>
I like this way. It is simple, easy to manage/maintain, and incredibly tweakable.
I hope this helps!
EDIT: Everything in HAML
-# app/views/errors/_error.html.haml
= content_for :message do
- if model.errors.any?
%ul
- model.errors.full_messages.each do |msg|
%li= msg
-# app/views/layouts/application.html.haml
= yield :message
= render 'errors/error', model: #some_model
-# app/views/errors/_collection.html.haml
- collection.each do |model|
= render 'errors/errors', model: #some_model
= render 'errors/_collection', collection: #some_model.some_has_many_association
I guess that the [#post, #post.comments.build] array is just passed to polymorphic_path inside form_for. This generates a sub-resource path for comments (like /posts/1/comments in this case). So it looks like your first example uses comments as sub-resources to posts, right?.
So actually the controller that will be called here is the CommentsController. The reason why Lukas' solution doesn't work for you might be that you actually don't use #post.comments.build inside the controller when creating the comment (it doesn't matter that you use it in the view when calling form_for). The CommentsController#create method should look like this (more or less):
def create
#post = Post.find(params[:post_id]
#comment = #post.comments.build(params[:comment])
if(#comment.save)
# you would probably redirect to #post
else
# you would probably render post#show or wherever you have the form
end
end
Then you can use the code generated by scaffolding, only replace #post instance variable with #comment in all the lines except form_for call.
I think it may also be a good idea to add the #comment = #post.comment.build to the controller method that displays this form and use form_for([#post, #comment], ...) to keep the form contents displayed in the form if there're errors.
If this doesn't work and you're not able to figure it out, please add your CommentsController#create method to the question.
I just looked into the docrails github issues, and they've decided to remove f.error_messages instead of explaining how to do validation for comments.
a rather simple implementation can be achieved with
class ActionView::Helpers::FormBuilder
def error_message(method)
return unless object.respond_to?(:errors) && object.errors.any?
#template.content_tag(:div, object.errors.full_messages_for(:"#{method}").first, class: 'error')
end
end
which allows one to use
<%= form.label :first_name %>
<%= form.text_field :first_name %>
<%= form.error_message :first_name %>
and with the following sass
#import variables
.error
padding: $margin-s
margin-left: $size-xl
color: red
.field_with_errors
input
border: 1px red solid
input:focus
outline-color: red
it looks like
using simple form gives you quite similiar functionality with more advanced functionality.
For example check out their examples with bootstrap