Skip to content

Commit 86a1e8c

Browse files
volvachevandrewseguin
authored andcommitted
fix(material/autocomplete): outside click in Angular zone. (#24817)
Fixes a bug in Angular Material `autocomplete` when outside click doesn't trigger `changeDetection`. Fixes #24811 (cherry picked from commit 4e5e286)
1 parent 68d09dd commit 86a1e8c

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

src/material-experimental/mdc-autocomplete/autocomplete.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3135,6 +3135,31 @@ describe('MDC-based MatAutocomplete', () => {
31353135

31363136
expect(fixture.componentInstance.trigger.panelOpen).toBe(true);
31373137
});
3138+
3139+
it('should emit from `autocomplete.closed` after click outside inside the NgZone', fakeAsync(() => {
3140+
const inZoneSpy = jasmine.createSpy('in zone spy');
3141+
3142+
const fixture = createComponent(SimpleAutocomplete, [
3143+
{provide: NgZone, useFactory: () => new NgZone({enableLongStackTrace: false})},
3144+
]);
3145+
const ngZone = TestBed.inject(NgZone);
3146+
fixture.detectChanges();
3147+
3148+
fixture.componentInstance.trigger.openPanel();
3149+
fixture.detectChanges();
3150+
flush();
3151+
3152+
const subscription = fixture.componentInstance.trigger.autocomplete.closed.subscribe(() =>
3153+
inZoneSpy(NgZone.isInAngularZone()),
3154+
);
3155+
ngZone.onStable.emit(null);
3156+
3157+
dispatchFakeEvent(document, 'click');
3158+
3159+
expect(inZoneSpy).toHaveBeenCalledWith(true);
3160+
3161+
subscription.unsubscribe();
3162+
}));
31383163
});
31393164

31403165
const SIMPLE_AUTOCOMPLETE_TEMPLATE = `

src/material/autocomplete/autocomplete-trigger.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,12 @@ export abstract class _MatAutocompleteTriggerBase
259259

260260
if (this.panelOpen) {
261261
// Only emit if the panel was visible.
262-
this.autocomplete.closed.emit();
262+
// The `NgZone.onStable` always emits outside of the Angular zone,
263+
// so all the subscriptions from `_subscribeToClosingActions()` are also outside of the Angular zone.
264+
// We should manually run in Angular zone to update UI after panel closing.
265+
this._zone.run(() => {
266+
this.autocomplete.closed.emit();
267+
});
263268
}
264269

265270
this.autocomplete._isOpen = this._overlayAttached = false;

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3141,6 +3141,31 @@ describe('MatAutocomplete', () => {
31413141

31423142
expect(fixture.componentInstance.trigger.panelOpen).toBe(true);
31433143
});
3144+
3145+
it('should emit from `autocomplete.closed` after click outside inside the NgZone', fakeAsync(() => {
3146+
const inZoneSpy = jasmine.createSpy('in zone spy');
3147+
3148+
const fixture = createComponent(SimpleAutocomplete, [
3149+
{provide: NgZone, useFactory: () => new NgZone({enableLongStackTrace: false})},
3150+
]);
3151+
const ngZone = TestBed.inject(NgZone);
3152+
fixture.detectChanges();
3153+
3154+
fixture.componentInstance.trigger.openPanel();
3155+
fixture.detectChanges();
3156+
flush();
3157+
3158+
const subscription = fixture.componentInstance.trigger.autocomplete.closed.subscribe(() =>
3159+
inZoneSpy(NgZone.isInAngularZone()),
3160+
);
3161+
ngZone.onStable.emit(null);
3162+
3163+
dispatchFakeEvent(document, 'click');
3164+
3165+
expect(inZoneSpy).toHaveBeenCalledWith(true);
3166+
3167+
subscription.unsubscribe();
3168+
}));
31443169
});
31453170

31463171
const SIMPLE_AUTOCOMPLETE_TEMPLATE = `

0 commit comments

Comments
 (0)