mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(autocomplete): unable to click to select items in IE (#3188)
Fixes #3351.
This commit is contained in:
committed by
Kara
parent
fbfd032c6e
commit
78985b711e
@@ -1,15 +1,18 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
forwardRef,
|
||||
Host,
|
||||
Input,
|
||||
NgZone,
|
||||
Optional,
|
||||
OnDestroy,
|
||||
ViewContainerRef,
|
||||
Directive,
|
||||
ElementRef,
|
||||
forwardRef,
|
||||
Host,
|
||||
Input,
|
||||
NgZone,
|
||||
Optional,
|
||||
OnDestroy,
|
||||
ViewContainerRef,
|
||||
Inject,
|
||||
ChangeDetectorRef,
|
||||
} from '@angular/core';
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
|
||||
import {DOCUMENT} from '@angular/platform-browser';
|
||||
import {Overlay, OverlayRef, OverlayState, TemplatePortal} from '../core';
|
||||
import {MdAutocomplete} from './autocomplete';
|
||||
import {PositionStrategy} from '../core/overlay/position/position-strategy';
|
||||
@@ -18,12 +21,13 @@ import {Observable} from 'rxjs/Observable';
|
||||
import {MdOptionSelectionChange, MdOption} from '../core/option/option';
|
||||
import {ENTER, UP_ARROW, DOWN_ARROW} from '../core/keyboard/keycodes';
|
||||
import {Dir} from '../core/rtl/dir';
|
||||
import {MdInputContainer} from '../input/input-container';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
import 'rxjs/add/observable/merge';
|
||||
import 'rxjs/add/observable/fromEvent';
|
||||
import 'rxjs/add/operator/filter';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
import {MdInputContainer} from '../input/input-container';
|
||||
|
||||
/**
|
||||
* The following style constants are necessary to save here in order
|
||||
@@ -58,8 +62,8 @@ export const MD_AUTOCOMPLETE_VALUE_ACCESSOR: any = {
|
||||
'[attr.aria-expanded]': 'panelOpen.toString()',
|
||||
'[attr.aria-owns]': 'autocomplete?.id',
|
||||
'(focus)': 'openPanel()',
|
||||
'(blur)': '_handleBlur($event.relatedTarget?.tagName)',
|
||||
'(input)': '_handleInput($event)',
|
||||
'(blur)': '_onTouched()',
|
||||
'(keydown)': '_handleKeydown($event)',
|
||||
},
|
||||
providers: [MD_AUTOCOMPLETE_VALUE_ACCESSOR]
|
||||
@@ -74,9 +78,6 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
|
||||
private _positionStrategy: ConnectedPositionStrategy;
|
||||
|
||||
/** Stream of blur events that should close the panel. */
|
||||
private _blurStream = new Subject<any>();
|
||||
|
||||
/** Whether or not the placeholder state is being overridden. */
|
||||
private _manuallyFloatingPlaceholder = false;
|
||||
|
||||
@@ -101,8 +102,10 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
|
||||
constructor(private _element: ElementRef, private _overlay: Overlay,
|
||||
private _viewContainerRef: ViewContainerRef,
|
||||
private _changeDetectorRef: ChangeDetectorRef,
|
||||
@Optional() private _dir: Dir, private _zone: NgZone,
|
||||
@Optional() @Host() private _inputContainer: MdInputContainer) {}
|
||||
@Optional() @Host() private _inputContainer: MdInputContainer,
|
||||
@Optional() @Inject(DOCUMENT) private _document: any) {}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this._panelPositionSubscription) {
|
||||
@@ -144,6 +147,12 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
|
||||
this._panelOpen = false;
|
||||
this._resetPlaceholder();
|
||||
|
||||
// We need to trigger change detection manually, because
|
||||
// `fromEvent` doesn't seem to do it at the proper time.
|
||||
// This ensures that the placeholder is reset when the
|
||||
// user clicks outside.
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,9 +161,9 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
*/
|
||||
get panelClosingActions(): Observable<MdOptionSelectionChange> {
|
||||
return Observable.merge(
|
||||
this.optionSelections,
|
||||
this._blurStream.asObservable(),
|
||||
this.autocomplete._keyManager.tabOut
|
||||
this.optionSelections,
|
||||
this.autocomplete._keyManager.tabOut,
|
||||
this._outsideClickStream
|
||||
);
|
||||
}
|
||||
|
||||
@@ -170,6 +179,18 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/** Stream of clicks outside of the autocomplete panel. */
|
||||
private get _outsideClickStream(): Observable<any> {
|
||||
if (this._document) {
|
||||
return Observable.fromEvent(this._document, 'click').filter((event: MouseEvent) => {
|
||||
let clickTarget = event.target as HTMLElement;
|
||||
return this._panelOpen &&
|
||||
!this._inputContainer._elementRef.nativeElement.contains(clickTarget) &&
|
||||
!this._overlayRef.overlayElement.contains(clickTarget);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the autocomplete's value. Part of the ControlValueAccessor interface
|
||||
* required to integrate with Angular's core forms API.
|
||||
@@ -225,15 +246,6 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
_handleBlur(newlyFocusedTag: string): void {
|
||||
this._onTouched();
|
||||
|
||||
// Only emit blur event if the new focus is *not* on an option.
|
||||
if (newlyFocusedTag !== 'MD-OPTION') {
|
||||
this._blurStream.next(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In "auto" mode, the placeholder will animate down as soon as focus is lost.
|
||||
* This causes the value to jump when selecting an option with the mouse.
|
||||
@@ -307,7 +319,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
* stemmed from the user.
|
||||
*/
|
||||
private _setValueAndClose(event: MdOptionSelectionChange | null): void {
|
||||
if (event) {
|
||||
if (event && event.source) {
|
||||
this._clearPreviousSelectedOption(event.source);
|
||||
this._setTriggerValue(event.source.value);
|
||||
this._onChange(event.source.value);
|
||||
|
||||
@@ -130,13 +130,12 @@ describe('MdAutocomplete', () => {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should close the panel when blurred', async(() => {
|
||||
it('should close the panel when input loses focus', async(() => {
|
||||
dispatchFakeEvent(input, 'focus');
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
dispatchFakeEvent(input, 'blur');
|
||||
fixture.detectChanges();
|
||||
dispatchFakeEvent(document, 'click');
|
||||
|
||||
expect(fixture.componentInstance.trigger.panelOpen)
|
||||
.toBe(false, `Expected clicking outside the panel to set its state to closed.`);
|
||||
|
||||
@@ -40,7 +40,7 @@ export function createKeyboardEvent(type: string, keyCode: number) {
|
||||
|
||||
/** Creates a fake event object with any desired event type. */
|
||||
export function createFakeEvent(type: string) {
|
||||
let event = document.createEvent('Event');
|
||||
let event = document.createEvent('Event');
|
||||
event.initEvent(type, true, true);
|
||||
return event;
|
||||
}
|
||||
|
||||
@@ -339,6 +339,7 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit {
|
||||
@ContentChildren(MdSuffix) _suffixChildren: QueryList<MdSuffix>;
|
||||
|
||||
constructor(
|
||||
public _elementRef: ElementRef,
|
||||
private _changeDetectorRef: ChangeDetectorRef,
|
||||
@Optional() private _parentForm: NgForm,
|
||||
@Optional() private _parentFormGroup: FormGroupDirective) { }
|
||||
|
||||
Reference in New Issue
Block a user