Skip to content

Commit 00c37d2

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 d22f48c commit 00c37d2

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
@@ -735,15 +735,48 @@ describe('MatAutocomplete', () => {
735735
fixture.componentInstance.trigger.openPanel();
736736
fixture.detectChanges();
737737
expect(fixture.componentInstance.stateCtrl.touched)
738-
.toBe(false, `Expected control to start out untouched.`);
738+
.toBe(false, 'Expected control to start out untouched.');
739739

740740
dispatchFakeEvent(input, 'blur');
741741
fixture.detectChanges();
742+
fixture.componentInstance.trigger.closePanel();
743+
fixture.detectChanges();
742744

743745
expect(fixture.componentInstance.stateCtrl.touched)
744-
.toBe(true, `Expected control to become touched on blur.`);
746+
.toBe(true, 'Expected control to become touched on blur.');
745747
});
746748

749+
it('should mark the autocomplete control as touched on blur if the panel is closed', () => {
750+
expect(fixture.componentInstance.stateCtrl.touched)
751+
.toBe(false, 'Expected control to start out untouched.');
752+
753+
dispatchFakeEvent(input, 'blur');
754+
fixture.detectChanges();
755+
756+
expect(fixture.componentInstance.stateCtrl.touched)
757+
.toBe(true, 'Expected control to become touched on blur.');
758+
});
759+
760+
it('should not mark the autocomplete control as touched if the input was blurred while ' +
761+
'the panel is open', () => {
762+
fixture.componentInstance.trigger.openPanel();
763+
fixture.detectChanges();
764+
expect(fixture.componentInstance.stateCtrl.touched)
765+
.toBe(false, 'Expected control to start out untouched.');
766+
767+
dispatchFakeEvent(input, 'blur');
768+
fixture.detectChanges();
769+
770+
expect(fixture.componentInstance.stateCtrl.touched)
771+
.toBe(false, 'Expected control to remain untouched.');
772+
773+
fixture.componentInstance.trigger.closePanel();
774+
fixture.detectChanges();
775+
776+
expect(fixture.componentInstance.stateCtrl.touched)
777+
.toBe(true, 'Expected control to be touched once the panel is closed.');
778+
});
779+
747780
it('should disable the input when used with a value accessor and without `matInput`', () => {
748781
overlayContainer.ngOnDestroy();
749782
fixture.destroy();

0 commit comments

Comments
 (0)