FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(forms): flush debounced descendant values when a field is touched by MeAkib · Pull Request #71150 · angular/angular · GitHub

fix(forms): flush debounced descendant values when a field is touched - #71150

Open
MeAkib wants to merge 2 commits into
angular:mainfrom
MeAkib:forms/submit-flush-child-debounce
Open

MeAkib wants to merge 2 commits into
angular:mainfrom
MeAkib:forms/submit-flush-child-debounce

Conversation

MeAkib commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

markAsTouched() synchronized a pending debounced value only on the field it was called on. Its descendants were marked as touched, but their pending values stayed unsynced.

submit() marks the root as touched before running the action, so submitting with Enter while a debounced child still had focus ran the action with the child's old value. Clicking a submit button hid the bug, because the click blurs the input first.

Flush each descendant's pending value as it is marked as touched.

Tested with new specs in //packages/forms/signals/test/node:test that fail without the fix: one touches the root, the other calls submit().

Fixes #71149

`markAsTouched()` synchronized a pending debounced value only on the
field it was called on. Its descendants were marked as touched, but
their pending values stayed unsynced.

`submit()` marks the root as touched before running the action, so
submitting with Enter while a debounced child still had focus ran the
action with the child's old value. Clicking a submit button hid the bug,
because the click blurs the input first.

Flush each descendant's pending value as it is marked as touched.

Tested with new specs in `//packages/forms/signals/test/node:test` that
fail without the fix: one touches the root, the other calls `submit()`.

Fixes angular#71149
pullapprove Bot requested a review from atscott October 3, 2026 01:28
ngbot Bot added this to the Backlog milestone Oct 3, 2026

JeanMeche left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

AGENT: Great catch on syncing pending values when a field is touched!

However, there is an inconsistency introduced by putting child.flushSync() directly in the descendant loop. If a child control skips validation (for example, if it's hidden or disabled), child.markAsTouchedInternal() will return early and will not iterate over its own descendants. Because of the unconditional child.flushSync() in the parent's loop, this results in:

  1. The hidden/disabled direct child is flushed.
  2. The grandchildren of that hidden/disabled child are not flushed.

To fix this inconsistency and fulfill the goal of flushing exactly when a node is marked as touched, we can move this.flushSync() directly into markAsTouchedInternal() right after marking the state, and remove the manual flush from the markAsTouched wrapper.

Here is what that would look like:

  markAsTouched(options?: MarkAsTouchedOptions): void {
    if (this.structure.isOrphaned()) {
      return;
    }
    untracked(() => {
      this.markAsTouchedInternal(options);
      // Removed this.flushSync() here
    });
  }

  markAsTouchedInternal(options?: MarkAsTouchedOptions): void {
    if (this.structure.isOrphaned()) {
      return;
    }
    if (this.validationState.shouldSkipValidation()) {
      return; // Hidden/Disabled fields correctly bail out BEFORE flushing
    }
    this.nodeState.markAsTouched();
    this.flushSync(); // Flush happens EXACTLY when marked as touched
    
    if (options?.skipDescendants) {
      return;
    }
    for (const child of this.structure.children()) {
      child.markAsTouchedInternal();
      // Removed child.flushSync() here
    }
  }

This ensures that hidden/disabled fields (and their descendants) correctly bail out before flushing, keeping the tree consistent.

MeAkib requested a review from JeanMeche October 3, 2026 12:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Signal forms: submit() runs the action before debounced child values are synced

2 participants


Back | FazBrowse Home | New Git URL