How to Create a Form in Rails Without Having to Use Form_For and a Model Instance

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



Leave a reply



Submit