Skip to content

Fix label for not matching input id when collection value is nil#1867

Open
55728 wants to merge 1 commit intoheartcombo:mainfrom
55728:fix/1840-label-for-nil-collection-value
Open

Fix label for not matching input id when collection value is nil#1867
55728 wants to merge 1 commit intoheartcombo:mainfrom
55728:fix/1840-label-for-nil-collection-value

Conversation

@55728
Copy link
Copy Markdown

@55728 55728 commented Apr 19, 2026

Fixes #1840.

Problem

When a collection contains a nil value, the generated label's for attribute does not match the corresponding input's id:

<%= form.input :my_method, as: :radio_buttons,
      collection: [['Yes', true], ['Undefined', nil]] %>

produces:

<!-- ✅ "true" — matches -->
<input id="model_my_method_true" type="radio" value="true" />
<label for="model_my_method_true">Yes</label>

<!-- ❌ nil — trailing underscore mismatch -->
<input id="model_my_method" type="radio" value="" />
<label for="model_my_method_">Undefined</label>

Clicking the "Undefined" label does not select its radio button because the for / id values differ by a single trailing underscore.

Cause

Rails' sanitize_attribute_name unconditionally interpolates an underscore separator:

def sanitize_attribute_name(value)
  "#{sanitized_method_name}_#{sanitized_value(value)}"
end

When value is nil, sanitized_value(nil) returns "", producing "my_method_" (trailing underscore). This value is passed to the label builder as the method name, which generates for="model_my_method_".

Meanwhile, the radio button's id is generated by add_default_name_and_id_for_value, which has a separate nil-check that skips the value suffix entirely, producing id="model_my_method" (no trailing underscore).

Fix

Override sanitize_attribute_name in SimpleForm::Tags::CollectionExtensions to skip the underscore when the sanitized value is empty:

def sanitize_attribute_name(value)
  sanitized = sanitized_value(value)

  if sanitized.empty?
    sanitized_method_name.dup
  else
    "#{sanitized_method_name}_#{sanitized}"
  end
end

This aligns the label's for with the input's id for both collection_radio_buttons and collection_check_boxes, in both inline and nested boolean styles.

When a collection contains a nil value (e.g. `[['Undefined', nil]]`),
the label's `for` attribute gets a trailing underscore
(`for="model_method_"`) while the input's `id` does not
(`id="model_method"`), making the label non-functional.

The root cause is in Rails' `sanitize_attribute_name`, which always
concatenates an underscore separator before the sanitized value
(`"#{sanitized_method_name}_#{sanitized_value(value)}"`), even when
`sanitized_value(nil)` returns an empty string.

Override `sanitize_attribute_name` in `CollectionExtensions` to skip
the trailing underscore when the sanitized value is empty, aligning
the label's `for` with the input's `id`.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Label's for attribute doesn't match the id when nil is given in collection

1 participant