How can I create a form in Rails without having to use form_for and a model instance?
Take a look at form_tag
.
<% form_tag '/posts' do %>
<div><%= submit_tag 'Save' %></div>
<% end %>
Rails 3 / Form without Model: How do I create a form that is not tied to a model?
Use form_tag
instead of form_for
, then use the appropriate form helpers: text_field_tag
instead of f.text_field
, text_area_tag
instead of f.text_area
, etc. Example:
<%= form_tag "/my_controller/update2" do %>
<%= text_field_tag "account", "default info" %>
<%= submit_tag "Save" %>
<% end %>
The Rails API site has a great reference to all of the _tag helpers: http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html
Rails 4 form without model and database
form_for
can also take an arbitrary symbol:
<%= form_for :anything, url: "my_controller/my_action" do |form| %>
<%= form.text_field :name %>
<%= form.submit %>
<% end %>
This will send a post to my_controller/my_action
.
The html output will look something like this:
<form accept-charset="UTF-8" action="my_controller/my_action" method="post">
<input id="anything_name" name="anything[name]" type="text">
<input name="commit" type="submit" value="Save Testing">
</form>
SimpleForm without for (non model form)
You can use :symbol
as the first argument.
<%= simple_form_for :user, url: users_path do |f| %>
<%= f.input :name, as: :string %>
...
<% end %>
It will output something like this:
<form novalidate="novalidate" class="simple_form user" action="/users" accept-charset="UTF-8" method="post">
...
<div class="input string required user_name">
<label class="string required" for="user_name">
<abbr title="required">*</abbr> Name
</label>
<input class="string required" type="text" name="user[name]" id="user_name" />
</div>
...
</form>
How to do a non-model form in ruby on rails?
You will need FormHelper methods:
Say you want a simple test
action that submits to do_test
action:
A simple view for test action (posts/test.html.erb):
<% form_tag '/posts/do_test' do %>
<%=label_tag 'name' %>
<%=text_field_tag 'name'%>
<%=label_tag 'phone' %>
<%=text_field_tag 'phone'%>
<div><%= submit_tag 'Save' %></div>
<% end -%>
In the posts controller:
def test
end
def do_test
name = params[:name]
phone = params[:phone]
# do whatever you want...
end
Also you need to add these 2 actions to the routes in config/routes.rb
map.resources :posts, :collection=>{:test => :get, :do_test => :post}
Ruby on Rails - Create a form with Multiple instances of the same model
Move the loop inside your form tag
= form_tag '/bank_account', html: { id: 'edit_bank_accounts', class: 'edit_bank_accounts' } do
-@bank_accounts.each do |account|
= text_field_tag :bank_account_first_name, account.first_name, name: "bank_account[#{account.id}][first_name]"
How are parameters sent when I use instance variables in form_for in rails
The form_for
helper creates a form from a model object. If you've set up your Rails app in the conventional way, you will have (it appears) a file called app/models/missed_call_flow.rb
that begins by defining class MissedCallFlow
.
This file defines the model. It is the source of the params[:missed_call_flow]
name, because it is the convention in Rails to name this data structure for the model.
As a generalization in programming, the variable name should not impact the functioning of the program. The fact that you have named your variable @flow
is ignored by the inner workings of your program. It is a convenience to the programmer to be able to name your variable whatever you want.
I wonder if you are not seeing the model name you expect because missed_call_flow
represents a join table between two other tables with models named missed_call
and flow
. You might be assigning an instance of this MissedCallFlow class to the variable @flow
by mistake.
in rails difference between form_for @article and form_for :article
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
Using form_with with an ActiveModel object?
Override persisted?
method. It is defined in ActiveModel::API
:
def persisted?
false
end
This method is used by the form builder to decide if it needs to send a post or patch request.
# app/models/transit_provider.rb
class TransitProvider
include ActiveModel::Model
attr_accessor :provider, :service
# NOTE: This is set to `false` by default. See `ActiveModel::API`.
# TODO: Decide what it means for `TransitProvider`
# to be persisted. Could `provider` be persisted while
# `service` is not?
def persisted?
provider.persisted? && service.persisted?
end
# NOTE: `id` would be required for the update route
# for plural `resources`.
# Don't need it for a singular `resource`. See routes.rb.
# def id
# 1
# end
end
# config/routes.rb
Rails.application.routes.draw do
resource :transit_provider, only: [:create, :update]
# ^
# NOTE: Singular. Don't need `id` in routes, we're not asking
# for any data from this controller.
# NOTE: Make url mapping always resolve to singular route.
#
# `transit_provider_path`
#
# Otherwise, in the form url would resolve to undefined
# plural `transit_providers_path` for `create` action.
resolve("TransitProvider") { [:transit_provider] }
# NOTE: Change it if you need `id`. Also add `id` method to
# `TransitProvider`
# resources :transit_providers
end
# app/controllers/transit_providers_controller.rb
class TransitProvidersController < ApplicationController
def create
# TODO: create
end
def update
# TODO: update
end
end
# NOTE: persisted
<% model = TransitProvider.new(
provider: Provider.first,
service: Service.first)
%>
# NOTE: not persisted
# model = TransitProvider.new(provider: Provider.new, service: Service.new)
<%= form_with model: model do |f| %>
<%= f.fields_for :provider, model.provider do |ff| %>
<%= ff.text_field :id if ff.object.persisted? %>
<%= ff.text_field :name %>
<% end %>
<%= f.fields_for :service, model.service do |ff| %>
<%= ff.text_field :id if ff.object.persisted? %>
<%= ff.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
For persisted TransitProvider form does a PATCH request to update.
Started PATCH "/transit_provider" for 127.0.0.1 at 2022-07-03 15:47:57 -0400
Processing by TransitProvidersController#update as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "transit_provider"=>{"provider"=>{"id"=>"1", "name"=>"provide"}, "service"=>{"id"=>"1", "name"=>"service"}}, "commit"=>"Update Transit provider"}
Otherwise it is a POST to create.
Started POST "/transit_provider" for 127.0.0.1 at 2022-07-03 16:13:43 -0400
Processing by TransitProvidersController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "transit_provider"=>{"provider"=>{"name"=>""}, "service"=>{"name"=>""}}, "commit"=>"Create Transit provider"}
Update what is persisted?
ActiveModel gets its persisted? method from ActiveModel::API it is unrelated to ActiveRecord's persisted? method. Neither take id
attribute into account to decide if the record is persisted:
# ActiveModel's persisted? is just `false`
# ActiveRecord
Service.create(name: "one") # => #<Service: id: 1, name: "one">
Service.new.persisted? # => false
Service.first.persisted? # => true
Service.new(id: 1).persisted? # => false
Service.new(id: 1).reload.persisted? # => true
s = Service.select(:name).first
s.id # => nil
s.persisted? # => true
s = Service.first.destroy
s.id # => 1
s.persisted? # => false
This is important because form builder uses this method to choose between POST and PATCH method and url_for helper uses it to build polymorphic routes.
url_for(Service.first) # => "/services/1"
url_for(Service.new(id: 1)) # => "/services"
url_for(Service.new) # => "/services"
# NOTE: it is different from named route helpers,
# which will grab required `params` from anything
# argument, hash, model, or url params.
service_path(Service.first) # => "/services/1"
service_path(Service.new(id: 1)) # => "/services/1"
service_path({id: 1}) # => "/services/1"
service_path(1) # => "/services/1"
# and if `params` have id: 1 (as in show action)
service_path # => "/services/1"
Note that, by default, ActiveModel::API implements persisted? to
return false, which is the most common case. You may want to override
it in your class to simulate a different scenario.
https://api.rubyonrails.org/classes/ActiveModel/API.html#method-i-persisted-3F
https://api.rubyonrails.org/classes/ActiveModel/Model.html
https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resource
https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/CustomUrls.html#method-i-resolve
Related Topics
Ruby: Merge Two Hash as One and with Value Connected
Does Ruby Support Unicode and How Does It Work
Error: While Executing Gem ... (Typeerror) Incompatible Marshal File Format (Can't Be Read)
"Gem Update --System Is Disabled on Debian" Error
Ruby Mechanize Post with Header
Ruby: Merge Two Hash as One and with Value Connected
Building Hash by Grouping Array of Objects Based on a Property of the Items
Split String Without Removing Delimiter
Dealing with Large CSV Files (20G) in Ruby
How to Run a Simple File on Heroku
Ruby on Rails Webpacker Can't Find Images Under Asset_Pack_Path
Trouble Downgrading Ruby on Os X Mavericks
Connecting to Web Services Using Rails (Http Requests)
How to Configure Config.Yml So That I Can Install Devkit
How to Modify the Input Type of the Rails Datetime_Select Helper