Skip to content

Commit 30256f7

Browse files
committed
fix(autocomplete): marking element as touched too early when clicking on options
Currently we mark the autocomplete's CVA as touched on each `blur` event, which can happen a little too early if the user holds their pointer while clicking on an item, causing the form validation to show up too early. These changes defer marking the element as touched until the panel has closed. Fixes #13732.
1 parent d2d8a1f commit 30256f7

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from '@angular/cdk/overlay';
1818
import {TemplatePortal} from '@angular/cdk/portal';
1919
import {DOCUMENT} from '@angular/common';
20-
import {filter, take, switchMap, delay, tap, map} from 'rxjs/operators';
20+
import {filter, take, switchMap, delay, tap, map, takeUntil} from 'rxjs/operators';
2121
import {
2222
ChangeDetectorRef,
2323
Directive,
@@ -108,7 +108,7 @@ export function getMatAutocompleteMissingPanelError(): Error {
108108
// Note: we use `focusin`, as opposed to `focus`, in order to open the panel
109109
// a little earlier. This avoids issues where IE delays the focusing of the input.
110110
'(focusin)': '_handleFocus()',
111-
'(blur)': '_onTouched()',
111+
'(blur)': '_handleBlur()',
112112
'(input)': '_handleInput($event)',
113113
'(keydown)': '_handleKeydown($event)',
114114
},
@@ -422,6 +422,16 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
422422
}
423423
}
424424

425+
_handleBlur(): void {
426+
if (this.panelOpen) {
427+
this.autocomplete.closed
428+
.pipe(take(1), takeUntil(this.autocomplete.opened.asObservable()))
429+
.subscribe(() => this._onTouched());
430+
} else {
431+
this._onTouched();
432+
}
433+
}
434+
425435
/**
426436
* In "auto" mode, the label will animate down as soon as focus is lost.
427437
* This causes the value to jump when selecting an option with the mouse.

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -723,15 +723,48 @@ describe('MatAutocomplete', () => {
723723
fixture.componentInstance.trigger.openPanel();
724724
fixture.detectChanges();
725725
expect(fixture.componentInstance.stateCtrl.touched)
726-
.toBe(false, `Expected control to start out untouched.`);
726+
.toBe(false, 'Expected control to start out untouched.');
727+
728+
dispatchFakeEvent(input, 'blur');
729+
fixture.detectChanges();
730+
fixture.componentInstance.trigger.closePanel();
731+
fixture.detectChanges();
732+
733+
expect(fixture.componentInstance.stateCtrl.touched)
734+
.toBe(true, 'Expected control to become touched on blur.');
735+
});
736+
737+
it('should mark the autocomplete control as touched on blur if the panel is closed', () => {
738+
expect(fixture.componentInstance.stateCtrl.touched)
739+
.toBe(false, 'Expected control to start out untouched.');
727740

728741
dispatchFakeEvent(input, 'blur');
729742
fixture.detectChanges();
730743

731744
expect(fixture.componentInstance.stateCtrl.touched)
732-
.toBe(true, `Expected control to become touched on blur.`);
745+
.toBe(true, 'Expected control to become touched on blur.');
733746
});
734747

748+
it('should not mark the autocomplete control as touched if the input was blurred while ' +
749+
'the panel is open', () => {
750+
fixture.componentInstance.trigger.openPanel();
751+
fixture.detectChanges();
752+
expect(fixture.componentInstance.stateCtrl.touched)
753+
.toBe(false, 'Expected control to start out untouched.');
754+
755+
dispatchFakeEvent(input, 'blur');
756+
fixture.detectChanges();
757+
758+
expect(fixture.componentInstance.stateCtrl.touched)
759+
.toBe(false, 'Expected control to remain untouched.');
760+
761+
fixture.componentInstance.trigger.closePanel();
762+
fixture.detectChanges();
763+
764+
expect(fixture.componentInstance.stateCtrl.touched)
765+
.toBe(true, 'Expected control to be touched once the panel is closed.');
766+
});
767+
735768
it('should disable the input when used with a value accessor and without `matInput`', () => {
736769
overlayContainer.ngOnDestroy();
737770
fixture.destroy();

0 commit comments

Comments
 (0)