How to make create and update use the same route - ruby-on-rails

I have a route and form that looks like this:
<%= form_for #address, url: {action: "update_contact", controller: "checkouts"}, html: {class: ""} do |f| %>
My route looks like:
post "checkouts/:cart_token/update_contact" => "checkouts#update_contact", as: "checkouts_update_contact"
For updates the form is looking for a PATCH which I haven't defined, and so I get an error when the #address model already exists i.e. updates
How can I make my form always POST no matter what?

Add method: :post
<%= form_for #address,
url: {action: "update_contact", controller: "checkouts"},
html: {class: ""},
method: :post do |f| %>
Without that Rails adds a hidden field which is used to fake a PATCH request when the form is used to update an object.
<input type="hidden" name="_method" value="patch" />

Related

Migrating from `form_for` over to the new `form_with`

How would one go about converting from form_for over to form_with for the following:
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
I've looked through Rails' form_with documentation but haven't found any relevant examples.
Thanks!
form_with has the scope: option that changes input name prefixes, ids and the for attribute of labels. As far as I can tell it does the exact same thing as the as: option for form_for.
:scope - The scope to prefix input field names with and thereby how
the submitted parameters are grouped in controllers.
# Adding a scope prefixes the input field names:
<%= form_with scope: :post, url: posts_path do |form| %>
<%= form.text_field :title %>
<% end %>
# =>
<form action="/posts" method="post" data-remote="true">
<input type="text" name="post[title]">
</form>
https://api.rubyonrails.org/v6.0.0/classes/ActionView/Helpers/FormHelper.html#method-i-form_with
So the equivalent call is:
<%= form_with(
model: resource,
scope: resource_name,
url: password_path(resource_name),
method: :post
) do |f| %>
Try
= form_with model: resource, scope: resource_name,
url: password_path(resource_name), method: :post do |f|
also form_with does not generate classes and ids for elements, if you need these - add them manually
Documentation in api is much more detailed than in guides at the moment.

Is there a way to write a HTML form in rails?

I'm new to RoR and I've stumbled upon a problem. I'm trying to approve or deny a rent, and have been successful in doing so using the HTML form.
<form action="<%= approve_rental_url(rental) %>" method="post">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
<input type="submit" value="Approve">
</form>
This code works without a problem and I know that it isn't the rails way of doing it. I've tried to write it in the rails way and have encountered a problem which says No route matches [POST] "/cats/2".
#Ruby code
<%= form_for approve_rental_url(rental) do |f| %>
<%= f.submit "Approve" %>
<% end %>
Here is the rails routes
Prefix Verb URI Pattern Controller#Action
approve_rental POST /rentals/:id/approve(.:format) rentals#approve
deny_rental POST /rentals/:id/deny(.:format) rentals#deny
rentals POST /rentals(.:format) rentals#create
new_rental GET /rentals/new(.:format) rentals#new
cats GET /cats(.:format) cats#index
POST /cats(.:format) cats#create
new_cat GET /cats/new(.:format) cats#new
edit_cat GET /cats/:id/edit(.:format) cats#edit
cat GET /cats/:id(.:format) cats#show
PATCH /cats/:id(.:format) cats#update
PUT /cats/:id(.:format) cats#update
root GET / cats#index
When you use form_for, you have to pass record as argument like this:
<%= form_for :person do |f| %>
First name: <%= f.text_field :first_name %><br />
Last name : <%= f.text_field :last_name %><br />
Biography : <%= f.text_area :biography %><br />
Admin? : <%= f.check_box :admin %><br />
<%= f.submit %>
<% end %>
If you need to pass URL, you need to use form_tag instead of form_for:
form_tag('/posts/1', method: :put)
But these helpers are softly deprecated. Now there is form_with helper.
You can pass to it URL or record
form_with(model: nil, scope: nil, url: nil, format: nil, **options)
Actually, since this is such a common problem, in rails you can just write
link_to 'Approve', approve_rental_url(rental), class: 'btn btn-success', method: :post
or use the button_to helper
button_to 'Approve', approve_rental_url(rental), class: 'btn btn-success'
(which will POST by default)
and both will automatically inline a form to perform the POST.

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.

rails 4 form_for custom action

I am using <%= form_for #invoice, url: {action: :pay} do |f| %> but the form is sent to the update action instead of the "pay" action I defined in the controller. What am I missing?
You have param :id in your route, and you should assign a param to id in your form action, try this
<%= form_for #invoice, url: {action: "pay", params: {id: #invoice.id}} %>
Or you can use a path
<%= form_for #invoice, url: for_pay_path(#invoice) %>
for_pay_path change to path of pay action, you can see on ouput of rake routes

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