Skip to content

perf(forms): avoid scheduling change detection on blur when already touched#68636

Open
arturovt wants to merge 1 commit into
angular:mainfrom
arturovt:perf/forms-blur-listener
Open

perf(forms): avoid scheduling change detection on blur when already touched#68636
arturovt wants to merge 1 commit into
angular:mainfrom
arturovt:perf/forms-blur-listener

Conversation

@arturovt
Copy link
Copy Markdown
Contributor

@arturovt arturovt commented May 8, 2026

The blur listener on native form controls was registered through host.listenToDom('blur', ...), which wraps the callback with wrapListener. wrapListener always calls markViewDirty before running the callback, triggering change detection on every blur event.

When a control is already touched, calling markAsTouched() again is a no-op. Signals skip updates for equal values, so no reactive notification or change detection should happen.

Fix this by registering the blur listener with addEventListener directly, bypassing wrapListener. When the control becomes touched for the first time, the signal write still schedules change detection. Subsequent blur events on an already-touched control no longer trigger unnecessary CD cycles.

A regression test verifies that ChangeDetectionScheduler.notify() is not called when blur fires on an already-touched control.

@angular-robot angular-robot Bot added area: performance Issues related to performance area: forms labels May 8, 2026
@ngbot ngbot Bot added this to the Backlog milestone May 8, 2026
Comment thread packages/forms/signals/test/web/form_field.spec.ts Outdated
@arturovt arturovt force-pushed the perf/forms-blur-listener branch from 7f28960 to d4ce89b Compare May 11, 2026 14:28
@arturovt arturovt marked this pull request as ready for review May 11, 2026 14:28
…ouched

The blur listener on native form controls was registered through
`host.listenToDom('blur', ...)`, which wraps the callback with `wrapListener`.
`wrapListener` always calls `markViewDirty` before running the callback,
triggering change detection on every blur event.

When a control is already touched, calling `markAsTouched()` again is a no-op.
Signals skip updates for equal values, so no reactive notification or change
detection should happen.

Fix this by registering the blur listener with `addEventListener` directly,
bypassing `wrapListener`. When the control becomes touched for the first time,
the signal write still schedules change detection. Subsequent blur events on an
already-touched control no longer trigger unnecessary CD cycles.

A regression test verifies that `ChangeDetectionScheduler.notify()` is not
called when blur fires on an already-touched control.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: forms area: performance Issues related to performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants