Request for feedback
I am looking for feedback on the following proposal. I would love to contribute a pull request which introduces a new FormBuilder
that uses partials for customizing form field rendering. My main focus is on figuring out if this idea has the potential to be merged to Rails or if it should rather live as a gem. Your comments are highly appreciated!
The Problem
Forms are an integral part of web apps. Almost every application needs to add to the behavior of form field helpers. Here a few examples of adaptations:
- Show error messages right next to the input field
- Render labels for inputs
- Indicate if a field is required or optional
- Keep the field markup & CSS classes consistent throughout the app
- Add more complex input fields (such as a barcode input or a custom date range picker)
Currently, there are multiple ways to solve these problems. There is a dedicated section in the Action View Form Helpers Guide on how to customize form elements. Some cases can even be solved with dedicated configuration options such as field_error_proc.
These approaches are either verbose or too limited to solve the above cases.
Current solutions
Here are alternatives how to solve the adaptations above:
- Copy the required logic & markup into templates Repeating the markup in some views may keep the cross-dependencies low. However, copying the same (faulty) markup/logic to all forms, decreases maintainability.
- Introduce helper methods Helpers are a very convenient way to refactor repeating code. However, specifying markup in helpers can easily become unreadable if the logic becomes more involved. Also, helpers do not have access to the form object unless passed explicitly.
- Add a form builder Form builders are made for adapting and extending form field rendering. They are a great tool. However, similar to introducing helper methods, specifying the markup can become cumbersome (example gist) and hard to read.
- Refactor to partials Partials are geared towards defining markup structures. They keep code readable even if logic becomes more complicated. However, partials do not have access to the form object per se. Also, using the
render
syntax does not communicate the type of input as well as a form builder method (render "forms/text_field", method: :title, builder: f
vs.f.text_field :title
). - Pull in a form builder gem There are many gems, that allow customizing form rendering. Each of them, has its own way to adapt the behavior. This requires the developer to learn these (sometimes complicated) concepts. Also, some gems provide additional functionality which may not be needed (e.g. automatic collection population).
I would love to combine the form builder concept with the advantages of partials.
The Idea
Provide a standard form builder that leverages partials.
I would like to provide an alternative to the existing ActionView::Helpers::FormBuilder
. This additional builder (let’s call it PartialFormBuilder
for now), would inherit from the existing FormBuilder
. Yet, instead of assembling the field markup directly, it would render partials (the gem Kaminari also uses this concept, example partial).
Here an example how this might look like (mind the _
prefix):
<%# app/views/blog/_form.html.erb %>
<%= form_with model: @blog do |f| %>
<%= f._text_field :title # renders app/views/form/_text_field.html.erb %>
<%= f._text_field :author, hint: "The main writer of the blog" %>
<%= f.text_field :slug # calling without prefix calls the default form helper %>
<% end %>
The PartialFormBuilder
allows to render partials when using the _
. Calls to these prefixed methods receive the given arguments, infer additional information (e.g. the field’s error messages) and pass them to a partial:
<%# app/views/form/_text_field.html.erb %>
<%# recieves +f+, +method+ and +errors+ and all options (keyword arguments) passed in the original template %>
<% if !defined?(label) || label != false %>
<% f.label(method) do %>
<% end %>
<% if errors.any? %>
%span.text-red <%= errors.to_sentence %>
<% end %>
<% class = class_names(local_assigns[:class], "bg-red-200": errors.any?) %>
<%= f.text_field(method, **local_assigns, class:) %>
Providing such a form builder, allows the developer to adapt and extend the rendering of form elements. By using partials, developers to focus on the templates themselves rather than developing builders. By using templates instead of methods, this approach avoids specifying markup with tag helper methods.
Ideally, we add the PartialFormBuilder
as part of the framework. This would introduce a “standard way” for solving the recurring problem of customizing the form elements.
What do you think?
Next steps
You can ignore this section for now.
- Decide if this could be an addition to Rails itself or a gem
- Discuss syntax and necessity of prefix
- Implement
PartialFromBuilder
- Devise standard partials and add generator
- Adapt Rails guide
- Add more extensive examples (tailwind, nested, etc.) as part of the documentation
- Devise & run performance benchmarks