Skip to content

Signal forms: select is not resynced when the text of an option without a value changes #71443

Open

Description

@auraseb

What happens

A <select> bound with [formField] keeps its value in sync with the model by watching its options with a MutationObserver. The observer is set up with characterData: true because an <option> without a value attribute uses its text as its value. However isRelevantSelectMutation in packages/forms/signals/src/directive/select.ts only treats a mutation as relevant when nodes were added or removed. A characterData record never has added or removed nodes, so text changes are always ignored.

As a result, when the model value matches an option only after that option's text changes, the select stays on no selection.

What should happen

When the text of an option changes so that it now matches the model value, the select should show that option as selected, the same as when a matching option is added.

Minimal repro

@Component({
  imports: [FormField],
  template: `
    <select [formField]="f">
      <option>one</option>
      <option>{{ label() }}</option>
    </select>
  `,
})
class Cmp {
  f = form(signal('two'));
  label = signal('pending');
}
  1. Render the component. select.value is '' because no option matches 'two'.
  2. Run label.set('two') and wait for the app to be stable.
  3. select.value is still ''. Expected 'two'.

Root cause

if (mutation.type === 'childList' || mutation.type === 'characterData') {
  if (mutation.target instanceof Comment) {
    return false;
  }
  for (const node of mutation.addedNodes) { ... }
  for (const node of mutation.removedNodes) { ... }
  return false;
}

For a characterData record addedNodes and removedNodes are empty, so the function returns false. A text node data change on a non comment node should be treated as relevant.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions