Skip to content

Single Select Column Allowed Every Option

Ed Mozley edited this page Sep 21, 2026 · 1 revision

A single-select column let you tick every option at once

Reported with a screenshot by a user Β· Fixed in #1840 Β· Released in 2.3.1


What you saw

A table question on a form, with a column set to Choose one - so each row offers a set of radio buttons and you pick exactly one.

Clicking 1, then 3, then 5, then 10 did not move the selection. It added one each time, leaving all four filled in on the same row. A column whose entire purpose is to accept one answer accepted every answer.

It happened on the analyst form and on the self-service portal alike.


What was actually wrong

A set of radio buttons is a group, and the group is defined by the name attribute. Buttons sharing a name are one control and the browser enforces one choice between them. Buttons with different names are separate controls that know nothing about each other.

The name has to be allocated once per cell - the cell is the group, each option a member of it. Both renderers allocated it inside the loop over the options:

return (c.options || []).map((o, n) =>
    `<input type="radio" name="g_${fieldId}_${c.id}_${gridRadioSeq++}" ...>`)

gridRadioSeq++ runs once per option, so every option got a different name. That is not a broken group. It is four groups of one, each perfectly happy to be ticked on its own.

The comment sitting directly above that line described the right rule - that the name must be scoped to the field, the column and the row, or every row's buttons would be one group and choosing in the second row would clear the first. The reasoning was correct and had been thought through. Only the increment was in the wrong place, which is exactly why the code reads as though it is fine.

πŸ”‘ A correct comment above incorrect code is harder to spot than no comment at all. It answers the question you were about to ask.

It was in two places

The analyst form and the self-service portal each render their own markup - deliberately, since they genuinely look different - and both carried the same defect. A form was affected wherever it was filled in.


How it was fixed

The name is taken once, before the options are walked:

const group = `g_${fieldId}_${c.id}_${gridRadioSeq++}`;
return (c.options || []).map(o =>
    `<input type="radio" name="${group}" ...>`)

Files changed

File What changed
forms/fill.php the group name is allocated once per cell
self-service/catalogue.php the same, in the portal's renderer

How it was proved

By clicking, not by reading. The real gridCellHtml() and catGridCellHtml() were lifted out of both pages into a harness - brace-matched out of the source rather than retyped, so the harness cannot quietly agree with itself while disagreeing with the page - and then driven with real clicks:

old code fixed
distinct group names in one cell 4 1
still ticked after clicking all four 4 1
choosing in row 2 clears row 1 yes no

Both renderers were measured, and the control run against git show HEAD: failed first - which is what makes the passing run mean anything.


If you have older submissions

A submission saved while this was happening kept only the first ticked option in each cell. The reader takes the first checked button it finds, and with four of them checked that is whichever was clicked earliest.

There is nothing to repair and nothing you need to do, but an answer recorded before 2.3.1 may be missing a later choice. It is worth knowing before you rely on one.


See also

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally