Skip to content

forms: signals - number [formField] loses characters while typing (parsed value written back to the input on every keystroke) #69635

Description

@apimentasoto

Which @angular/* package(s) are the source of the bug?

forms

Is this a regression?

No

Description

Description

When a number | null field is bound to a native <input> via [formField], the input's
displayed text is overwritten with the parsed model value on every keystroke. Intermediate
states that don't round-trip through Number → String (e.g. -0, 1.0000) are lost mid-edit,
so characters disappear while typing.

The same happens with the Angular 22 number | null<input type="text"> binding.

The equivalent reactive-forms setup (FormControl on the same native input) does not show this.

I noticed this on ng 21 when the feature was experimental but I was hoping it would get fixed. I did see some related issues like #67847 and that got addressed, however it seems only targetting CVA controls.

Just the fact that it's not possible to type/input a negative decimal number without going back to add the - (minus) seems like a big inconvenience...

Reproduction

Minimal standalone component:

import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';

@Component({
  selector: 'app-root',
  imports: [FormField],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <input type="number" step="0.01" [formField]="f.numero">
    <input type="text" inputmode="decimal" [formField]="f.numeroTesto">
    <pre>{{ model() | json }}</pre>
  `,
})
export class App {
  model = signal<{ numero: number | null; numeroTesto: number | null }>(
    { numero: null, numeroTesto: null },
  );
  f = form(this.model);
}

Type character-by-character (real keystrokes — not paste, not input.value = …):

# Field Typed keystroke-by-keystroke Expected Actual
A type=number - 0 . 5 -0.5 0.5 (minus dropped)
B type=number 1 . 0 0 0 0 5, then Backspace, then 4 1.00004 14
C type=text - 0 . 5 -0.5 0.5 (minus dropped)

Injecting the whole value in one programmatic InputEvent (e.g. -0.5) keeps it intact — only
incremental typing is affected.

Expected behavior

Typing into the input should not have the parsed value re-rendered back onto it mid-edit;
intermediate states (-, -0, 1.0000) should be preserved while the user types, as with a
reactive FormControl.

Actual behavior

Characters are lost / the value is corrupted while typing (table above). No console errors.

Please provide a link to a minimal reproduction of the bug

https://github.com/apimentasoto/signal-forms-number-repro

Please provide the exception or error you saw


Please provide the environment you discovered this bug in (run ng version)

Angular CLI       : 22.0.5
Angular           : 22.0.5
Node.js           : 24.18.0
Package Manager   : npm 11.16.0
Operating System  : linux x64

┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package                   │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build            │ 22.0.5            │ ^22.0.4           │
│ @angular/cli              │ 22.0.5            │ ^22.0.4           │
│ @angular/common           │ 22.0.5            │ ^22.0.0           │
│ @angular/compiler         │ 22.0.5            │ ^22.0.0           │
│ @angular/compiler-cli     │ 22.0.5            │ ^22.0.0           │
│ @angular/core             │ 22.0.5            │ ^22.0.0           │
│ @angular/forms            │ 22.0.5            │ ^22.0.0           │
│ @angular/platform-browser │ 22.0.5            │ ^22.0.0           │
│ rxjs                      │ 7.8.2             │ ~7.8.0            │
│ typescript                │ 6.0.3             │ ~6.0.2            │
└───────────────────────────┴───────────────────┴───────────────────┘

Anything else?

Minimal reproduction (clone and run — one standalone component, no Angular Material or router):

git clone https://github.com/apimentasoto/signal-forms-number-repro
cd signal-forms-number-repro && npm install && npm start

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions