mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(autocomplete): allow autocomplete panel to be disabled (#11142)
This commit is contained in:
committed by
Joey Perrott
parent
ab1204d268
commit
e8bc0e9186
@@ -17,6 +17,7 @@ ng_module(
|
||||
"//src/cdk/keycodes",
|
||||
"//src/cdk/portal",
|
||||
"//src/cdk/overlay",
|
||||
"//src/cdk/coercion",
|
||||
],
|
||||
tsconfig = "//src/lib:tsconfig-build.json",
|
||||
)
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
import {MatFormField} from '@angular/material/form-field';
|
||||
import {Subscription, defer, fromEvent, merge, of as observableOf, Subject, Observable} from 'rxjs';
|
||||
import {MatAutocomplete} from './autocomplete';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
|
||||
|
||||
/**
|
||||
@@ -93,12 +94,12 @@ export function getMatAutocompleteMissingPanelError(): Error {
|
||||
@Directive({
|
||||
selector: `input[matAutocomplete], textarea[matAutocomplete]`,
|
||||
host: {
|
||||
'role': 'combobox',
|
||||
'autocomplete': 'off',
|
||||
'aria-autocomplete': 'list',
|
||||
'[attr.role]': 'autocompleteDisabled ? null : "combobox"',
|
||||
'[attr.aria-autocomplete]': 'autocompleteDisabled ? null : "list"',
|
||||
'[attr.aria-activedescendant]': 'activeOption?.id',
|
||||
'[attr.aria-expanded]': 'panelOpen.toString()',
|
||||
'[attr.aria-owns]': 'autocomplete?.id',
|
||||
'[attr.aria-expanded]': 'autocompleteDisabled ? null : panelOpen.toString()',
|
||||
'[attr.aria-owns]': 'autocompleteDisabled ? null : autocomplete?.id',
|
||||
// Note: we use `focusin`, as opposed to `focus`, in order to open the panel
|
||||
// a little earlier. This avoids issues where IE delays the focusing of the input.
|
||||
'(focusin)': '_handleFocus()',
|
||||
@@ -113,6 +114,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
private _overlayRef: OverlayRef | null;
|
||||
private _portal: TemplatePortal;
|
||||
private _componentDestroyed = false;
|
||||
private _autocompleteDisabled = false;
|
||||
|
||||
/** Old value of the native input. Used to work around issues with the `input` event on IE. */
|
||||
private _previousValue: string | number | null;
|
||||
@@ -141,6 +143,16 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
/** The autocomplete panel to be attached to this trigger. */
|
||||
@Input('matAutocomplete') autocomplete: MatAutocomplete;
|
||||
|
||||
/**
|
||||
* Whether the autocomplete is disabled. When disabled, the element will
|
||||
* act as a regular input and the user won't be able to open the panel.
|
||||
*/
|
||||
@Input('matAutocompleteDisabled')
|
||||
get autocompleteDisabled(): boolean { return this._autocompleteDisabled; }
|
||||
set autocompleteDisabled(value: boolean) {
|
||||
this._autocompleteDisabled = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
constructor(private _element: ElementRef, private _overlay: Overlay,
|
||||
private _viewContainerRef: ViewContainerRef,
|
||||
private _zone: NgZone,
|
||||
@@ -569,7 +581,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
/** Determines whether the panel can be opened. */
|
||||
private _canOpen(): boolean {
|
||||
const element: HTMLInputElement = this._element.nativeElement;
|
||||
return !element.readOnly && !element.disabled;
|
||||
return !element.readOnly && !element.disabled && !this._autocompleteDisabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -455,6 +455,20 @@ describe('MatAutocomplete', () => {
|
||||
expect(fixture.componentInstance.closedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not be able to open the panel if the autocomplete is disabled', () => {
|
||||
expect(fixture.componentInstance.trigger.panelOpen)
|
||||
.toBe(false, `Expected panel state to start out closed.`);
|
||||
|
||||
fixture.componentInstance.autocompleteDisabled = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
dispatchFakeEvent(input, 'focusin');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.trigger.panelOpen)
|
||||
.toBe(false, `Expected panel to remain closed.`);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should have the correct text direction in RTL', () => {
|
||||
@@ -1300,6 +1314,16 @@ describe('MatAutocomplete', () => {
|
||||
expect(document.activeElement).toBe(input, 'Expected focus to be restored to the input.');
|
||||
}));
|
||||
|
||||
it('should remove autocomplete-specific aria attributes when autocomplete is disabled', () => {
|
||||
fixture.componentInstance.autocompleteDisabled = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(input.getAttribute('role')).toBeFalsy();
|
||||
expect(input.getAttribute('aria-autocomplete')).toBeFalsy();
|
||||
expect(input.getAttribute('aria-expanded')).toBeFalsy();
|
||||
expect(input.getAttribute('aria-owns')).toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Fallback positions', () => {
|
||||
@@ -1959,7 +1983,12 @@ describe('MatAutocomplete', () => {
|
||||
@Component({
|
||||
template: `
|
||||
<mat-form-field [floatLabel]="floatLabel" [style.width.px]="width">
|
||||
<input matInput placeholder="State" [matAutocomplete]="auto" [formControl]="stateCtrl">
|
||||
<input
|
||||
matInput
|
||||
placeholder="State"
|
||||
[matAutocomplete]="auto"
|
||||
[matAutocompleteDisabled]="autocompleteDisabled"
|
||||
[formControl]="stateCtrl">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-autocomplete class="class-one class-two" #auto="matAutocomplete" [displayWith]="displayFn"
|
||||
@@ -1977,6 +2006,7 @@ class SimpleAutocomplete implements OnDestroy {
|
||||
floatLabel = 'auto';
|
||||
width: number;
|
||||
disableRipple = false;
|
||||
autocompleteDisabled = false;
|
||||
openedSpy = jasmine.createSpy('autocomplete opened spy');
|
||||
closedSpy = jasmine.createSpy('autocomplete closed spy');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user