Hacktoberfest 2026 : les issues que les mainteneurs ont marquées pour octobre, ouvertes et accessibles aux débutants. Parcourir les issues Hacktoberfest

Signal Forms: [formField] bound to a CVA control (e.g. mat-chip-listbox) crashes with "this.field(...) is not a function" on a native double-click

Ouverte
#33,705 1 commentaire 2 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Évaluation

Difficulté
4/5
Temps estimé
3-5 jours
Accessibilité débutants
48/100
Type d'issue
Bug
Clarté
Plutôt claire
Activité
Active
Stack technique
angular, typescript
Domaine
frontend

Piste de recherche

Commencez par la reproduction minimale liée et reproduisez l’échec du double clic synchrone, puis examinez la pile @angular/forms/signals autour de FormField.state et de ɵngControlUpdate, en comparant un clic à deux clics dans le même tour via le pont ControlValueAccessor. C’est terminé lorsque la reproduction ne signale plus TypeError ni NG01902 et que la mat-chip-listbox liée reste utilisable lors de clics répétés.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

area: material/chips gemini-triaged
Which @angular/* package(s) are the source of the bug?

forms

Is this a regression?

No

Description

Binding [formField] to a component that Signal Forms integrates via the ControlValueAccessor bridge (e.g. mat-chip-listbox) crashes with TypeError: this.field(...) is not a function when two click events fire synchronously in the same JS turn - which is exactly what a native double-click produces (browsers dispatch two click events before dblclick).

This is not specific to a dynamic/record-keyed field - it reproduces with a single, plain, statically-named field on a minimal one-field form. It also surfaces the related error NG01902: Orphan field, looking for property '<key>' of <root> on the same trigger.

Reproduced two ways:

  • el.click(); el.click(); on the chip option, in the same script turn (no animation/timing involved)
  • Playwright's .dblclick()

A debounce() on the field does not prevent it (tested both applied and unset).

Minimal reproduction
import { Component, signal } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { form, FormField } from '@angular/forms/signals';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { MatChipsModule } from '@angular/material/chips';

interface Model {
  color: string;
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [FormField, MatChipsModule],
  template: `
    <mat-chip-listbox [formField]="colorForm.color">
      <mat-chip-option value="red">Red</mat-chip-option>
      <mat-chip-option value="blue">Blue</mat-chip-option>
    </mat-chip-listbox>
  `,
})
export class App {
  model = signal<Model>({ color: '' });
  colorForm = form(this.model);
}

bootstrapApplication(App, { providers: [provideAnimationsAsync()] });

Steps:

  1. Load the page.
  2. Double-click either chip option (or, via devtools console:
    document.querySelector('mat-chip-option').click(); document.querySelector('mat-chip-option').click();).

Verified locally against exactly this minimal component (isolated from any larger app code) with both trigger methods above - 0 errors on a single click, crashes reliably on every double-click / synchronous double-click().

Please provide a link to a minimal reproduction of the bug

https://github.com/CSchulz/angular-signal-forms-dblclick-repro

Please provide the exception or error you saw
ERROR TypeError: this.field(...) is not a function
    at FormField.state.ngDevMode.debugName [as computation] (@angular_forms_signals.js:3109:38)
    at Object.producerRecomputeValue (chunk-....js:300:25)
    at producerUpdateValueVersion (chunk-....js:110:8)
    at _FormField.computed2 [as state] (chunk-....js:263:5)
    at _FormField.ɵngControlUpdate (@angular_forms_signals.js:2910:31)
    at Object.update (chunk-....js:10664:33)
    at controlUpdateInternal (chunk-....js:8546:14)
    at Module.ɵɵcontrol (chunk-....js:8532:3)


and, on repeated triggering:


RuntimeError: NG01902: Orphan field, looking for property 'color' of <root>.
Please provide the environment you discovered this bug in (run ng version)
Angular CLI: 22.0.6
Angular: 22.0.6
@angular/material: 22.0.4
@angular/cdk: 22.0.4
Node.js: 24.15.0
TypeScript: 6.0.3
rxjs: 7.8.2
Anything else?

Re-verified 2026-08-19 against:

  • 22.2.0-next.2/22.2.0-next.3 (next dist-tag, i.e. the newest prerelease, not yet promoted to latest) - same crash, not fixed there either
  • Also reproduces with provideNoopAnimations() instead of provideAnimationsAsync() - not animation-timing-related
Is mat-chip-listbox required, or would any CVA control do?

Every frame in the stack trace above is @angular/forms/signals code - no @angular/material code appears in it, so the defect itself is squarely in Signal Forms. That said, a hand-written, Material-free ControlValueAccessor component (tried both a plain two-<button> version and a more faithful one with nested child components, ChangeDetectorRef.markForCheck(), and an
EventEmitter) does not reproduce the crash under the same conditions (page.dblclick() on a never-before-clicked element). So mat-chip-listbox isn't strictly required, but something about its specific interaction pattern is a necessary trigger for this repro, and that trigger has not been identified yet - this app has no zone.js dependency (it's zoneless), which already rules out one candidate (CDK's FocusMonitor/MatRipple moving work in and out of NgZone, since without zone.js those calls are inert no-ops).

Additional context
  • We first hit this in an app with a dynamic per-key record model bound via [formField]="form[dynamicKey]" to a mat-chip-listbox inside an @for loop - but the minimal repro above shows the dynamic-key/loop aspect isn't required at all; a single static field on a single-field form is enough.
  • Workaround: bind the CVA-based control manually ([value]/(change) reading and writing field().value directly) instead of [formField], bypassing the affected path entirely - at the cost of losing automatic touched/disabled/focus wiring for that control.
Langage dominant
TypeScript
Étoiles
25k
Forks
6.8k
Merge moyen
1 j 1 h
PR mergées (30 j)
84

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de angular/components

Toutes les issues de angular/components

Issues similaires

Plus d'issues TypeScript

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.