Skip to content

Conversation

@nolantait
Copy link
Contributor

This branch adds a few core features to select according to https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/select

  • Adds include_blank keyword that adds a blank option
  • Adds multiple keyword that changes name to add [] if mutliple is true
  • Adds hidden input to select elements to handle the way browsers handle blank selects

Relevant part from the link above:

The HTML specification says when multiple parameter passed to select and all options got deselected web browsers do not send any value to server. Unfortunately this introduces a gotcha: if a User model has many roles and have role_ids accessor, and in the form that edits roles of the user the user deselects all roles from role_ids multiple select box, no role_ids parameter is sent. So, any mass-assignment idiom like

@bradgessler
Copy link
Contributor

Thanks for the PR! I didn't realize there was a specification for multiple in a select.

I ran into a similar problem with checkboxes where it actually needs multiple HTML elements to deliver the desired behavior, which I solved via its own component:

class CheckboxComponent < FieldComponent
def template(&)
# Rails has a hidden and checkbox input to deal with sending back a value
# to the server regardless of if the input is checked or not.
input(name: dom.name, type: :hidden, value: "0")
# The hard coded keys need to be in here so the user can't overrite them.
input(type: :checkbox, value: "1", **attributes)
end
def field_attributes
{ id: dom.id, name: dom.name, checked: field.value }
end
end

Since I want Superform to do as little as possible to alter the constructs of HTML, I'd like to explore creating a component for multiple select that isolates this behavior in its own component instead of altering the select tag.

Here's what that would mean to get this merged:

  1. Create a MultipleSelect component that implements the behavior you've built. This could extend the Select component or be its own (more likely it would extend select.

  2. For now expose this via multiple_select such that field(:foo).multiple_select is the way that this is accessed. I'm not in love with a separate method name, but this would be easy to graft into select (or a better method name) when the component is in place.

  3. Use the helpers to generate the field names for arrays of objects vs a single name.

I'm avoiding weird Rails form helper constructs like allow_blank and instead opting for passing a nil into the select method. You can see an example of that at https://github.com/rubymonolith/superform?tab=readme-ov-file#form-field-guide where a nil can be passed into the front or back of the select method. This makes controlling placement of the blank more straightforward.


def field_attributes
super.merge(
name: "#{dom.name}[]",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field(:foo).collection method will generate the correct field names for this without having to manually interpolate strings into the field name. Example at https://github.com/rubymonolith/superform/blob/main/README.md?plain=1#L135-L142 and implementation at https://github.com/rubymonolith/superform/blob/main/lib/superform.rb#L224-L259

I haven't thought too much about exactly how this would be integrated, but if you're willing to bounce around a bit with these commits I think we can get there.

Copy link

@mvkampen mvkampen Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we prefer field.select(multiple: true) over field.multiple_select() i know i do.

The 2nd link is broken, do you mean the
FieldCollection class?

Not entirely sure what you mean on how to use the collection I guess starting with radiobutton would be slightly easier as you don't have to deal with this particular issue about key naming as it is of singular value. As your suggestion here #13

Intuitively it is easier to have the element be responsible for both key and value 1 to 1.
Whereas here you have field(:fruits).collection to yield each <option> but its key should be used in the parent <select> element. However you could argue that select itself should be responsible to generate its key, and not require us to generate collection internally to retrieve the name?
Here the reference to rails key naming for multiple values in form_tag_helper

The code affecting value keys naming in superform.
One more issue that this PR seems to miss is rendering selected options
field.value == key in this case value would be an array and then include? would be the right predicate?

<select name="fruits[]" multiple>
  <option value="apple" selected>Apple</option>
  <option value="banana">Banana</option>
  <option value="cherry" selected>Cherry</option>
</select>

<input type="checkbox" name="fruits[]" value="Apple">
<input type="checkbox" name="fruits[]" value="Banana">
<input type="checkbox" name="fruits[]" value="Cherry">

<input type="radio" name="fruit" value="Apple">
<input type="radio" name="fruit" value="Banana">
<input type="radio" name="fruit" value="Cherry">

# roles of the user the user deselects all roles from role_ids
# multiple select box, no role_ids parameter is sent.
input(type: "hidden", name: dom.name, value: "")
super(...)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also used the argument forwarding, but i realized that this was introduced in ruby 2.7 so then it needs to bump required_ruby_version in the gemspec. If you don't want to include that in this ticket then switch to *, **, & to catch all arguments and can forward explicit, or implicitly super without parenthesis. For these discussions we could include more code style guidance (e.g. rubocop)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants