Skip to content

bug(aria/combobox): setting expanded programmatically closes the popup immediately under Zone.js (works zoneless) #33516

Description

@FrapperColin

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

Setting the combobox's expanded model to true programmatically (e.g. open-on-focus) does not keep the popup open under Zone.js change detection: expanded is immediately reset to
false, so the popup never becomes visible. The same code works correctly under provideZonelessChangeDetection().

Keyboard-driven opening (ArrowDown) works fine under Zone.js — only programmatically setting expanded fails.

This looks like the same class of Zone.js afterRenderEffect scheduling issue fixed for the keyboard relay in #33453 (commit e87216c), but applied only to keyboardEventRelayEffect. In
src/aria/combobox/combobox.ts the sibling effects are still registered in the default phase:

afterRenderEffect({ write: () => this._pattern.keyboardEventRelayEffect() }); // fixed in #33453 (https://github.com/angular/components/pull/33453)
afterRenderEffect(() => this._pattern.closePopupOnBlurEffect());              // still default phase
afterRenderEffect(() => { this.contentVisible.set(this._pattern.isExpanded()); }); // still default phase

closePopupOnBlurEffect() does:

if (expanded && !alwaysExpanded && !comboboxFocused && !popupFocused) {
  this.inputs.expanded.set(false);
}

Under Zone.js, when expanded is set in a focus handler, this effect observes isFocused() as still false and resets expanded. alwaysExpanded is the only way to keep the popup
open, but it permanently forces isExpanded() true, disabling Escape-to-close and ArrowUp/Down-to-open.

Reproduction

StackBlitz link: https://stackblitz.com/edit/components-issue-starter-vhdrsdal?file=src%2Fmain.ts
Steps to reproduce:

Minimal editable combobox, bootstrapped with provideZoneChangeDetection():

<input ngCombobox #cb="ngCombobox" [(expanded)]="expanded" (focus)="expanded.set(true)" />
<ng-template ngComboboxPopup [combobox]="cb">
  <div ngListbox ngComboboxWidget>
    <div ngOption value="a">A</div>
  </div>
</ng-template>
  1. Focus the input.
  2. Popup briefly opens then closes; expanded reads false in devtools.
  3. Switch to provideZonelessChangeDetection() → works as expected.

Expected

Setting expanded = true (while the input is focused) keeps the popup open under Zone.js, matching zoneless behaviour.

Actual

expanded is reset to false by closePopupOnBlurEffect before the focus state settles; popup does not stay open.

Expected Behavior

Setting expanded = true (while the input is focused) keeps the popup open under Zone.js, matching zoneless behaviour.

Actual Behavior

expanded is reset to false by closePopupOnBlurEffect before the focus state settles; popup does not stay open.

Environment

  • @angular/aria, @angular/cdk, @angular/core: 22.0.3
  • Zone.js: enabled (provideZoneChangeDetection())
  • Repro also expected on main (effects still default-phase).

Metadata

Metadata

Assignees

Labels

P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: aria/comboboxgemini-triagedLabel noting that an issue has been triaged by gemini

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions