Skip to content

Provide form_with as a new alternative to form_for/form_tag #25197

@dhh

Description

@dhh

form_tag and form_for provide two very similiar interfaces to much of the same thing. We should unify that usage and subsequent calls to field tags. Additionally, there are several deficiencies with the current helpers that I'd like to solve at the same time, now that we will have a new form method to change defaults with:

  1. Don't set DOM id or classes on the form or the fields any more. It frequently created duplicate ids and its not used often enough to warrant being a default behavior. You can easily add the id or class if need be.
  2. Don't require HTML tags, like class and id, to be wrapped in a html: {} key. There aren't a high likelihood of conflict and it complicates the default cases.
  3. Allow form fields that do not correspond to model attributes. This makes it easier to mix and match a form with some fields that correspond to a model and others that will trigger other behaviors (like sending a welcome email).
  4. Make remote: true the default. Full-page changes after submissions are rough. When using Turbolinks, a normal redirect will generate a Turbolinks.visit() call, and otherwise there's SJR. (We could consider having config.action_view.forms_remote_by_default that you could set to false, for people going old school).

Examples:

# Passing model: @post will 1) set scope: :post, 2) set url: url_for(@post)
form_with(model: @post) do |form|
  form.text_field :title # Will reference @post.title as normal
  form.text_area :description, "Overwrite @post.description if present, if not, it will still work"

  form.submit
end

form_with(scope: :post, url: posts_path) do |form|
  form.text_field :title # post[title]
  form.text_area :description, "Overwrite @post.description or ignore if it's not present"

  form.submit
end

# Submits title=X&description=Y
form_with(url: different_path, class: 'something', id: 'specific') do |form|
  form.text_field :title, 'This has is the value of the title'

  form.text_area :description, class: 'No value has been supplied here'

  form.fields(:permission) do |fields|
    # on/off instead of positional parameters for setting values
    fields.check_box :admin, on: 'yes', off: 'no'
  end

  form.select :category, Post::CATEGORIES, blank: 'None'
  form.select :author_id, Person.all.collect { |p| [ p.name, p.id ] }, blank: 'Pick someone'

  form.submit
end

There's still a fair amount of design work to deal with, especially around the FormOptionsHelper. We should move away from positional parameters entirely and replace them with named ones. But we should also try to cut down on needless options and pick better defaults. And finally we should simply drop a lot of the overly-specialized select option methods.

Note: For 5.x, form_for and form_tag should just be soft deprecations, no warnings. Then we can deprecate with a warning, perhaps, in Rails 6.x.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions