Skip to content

Commit 808a3a5

Browse files
committed
fix(autocomplete): mark control as touched once panel is closed
Currently we mark the autocomplete control as touched on `blur`. The problem is that the `blur` event happens a split second before the panel is closed which can cause the error styling to show up and disappear quickly which looks glitchy. These changes change it so that the control is marked as touched once the panel is closed. Also makes a couple of underscored properties private since they weren't used anywhere in the view. Fixes #18313.
1 parent 290d102 commit 808a3a5

File tree

3 files changed

+48
-15
lines changed

3 files changed

+48
-15
lines changed

src/material/autocomplete/autocomplete-trigger.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ export function getMatAutocompleteMissingPanelError(): Error {
116116
// Note: we use `focusin`, as opposed to `focus`, in order to open the panel
117117
// a little earlier. This avoids issues where IE delays the focusing of the input.
118118
'(focusin)': '_handleFocus()',
119-
'(blur)': '_onTouched()',
120119
'(input)': '_handleInput($event)',
121120
'(keydown)': '_handleKeydown($event)',
122121
},
@@ -172,10 +171,10 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, AfterViewIn
172171
}
173172

174173
/** `View -> model callback called when value changes` */
175-
_onChange: (value: any) => void = () => {};
174+
private _onChange: (value: any) => void = () => {};
176175

177176
/** `View -> model callback called when autocomplete has been touched` */
178-
_onTouched = () => {};
177+
private _onTouched = () => {};
179178

180179
/** The autocomplete panel to be attached to this trigger. */
181180
@Input('matAutocomplete') autocomplete: MatAutocomplete;
@@ -604,6 +603,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, AfterViewIn
604603
}
605604

606605
this.closePanel();
606+
this._onTouched();
607607
}
608608

609609
/**

src/material/autocomplete/autocomplete.spec.ts

+45-10
Original file line numberDiff line numberDiff line change
@@ -810,18 +810,53 @@ describe('MatAutocomplete', () => {
810810
.toBe(false, `Expected control to stay pristine if value is set programmatically.`);
811811
});
812812

813-
it('should mark the autocomplete control as touched on blur', () => {
814-
fixture.componentInstance.trigger.openPanel();
815-
fixture.detectChanges();
816-
expect(fixture.componentInstance.stateCtrl.touched)
817-
.toBe(false, `Expected control to start out untouched.`);
813+
it('should mark the autocomplete control as touched when the panel is closed via the keyboard',
814+
fakeAsync(() => {
815+
fixture.componentInstance.trigger.openPanel();
816+
fixture.detectChanges();
817+
zone.simulateZoneExit();
818818

819-
dispatchFakeEvent(input, 'blur');
820-
fixture.detectChanges();
819+
expect(fixture.componentInstance.stateCtrl.touched)
820+
.toBe(false, `Expected control to start out untouched.`);
821821

822-
expect(fixture.componentInstance.stateCtrl.touched)
823-
.toBe(true, `Expected control to become touched on blur.`);
824-
});
822+
dispatchKeyboardEvent(input, 'keydown', TAB);
823+
fixture.detectChanges();
824+
825+
expect(fixture.componentInstance.stateCtrl.touched)
826+
.toBe(true, `Expected control to become touched on blur.`);
827+
}));
828+
829+
it('should mark the autocomplete control as touched when the panel is closed by clicking away',
830+
fakeAsync(() => {
831+
fixture.componentInstance.trigger.openPanel();
832+
fixture.detectChanges();
833+
zone.simulateZoneExit();
834+
835+
expect(fixture.componentInstance.stateCtrl.touched)
836+
.toBe(false, `Expected control to start out untouched.`);
837+
838+
dispatchFakeEvent(document, 'click');
839+
fixture.detectChanges();
840+
841+
expect(fixture.componentInstance.stateCtrl.touched)
842+
.toBe(true, `Expected control to become touched on blur.`);
843+
}));
844+
845+
it('should not mark the autocomplete control as touched when the panel is closed ' +
846+
'programmatically', fakeAsync(() => {
847+
fixture.componentInstance.trigger.openPanel();
848+
fixture.detectChanges();
849+
zone.simulateZoneExit();
850+
851+
expect(fixture.componentInstance.stateCtrl.touched)
852+
.toBe(false, `Expected control to start out untouched.`);
853+
854+
fixture.componentInstance.trigger.closePanel();
855+
fixture.detectChanges();
856+
857+
expect(fixture.componentInstance.stateCtrl.touched)
858+
.toBe(false, `Expected control to stay untouched.`);
859+
}));
825860

826861
it('should disable the input when used with a value accessor and without `matInput`', () => {
827862
overlayContainer.ngOnDestroy();

tools/public_api_guard/material/autocomplete.d.ts

-2
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,6 @@ export declare class MatAutocompleteSelectedEvent {
7979
}
8080

8181
export declare class MatAutocompleteTrigger implements ControlValueAccessor, AfterViewInit, OnChanges, OnDestroy {
82-
_onChange: (value: any) => void;
83-
_onTouched: () => void;
8482
get activeOption(): MatOption | null;
8583
autocomplete: MatAutocomplete;
8684
autocompleteAttribute: string;

0 commit comments

Comments
 (0)