Why does my form_for :blog point to /blogs/new? - ruby-on-rails

I was going through instance-variable-vs-symbol-in-ruby-on-rails-form-for SO post.
As per the most voted answer
if you use symbol :post it creates
<form action="/posts" method="post">
if you use the instance #post
for #post = Post.new you will get
<form action="/posts/create" class="new_account" id="new_account" method="post">
But when I look at my rendered html page
<form action="/blogs/new" accept-charset="UTF-8" method="post">
<input name="utf8" type="hidden" value="✓" />
<input type="hidden" name="authenticity_token"
......
for the
new.html.erb
<%=form_for :blog do |f|%>
<%= f.text_field :title%>
<%= f.text_field :content%>
<%= f.submit :button %>
<% end %>
this throws me error saying
Routing Error
No route matches [POST] "/blogs/new"
Why is path mismatch occuring?

action url of form_for :blog will be the url render that form
action url of form_for "blog based on paths of that object (defined in routes) combine with state of that object
http://apidock.com/rails/ActionView/Helpers/FormHelper/form_for
When the model is
represented by a string or symbol, as in the example above, if the
:url option is not specified, by default the form will be sent back to
the current url

The answer you have linked is relatively old and I am fairly sure not accurate anymore.
The Rails way to do it is using instance variable.
You should then use <%= form_for #Blog.new do |f| =>. This should render as
<form action="/blogs" method="post">

For a resource, your form should be general for both show.html.erb and new.html.erb. In this instance, you should break your form into a partial _form.html.erb, and replace :blog with an instance variable:
_form.html.erb
<%=form_for #blog do |f|%>
<%= f.text_field :title%>
<%= f.text_field :content%>
<%= f.submit :button %>
<% end %>
Your controller actions should create this instance variable:
BlogsController
def new
#blog = Blog.new
end
def update
#blog = Blog.find(params[:id])
end
And, finally, your templates should just call the partial
new.html.erb
render partial: :form
update.html.erb
render partial: :form

Related

in rails difference between form_for #article and form_for :article

im new to ruby on rails, abit confused between the usage of the following when i try to update a record:
<%= form_for #article, url:{action: "update"} do |form| %>
this one works, but i dont understand how come the submit button says 'update article'
<%= form_for :article, url:{action: "update"} do |form| %>
this one throws No route matches [POST] "/articles/2", and the submit button says 'save article'
finally:
<%= form_with(model: #article) do |form| %>
actually updates the record, but i dont understand why it's calling update, and not other methods
form_for(#article) creates a form builder which is bound to a model instance.
If #article is nil it will raise an error.
If the instance is a new record the form will use method="POST" and action="/arcticles".
If the record has been persisted it will have method="PATCH" and action="/arcticles/:article_id".
Rails derives the URL for the action attribute based on convention over configuration. So there is no need to explicitly pass the url option if you follow the conventions.
An example of this would be:
<% #article = Article.new(title: 'Hello World') %>
<%= form_for(#article) do |f| %>
<%= f.text_input :title %>
<% end %>
This will render something like:
<form action="/articles" method="POST">
<input type="text" name="article[title]" value="Hello World"/>
...
</form>
<%= form_for #article, url:{action: "update"} do |form| %> this one
works, but i dont understand how come the submit button says 'update
article'
The form builder knows it is updating an record by calling .new_record? on the the record you passed to form_with. You can change the default value of the submit button by providing translations:
# config/locales/en.yml
en:
helpers:
submit:
create: "Save new record"
update: "Save changes"
form_for(:article) creates a scoped form builder that does not wrap an object.
This creates a form builder where the inputs will be "scoped". For example:
<%= form_for(:article) do |f| %>
<%= f.text_input :title %>
<% end %>
This will render something like:
<form action="/articles" method="POST">
<input type="text" name="article[title]"/>
...
</form>
Rails derives the URL for the action attribute based on convention over configuration.
In your case <%= form_for :article, url:{action: "update"} do |form| %> causes a routing error since form_for defaults to method: "POST".
form_with is the Rails 5.1 replacement for form_for and form_tag
form_with will replace the form_for and form_tag methods which are closely related yet have very different signatures. form_for and form_tag have been soft depreciated and are slated for removal.
The idea is to provide a single method with a more consistent signature.
If you are using Rails 5.1+ this is what you should be using.
See:
Rails Guides - Action View Form Helpers
Rails API - ActionView::Helpers::FormHelper
Rails 5.1's form_with vs. form_tag vs. form_for
It all depends on #artical. If #artical is new object (id in #artical is nil) is call the create action. If #artical is existing object then it called the update method.

"No route matches [POST]" despite Resources in my Routes file

I am creating a Rails app, and I need a form to function in one of my views and submit data to a table without the use of a scaffold (like I usually do).
Now, the place where this comment form is going to appear is in one view within the blog folder. It will need to allow the user to put in their comment, save it to the table, and then return to the same page.
While this is a pretty commonplace error, I am confused because I am specifying two things that seem critical: creating resources in my routes file for the form, and second, using a create method in my controller.
In the blog.html.erb, this happens in this form:
<%= form_for :cements do |f| %>
<div class="form-group">
<div class="field">
<%= f.label :post %><br>
<%= f.text_area :post, class: "form-control" %>
</div>
</div>
<h5 id="username">Username</h5>
<div class="form-group">
<div class="field">
<%= f.text_field :username, class: "form-control" %>
</div>
</div>
<%= f.hidden_field :slug, :id => "hiddenPicker"%>
<div class="actions">
<%= f.submit "Save", class: "btn btn-success-outline" %>
</div>
<% end %>
Then, in my controller, I have a create method that should redirect back to the original page, as I wanted.
blogs_controller.rb
class BlogsController < ActionController::Base
def index
#posts = Post.order('updated_at DESC').all
#comments = Cement.all
end
def blog
#posts = Post.where(slug: params[:id]).all
#comments = Cement.all
end
def create
#cements= Cement.new(story_params)
#cements.save
redirect_to(:back)
end
private
def story_params
params.require(:cements).permit(:username, :post, :slug)
end
end
Good news: the comment form renders in the view. Bad news: when I submit, I am getting this error: No route matches [POST] "/blog".
My expectation is this will be an issue with my Routes file; however, I have a resources method already in there:
Rails.application.routes.draw do
resources :posts
resources :cements
resources :blogs
The naming convention is the same as my controller file, so I am confused why this error is happening. Any ideas?
:cement is not an object it is just a symbol, so how rails will determine where to POST form? If you inspect your form you will see form action as /blog (current page url).
You should either do
<%= form_for :cements, url: cements_path do |f| %>
or
<%= form_for Cement.new do |f| %>
Both of above will generate form action as /cements, which will submit to CementsController create action, But I see in your case you want to submit it to BlogsController so use the appropriate routes(blogs_path). You can use url in second version also.

form_for Ruby On Rails

I'm trying to follow a tutorial on using basic AJAX to add a record to a list in place, and I'm having issues using form form_for.
Here is my code.
<%= form_for ([#product, #product.new]) do |p| %>
<p>
<%= p.label :product_part %>
<%= p.text_field :product_part%>
</p>
<p>
<%= p.submit %>
</p>
<% end %>
The error I am getting is
undefined method `new' for nil:NilClass
I understand why I am getting the error (#products hasn't been "initialized") but I have no idea how to fix this issue (I am sure it's simple). I have seen something about putting a resource in the routes file, but I do not know for sure.
If you're trying to make a form for a new product, you should (in your controller) be setting #product to an instance of a new Product:
# app/controllers/products_controller.rb
def new
#product = Product.new
end
In your view, using [#product, #product.new] makes no sense. You can't invoke new on an instance of a product. It's very unclear why you're not simply using the following, which is the correct use of form_for with a new instance of an ActiveRecord model:
form_for #product do |p|
Do this:
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
def new
#product = Product.new
render :form
end
def edit
#product = Product.find params[:id]
render :form
end
end
#app/views/products/form.html.erb
<%= form_for #product, remote: true do |f| %>
<%= p.label :product_part %>
<%= p.text_field :product_part%>
<%= f.submit %>
<% end %>
This will do everything you need for both the new and edit methods (which you raised concerns about in your comments with #meagar).
This should be corroborated with the following routes (you can see why here):
#config/routes.rb
resources :products
I would say In case you need to look the form_for helper ; to understand the behavior of the method.
The method form_for It accept the argument as record, options = {}. The value of record could be a symbol object or a newly object of respective class in your case Person.new.
Second argument could be
:url, :namespace, :method, :authenticity_token, :remote , :enforce_utf8, :html
Among them :remote => true option is used as the Ajaxify your request.
form_for is a helper that assists with writing forms. form_for takes a :remote option. It works like this:
<%= form_for(#article, remote: true) do |f| %>
....
<% end %>
This will generate the following HTML:
<form accept-charset="UTF-8" action="/articles" class="new_article" data-remote="true" id="new_article" method="post">
...
</form>
Note the data-remote="true". Now, the form will be submitted by Ajax rather than by the browser's normal submit mechanism.
For more info about Form-For helper
Hope this solve your problem!!!.

form_for renders get rather than post, using rails

I have a long form that auto-fills with information from our database (based upon partial information supplied by a user). When the user verifies the information and re-submits the form, the information should be saved as the users. The form - qualifying_events - is a partial that's in the views / shared folder. The data that autofills it is rendered by the qualifying_events_controller as part of the create method. The partial is presented for verification in a views / users page that's rendered by the user controller. The initial, partial information input by a user - in a similar partial on the same page - is correctly saving. Here's the top of the form:
<form class="form-inline">
<%= form_for(#qualifying_event) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div >
<div class="form-group col-sm-12 text-center" style="margin-bottom: 5px;"><h5>
<div>
<li style="list-style-type: none; float: left; display: none; visibility: hidden;">
<%= current_user %>
</li>
</div>
<div>
<li style="list-style-type: none; float: left;">
<%= f.label :class_date %><br/>
<%= f.text_area :class_date, autofocus: true %>
</li>
</div>
Here is the error message:
No route matches [GET] "/qualifying_events"
Here's what I've tried:
1. Explicitly adding a route to the config / routes file although it already showed if I ran rake routes:
post 'qualifying_events_path' => 'qualifying_events#create'
2. Changing the form_for language to explicitly call 'post':
<%= form_for(#qualifying_event, url: qualifying_events_path, method: => post) do |f| %>
I still get the same error message. Since I have other forms that are saving to the database with the same code, I have to assume something changes when a form is populated from a database and one wants to re-save the information. I'm using the devise gem so I looked at the registration#edit code, hoping I could follow the format:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
Unfortunately, that approach didn't work either.
For 'completeness', here's the qualifying_events_controller code:
def create
#user = current_user
#qualifying_event = current_user.qualifying_events.build(qualifying_event_params)
if #qualifying_event.validated.nil?
# Match partial course information from current_user with records in table
# Get partial record's information
active_date = #qualifying_event.class_date
active_course = #qualifying_event.course_id
active_sponsor = #qualifying_event.sponsor_name
# Match on class_date, course_id, and sponsor_name
#qualifying_event = QualifyingEvent.new
#qualifying_event.assign_attributes(QualifyingEvent.
where("(class_date = :class_date AND course_id = :course_id)
OR (class_date = :class_date AND sponsor_name = :sponsor_name)
OR (course_id = :course_id AND sponsor_name = :sponsor_name)",
{class_date: active_date, course_id: active_course,
sponsor_name: active_sponsor}).first.attributes)
# render to confirmation form / form for completion
flash[:success] = "Qualifying Event saved for verification!"
respond_to do |format|
format.html {render 'users/user_dashboard' }
end
else
# if the record is complete
#qualifying_event.save
flash[:success] = "Qualifying Event created!"
redirect_to user_dashboard_path
end
There may be an error in the 'else' statement. I haven't yet added the code to verify that all the required information is present, etc.
I can save to and retrieve information from the database, however a re-submit of edited information calls 'get' rather than 'post.' What am I missing? Thank you in advance.
Remove the first <form> element and fix your form_for code from:
<%= form_for(#qualifying_event, url: qualifying_events_path, method: => post) do |f| %>
to:
<%= form_for(#qualifying_event, url: qualifying_events_path, method: :post, class: 'form-inline') do |f| %>
Remove <form class="form-inline"> (and the closing tag if any) before form_for. HTML forms can't be nested, thus only the outer form are processed which is have GET method.

simple_form delete method instead post method

In simple_form is possible to use the http delete verb instead the default post verb?
<%= simple_form_for #object , method: :delete do |f| %>
<%= f.input :instance_name, as: :check_boxes, collection: #roles %>
<%= f.button :submit %>
<% end %>
It doesn't works.
Unfortunately simply stating that it doesn't work is not helpful in understanding the problem you're seeing, but I'll make a guess based on my own initial confusion with the "method:" parameter. Most browsers don't support PUT and DELETE methods, so what simple_form_for does is generate a form with a POST method, but it also adds a hidden field to pass the actual method. So:
simple_form_for #service, url: service_path, method: :delete
generates:
<form action="/services/6" method="post">
<input name="_method" type="hidden" value="delete" />
....
Rails uses that to call the correct controller method. Hope that helps.

Resources