Skip to content

Commit db3bf3d

Browse files
volvachevEgor Volvachev
authored and
Egor Volvachev
committed
fix(material/autocomplete): outside click in Angular zone.
Fixes a bug in Angular Material `autocomplete` when outside click doesn't trigger `changeDetection`. Fixes #24811
1 parent c6a1d15 commit db3bf3d

File tree

3 files changed

+58
-1
lines changed

3 files changed

+58
-1
lines changed

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3365,6 +3365,32 @@ describe('MDC-based MatAutocomplete', () => {
33653365

33663366
expect(fixture.componentInstance.trigger.panelOpen).toBe(true);
33673367
});
3368+
3369+
it('should emit from `autocomplete.closed` after click outside inside the NgZone', fakeAsync(() => {
3370+
const inZoneSpy = jasmine.createSpy('in zone spy');
3371+
3372+
const fixture = createComponent(SimpleAutocomplete, [
3373+
{provide: NgZone, useFactory: () => new NgZone({enableLongStackTrace: false})},
3374+
]);
3375+
const zone = TestBed.inject(NgZone);
3376+
fixture.detectChanges();
3377+
3378+
fixture.componentInstance.trigger.openPanel();
3379+
fixture.detectChanges();
3380+
flush();
3381+
3382+
const subscription = fixture.componentInstance.trigger.autocomplete.closed.subscribe(() =>
3383+
inZoneSpy(NgZone.isInAngularZone()),
3384+
);
3385+
zone.onStable.emit(null);
3386+
3387+
dispatchFakeEvent(document, 'click');
3388+
3389+
expect(inZoneSpy).toHaveBeenCalled();
3390+
expect(inZoneSpy).toHaveBeenCalledWith(true);
3391+
3392+
subscription.unsubscribe();
3393+
}));
33683394
});
33693395

33703396
const SIMPLE_AUTOCOMPLETE_TEMPLATE = `

src/material/autocomplete/autocomplete-trigger.ts

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

269269
if (this.panelOpen) {
270270
// Only emit if the panel was visible.
271-
this.autocomplete.closed.emit();
271+
// The `NgZone.onStable` always emits outside of the Angular zone,
272+
// so all the subscriptions from `_subscribeToClosingActions()` are also outside of the Angular zone.
273+
// We should manually run in Angular zone to update UI after panel closing.
274+
this._zone.run(() => {
275+
this.autocomplete.closed.emit();
276+
});
272277
}
273278

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

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3371,6 +3371,32 @@ describe('MatAutocomplete', () => {
33713371

33723372
expect(fixture.componentInstance.trigger.panelOpen).toBe(true);
33733373
});
3374+
3375+
it('should emit from `autocomplete.closed` after click outside inside the NgZone', fakeAsync(() => {
3376+
const inZoneSpy = jasmine.createSpy('in zone spy');
3377+
3378+
const fixture = createComponent(SimpleAutocomplete, [
3379+
{provide: NgZone, useFactory: () => new NgZone({enableLongStackTrace: false})},
3380+
]);
3381+
const zone = TestBed.inject(NgZone);
3382+
fixture.detectChanges();
3383+
3384+
fixture.componentInstance.trigger.openPanel();
3385+
fixture.detectChanges();
3386+
flush();
3387+
3388+
const subscription = fixture.componentInstance.trigger.autocomplete.closed.subscribe(() =>
3389+
inZoneSpy(NgZone.isInAngularZone()),
3390+
);
3391+
zone.onStable.emit(null);
3392+
3393+
dispatchFakeEvent(document, 'click');
3394+
3395+
expect(inZoneSpy).toHaveBeenCalled();
3396+
expect(inZoneSpy).toHaveBeenCalledWith(true);
3397+
3398+
subscription.unsubscribe();
3399+
}));
33743400
});
33753401

33763402
const SIMPLE_AUTOCOMPLETE_TEMPLATE = `

0 commit comments

Comments
 (0)