From a4403f76cc0e226c06c8dd5fcb5e6ca98fb44464 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 11 Jul 2023 14:01:09 +0200 Subject: [PATCH] feat(material/autocomplete): add input to require selection from the panel Adds the `requireSelection` input to the autocomplete, which when enabled will clear the input value if the user doesn't select an option from the list. Fixes #3334. --- ...autocomplete-require-selection-example.css | 9 + ...utocomplete-require-selection-example.html | 18 ++ .../autocomplete-require-selection-example.ts | 45 +++++ .../material/autocomplete/index.ts | 1 + .../autocomplete/autocomplete-demo.html | 19 +- src/dev-app/autocomplete/autocomplete-demo.ts | 4 +- .../autocomplete/autocomplete-trigger.ts | 46 ++++- src/material/autocomplete/autocomplete.html | 1 + src/material/autocomplete/autocomplete.md | 28 ++- .../autocomplete/autocomplete.spec.ts | 173 +++++++++++++++++- src/material/autocomplete/autocomplete.ts | 35 +++- .../legacy-autocomplete/autocomplete.ts | 1 + .../public_api_guard/material/autocomplete.md | 13 +- .../material/legacy-autocomplete.md | 2 + 14 files changed, 369 insertions(+), 26 deletions(-) create mode 100644 src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.css create mode 100644 src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.html create mode 100644 src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.ts diff --git a/src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.css b/src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.css new file mode 100644 index 000000000000..08fa67536b1f --- /dev/null +++ b/src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.css @@ -0,0 +1,9 @@ +.example-form { + min-width: 150px; + max-width: 500px; + width: 100%; +} + +.example-full-width { + width: 100%; +} diff --git a/src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.html b/src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.html new file mode 100644 index 000000000000..6b428b0b45c9 --- /dev/null +++ b/src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.html @@ -0,0 +1,18 @@ + + + Number + + + + {{option}} + + + + + +Control value: {{myControl.value}} diff --git a/src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.ts b/src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.ts new file mode 100644 index 000000000000..a93619ae3748 --- /dev/null +++ b/src/components-examples/material/autocomplete/autocomplete-require-selection/autocomplete-require-selection-example.ts @@ -0,0 +1,45 @@ +import {Component, OnInit} from '@angular/core'; +import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {Observable} from 'rxjs'; +import {map, startWith} from 'rxjs/operators'; +import {NgFor, AsyncPipe} from '@angular/common'; +import {MatAutocompleteModule} from '@angular/material/autocomplete'; +import {MatInputModule} from '@angular/material/input'; +import {MatFormFieldModule} from '@angular/material/form-field'; + +/** + * @title Require an autocomplete option to be selected. + */ +@Component({ + selector: 'autocomplete-require-selection-example', + templateUrl: 'autocomplete-require-selection-example.html', + styleUrls: ['autocomplete-require-selection-example.css'], + standalone: true, + imports: [ + FormsModule, + MatFormFieldModule, + MatInputModule, + MatAutocompleteModule, + ReactiveFormsModule, + NgFor, + AsyncPipe, + ], +}) +export class AutocompleteRequireSelectionExample implements OnInit { + myControl = new FormControl(''); + options: string[] = ['One', 'Two', 'Three', 'Three', 'Four']; + filteredOptions: Observable; + + ngOnInit() { + this.filteredOptions = this.myControl.valueChanges.pipe( + startWith(''), + map(value => this._filter(value || '')), + ); + } + + private _filter(value: string): string[] { + const filterValue = value.toLowerCase(); + + return this.options.filter(option => option.toLowerCase().includes(filterValue)); + } +} diff --git a/src/components-examples/material/autocomplete/index.ts b/src/components-examples/material/autocomplete/index.ts index 252c88070069..18aad314a477 100644 --- a/src/components-examples/material/autocomplete/index.ts +++ b/src/components-examples/material/autocomplete/index.ts @@ -5,4 +5,5 @@ export {AutocompleteOptgroupExample} from './autocomplete-optgroup/autocomplete- export {AutocompleteOverviewExample} from './autocomplete-overview/autocomplete-overview-example'; export {AutocompletePlainInputExample} from './autocomplete-plain-input/autocomplete-plain-input-example'; export {AutocompleteSimpleExample} from './autocomplete-simple/autocomplete-simple-example'; +export {AutocompleteRequireSelectionExample} from './autocomplete-require-selection/autocomplete-require-selection-example'; export {AutocompleteHarnessExample} from './autocomplete-harness/autocomplete-harness-example'; diff --git a/src/dev-app/autocomplete/autocomplete-demo.html b/src/dev-app/autocomplete/autocomplete-demo.html index 2502c1584c65..e2d04e637bd8 100644 --- a/src/dev-app/autocomplete/autocomplete-demo.html +++ b/src/dev-app/autocomplete/autocomplete-demo.html @@ -10,9 +10,11 @@ State - + [autoActiveFirstOption]="reactiveAutoActiveFirstOption" + [requireSelection]="reactiveRequireSelection"> {{ state.name }} @@ -45,6 +47,11 @@ Automatically activate first option
+ + Require Selection + +
Disable States diff --git a/src/dev-app/autocomplete/autocomplete-demo.ts b/src/dev-app/autocomplete/autocomplete-demo.ts index bb1f6ff02c68..d43a358ad72d 100644 --- a/src/dev-app/autocomplete/autocomplete-demo.ts +++ b/src/dev-app/autocomplete/autocomplete-demo.ts @@ -59,7 +59,6 @@ export class AutocompleteDemo { tdStates: State[]; tdDisabled = false; - hideSingleSelectionIndicators = false; reactiveStatesTheme: ThemePalette = 'primary'; templateStatesTheme: ThemePalette = 'primary'; @@ -69,6 +68,9 @@ export class AutocompleteDemo { {value: 'warn', name: 'Warn'}, ]; + reactiveRequireSelection = false; + templateRequireSelection = false; + reactiveHideSingleSelectionIndicator = false; templateHideSingleSelectionIndicator = false; diff --git a/src/material/autocomplete/autocomplete-trigger.ts b/src/material/autocomplete/autocomplete-trigger.ts index 52d08ca44019..c5bb5d9eda44 100644 --- a/src/material/autocomplete/autocomplete-trigger.ts +++ b/src/material/autocomplete/autocomplete-trigger.ts @@ -110,6 +110,9 @@ export abstract class _MatAutocompleteTriggerBase /** Old value of the native input. Used to work around issues with the `input` event on IE. */ private _previousValue: string | number | null; + /** Value of the input element when the panel was opened. */ + private _valueOnOpen: string | number | null; + /** Strategy that is used to position the panel. */ private _positionStrategy: FlexibleConnectedPositionStrategy; @@ -561,7 +564,7 @@ export abstract class _MatAutocompleteTriggerBase // of the available options, // - if a valid string is entered after an invalid one. if (this.panelOpen) { - this.autocomplete.opened.emit(); + this._emitOpened(); } else { this.autocomplete.closed.emit(); } @@ -578,6 +581,15 @@ export abstract class _MatAutocompleteTriggerBase ); } + /** + * Emits the opened event once it's known that the panel will be shown and stores + * the state of the trigger right before the opening sequence was finished. + */ + private _emitOpened() { + this._valueOnOpen = this._element.nativeElement.value; + this.autocomplete.opened.emit(); + } + /** Destroys the autocomplete suggestion panel. */ private _destroyPanel(): void { if (this._overlayRef) { @@ -616,14 +628,28 @@ export abstract class _MatAutocompleteTriggerBase * stemmed from the user. */ private _setValueAndClose(event: MatOptionSelectionChange | null): void { + const panel = this.autocomplete; const toSelect = event ? event.source : this._pendingAutoselectedOption; if (toSelect) { this._clearPreviousSelectedOption(toSelect); this._assignOptionValue(toSelect.value); + // TODO(crisbeto): this should wait until the animation is done, otherwise the value + // gets reset while the panel is still animating which looks glitchy. It'll likely break + // some tests to change it at this point. this._onChange(toSelect.value); - this.autocomplete._emitSelectEvent(toSelect); + panel._emitSelectEvent(toSelect); this._element.nativeElement.focus(); + } else if (panel.requireSelection && this._element.nativeElement.value !== this._valueOnOpen) { + this._clearPreviousSelectedOption(null); + this._assignOptionValue(null); + // Wait for the animation to finish before clearing the form control value, otherwise + // the options might change while the animation is running which looks glitchy. + if (panel._animationDone) { + panel._animationDone.pipe(take(1)).subscribe(() => this._onChange(null)); + } else { + this._onChange(null); + } } this.closePanel(); @@ -633,13 +659,13 @@ export abstract class _MatAutocompleteTriggerBase * Clear any previous selected option and emit a selection change event for this option */ private _clearPreviousSelectedOption(skip: _MatOptionBase | null, emitEvent?: boolean) { - if (this.autocomplete && this.autocomplete.options) { - this.autocomplete.options.forEach(option => { - if (option !== skip && option.selected) { - option.deselect(emitEvent); - } - }); - } + // Null checks are necessary here, because the autocomplete + // or its options may not have been assigned yet. + this.autocomplete?.options?.forEach(option => { + if (option !== skip && option.selected) { + option.deselect(emitEvent); + } + }); } private _attachOverlay(): void { @@ -683,7 +709,7 @@ export abstract class _MatAutocompleteTriggerBase // We need to do an extra `panelOpen` check in here, because the // autocomplete won't be shown if there are no options. if (this.panelOpen && wasOpen !== this.panelOpen) { - this.autocomplete.opened.emit(); + this._emitOpened(); } } diff --git a/src/material/autocomplete/autocomplete.html b/src/material/autocomplete/autocomplete.html index da358691b7b2..cd4afb7f4a33 100644 --- a/src/material/autocomplete/autocomplete.html +++ b/src/material/autocomplete/autocomplete.html @@ -7,6 +7,7 @@ [attr.aria-label]="ariaLabel || null" [attr.aria-labelledby]="_getPanelAriaLabelledby(formFieldId)" [@panelAnimation]="isOpen ? 'visible' : 'hidden'" + (@panelAnimation.done)="_animationDone.next($event)" #panel> diff --git a/src/material/autocomplete/autocomplete.md b/src/material/autocomplete/autocomplete.md index a9c142789d4c..48fdcd9070e4 100644 --- a/src/material/autocomplete/autocomplete.md +++ b/src/material/autocomplete/autocomplete.md @@ -7,11 +7,11 @@ defined by a `mat-option` tag. Set each option's value property to whatever you' of the text input to be when that option is selected. -Next, create the input and set the `matAutocomplete` input to refer to the template reference we assigned -to the autocomplete. Let's assume you're using the `formControl` directive from `ReactiveFormsModule` to +Next, create the input and set the `matAutocomplete` input to refer to the template reference we assigned +to the autocomplete. Let's assume you're using the `formControl` directive from `ReactiveFormsModule` to track the value of the input. > Note: It is possible to use template-driven forms instead, if you prefer. We use reactive forms @@ -25,7 +25,7 @@ panel instance into a local template variable (here we called it "auto"), and bi to the input's `matAutocomplete` property. ### Adding a custom filter @@ -61,6 +61,22 @@ desired display value. Then bind it to the autocomplete's `displayWith` property +### Require an option to be selected + +By default, the autocomplete will accept the value that the user typed into the input field. +Instead, if you want to instead ensure that an option from the autocomplete was selected, you can +enable the `requireSelection` input on `mat-autocomplete`. This will change the behavior of +the autocomplete in the following ways: +1. If the user opens the autocomplete, changes its value, but doesn't select anything, the +autocomplete value will be reset back to `null`. +2. If the user opens and closes the autocomplete without changing the value, the old value will +be preserved. + +This behavior can be configured globally using the `MAT_AUTOCOMPLETE_DEFAULT_OPTIONS` +injection token. + + + ### Automatically highlighting the first option If your use case requires for the first autocomplete option to be highlighted when the user opens @@ -112,7 +128,7 @@ autocomplete is attached to using the `matAutocompleteOrigin` directive together ### Option groups `mat-option` can be collected into groups using the `mat-optgroup` element: ### Accessibility @@ -132,4 +148,4 @@ navigation though the autocomplete options. By default, `MatAutocomplete` displays a checkmark to identify the selected item. While you can hide the checkmark indicator via `hideSingleSelectionIndicator`, this makes the component less accessible -by making it harder or impossible for users to visually identify selected items. \ No newline at end of file +by making it harder or impossible for users to visually identify selected items. diff --git a/src/material/autocomplete/autocomplete.spec.ts b/src/material/autocomplete/autocomplete.spec.ts index 4e2f4b885a40..a9a193d5ba3b 100644 --- a/src/material/autocomplete/autocomplete.spec.ts +++ b/src/material/autocomplete/autocomplete.spec.ts @@ -2504,6 +2504,165 @@ describe('MDC-based MatAutocomplete', () => { subscription.unsubscribe(); })); + + it('should accept the user selection if they click on an option while selection is required', fakeAsync(() => { + const input = fixture.nativeElement.querySelector('input'); + const {stateCtrl, trigger, states} = fixture.componentInstance; + fixture.componentInstance.requireSelection = true; + stateCtrl.setValue(states[1]); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe('California'); + expect(stateCtrl.value).toEqual({code: 'CA', name: 'California'}); + + trigger.openPanel(); + fixture.detectChanges(); + zone.simulateZoneExit(); + + const options = overlayContainerElement.querySelectorAll( + 'mat-option', + ) as NodeListOf; + const spy = jasmine.createSpy('optionSelected spy'); + const subscription = trigger.optionSelections.subscribe(spy); + + options[5].click(); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe('Oregon'); + expect(stateCtrl.value).toEqual({code: 'OR', name: 'Oregon'}); + expect(spy).toHaveBeenCalledTimes(1); + + subscription.unsubscribe(); + })); + + it('should accept the user selection if they press enter on an option while selection is required', fakeAsync(() => { + const input = fixture.nativeElement.querySelector('input'); + const {stateCtrl, trigger, states} = fixture.componentInstance; + fixture.componentInstance.requireSelection = true; + stateCtrl.setValue(states[1]); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe('California'); + expect(stateCtrl.value).toEqual({code: 'CA', name: 'California'}); + + trigger.openPanel(); + fixture.detectChanges(); + zone.simulateZoneExit(); + + const options = overlayContainerElement.querySelectorAll( + 'mat-option', + ) as NodeListOf; + const spy = jasmine.createSpy('optionSelected spy'); + const subscription = trigger.optionSelections.subscribe(spy); + + dispatchKeyboardEvent(options[5], 'keydown', ENTER); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe('Oregon'); + expect(stateCtrl.value).toEqual({code: 'OR', name: 'Oregon'}); + expect(spy).toHaveBeenCalledTimes(1); + + subscription.unsubscribe(); + })); + + it('should accept the user selection if autoSelectActiveOption is enabled', fakeAsync(() => { + const input = fixture.nativeElement.querySelector('input'); + const {stateCtrl, trigger, states} = fixture.componentInstance; + fixture.componentInstance.requireSelection = true; + trigger.autocomplete.autoSelectActiveOption = true; + stateCtrl.setValue(states[1]); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe('California'); + expect(stateCtrl.value).toEqual({code: 'CA', name: 'California'}); + + trigger.openPanel(); + fixture.detectChanges(); + zone.simulateZoneExit(); + + for (let i = 0; i < 5; i++) { + dispatchKeyboardEvent(input, 'keydown', DOWN_ARROW); + fixture.detectChanges(); + } + + dispatchFakeEvent(document, 'click'); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe('New York'); + expect(stateCtrl.value).toEqual({code: 'NY', name: 'New York'}); + })); + + it('should clear the value if selection is required and the user interacted with the panel without selecting anything', fakeAsync(() => { + const input = fixture.nativeElement.querySelector('input'); + const {stateCtrl, trigger, states} = fixture.componentInstance; + fixture.componentInstance.requireSelection = true; + stateCtrl.setValue(states[1]); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe('California'); + expect(stateCtrl.value).toEqual({code: 'CA', name: 'California'}); + + trigger.openPanel(); + fixture.detectChanges(); + zone.simulateZoneExit(); + + const spy = jasmine.createSpy('optionSelected spy'); + const subscription = trigger.optionSelections.subscribe(spy); + + input.value = 'Cali'; + dispatchKeyboardEvent(input, 'input'); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe('Cali'); + expect(stateCtrl.value).toBe('Cali'); + expect(spy).not.toHaveBeenCalled(); + + dispatchFakeEvent(document, 'click'); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe(''); + expect(stateCtrl.value).toBe(null); + expect(spy).not.toHaveBeenCalled(); + + subscription.unsubscribe(); + })); + + it('should preserve the value if a selection is required, but the user opened and closed the panel without interacting with it', fakeAsync(() => { + const input = fixture.nativeElement.querySelector('input'); + const {stateCtrl, trigger, states} = fixture.componentInstance; + fixture.componentInstance.requireSelection = true; + stateCtrl.setValue(states[1]); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe('California'); + expect(stateCtrl.value).toEqual({code: 'CA', name: 'California'}); + + trigger.openPanel(); + fixture.detectChanges(); + zone.simulateZoneExit(); + + const spy = jasmine.createSpy('optionSelected spy'); + const subscription = trigger.optionSelections.subscribe(spy); + + dispatchFakeEvent(document, 'click'); + fixture.detectChanges(); + tick(); + + expect(input.value).toBe('California'); + expect(stateCtrl.value).toEqual({code: 'CA', name: 'California'}); + expect(spy).not.toHaveBeenCalled(); + subscription.unsubscribe(); + })); }); describe('panel closing', () => { @@ -3540,9 +3699,16 @@ const SIMPLE_AUTOCOMPLETE_TEMPLATE = ` [matAutocompleteDisabled]="autocompleteDisabled" [formControl]="stateCtrl"> - + | null; + /** Manages active item in option list based on key events. */ _keyManager: ActiveDescendantKeyManager<_MatOptionBase>; @@ -186,6 +197,21 @@ export abstract class _MatAutocompleteBase } private _autoSelectActiveOption: boolean; + /** + * Whether the user is required to make a selection when they're interacting with the + * autocomplete. If the user moves away from the autcomplete without selecting an option from + * the list, the value will be reset. If the user opens the panel and closes it without + * interacting or selecting a value, the initial value will be kept. + */ + @Input() + get requireSelection(): boolean { + return this._requireSelection; + } + set requireSelection(value: BooleanInput) { + this._requireSelection = coerceBooleanProperty(value); + } + private _requireSelection: boolean; + /** * Specify the width of the autocomplete panel. Can be any CSS sizing value, otherwise it will * match the width of its host. @@ -251,6 +277,7 @@ export abstract class _MatAutocompleteBase this.inertGroups = platform?.SAFARI || false; this._autoActiveFirstOption = !!_defaults.autoActiveFirstOption; this._autoSelectActiveOption = !!_defaults.autoSelectActiveOption; + this._requireSelection = !!_defaults.requireSelection; } ngAfterContentInit() { @@ -343,13 +370,14 @@ export abstract class _MatAutocompleteBase providers: [{provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatAutocomplete}], animations: [panelAnimation], }) -export class MatAutocomplete extends _MatAutocompleteBase { +export class MatAutocomplete extends _MatAutocompleteBase implements OnDestroy { /** Reference to all option groups within the autocomplete. */ @ContentChildren(MAT_OPTGROUP, {descendants: true}) optionGroups: QueryList; /** Reference to all options within the autocomplete. */ @ContentChildren(MatOption, {descendants: true}) options: QueryList; protected _visibleClass = 'mat-mdc-autocomplete-visible'; protected _hiddenClass = 'mat-mdc-autocomplete-hidden'; + override _animationDone = new EventEmitter(); /** Whether checkmark indicator for single-selection options is hidden. */ @Input() @@ -372,6 +400,11 @@ export class MatAutocomplete extends _MatAutocompleteBase { } } + override ngOnDestroy(): void { + super.ngOnDestroy(); + this._animationDone.complete(); + } + // `skipPredicate` determines if key manager should avoid putting a given option in the tab // order. Allow disabled list items to receive focus via keyboard to align with WAI ARIA // recommendation. diff --git a/src/material/legacy-autocomplete/autocomplete.ts b/src/material/legacy-autocomplete/autocomplete.ts index 2fca99864382..d22cd390f831 100644 --- a/src/material/legacy-autocomplete/autocomplete.ts +++ b/src/material/legacy-autocomplete/autocomplete.ts @@ -47,4 +47,5 @@ export class MatLegacyAutocomplete extends _MatAutocompleteBase { @ContentChildren(MatLegacyOption, {descendants: true}) options: QueryList; protected _visibleClass = 'mat-autocomplete-visible'; protected _hiddenClass = 'mat-autocomplete-hidden'; + override _animationDone = null; } diff --git a/tools/public_api_guard/material/autocomplete.md b/tools/public_api_guard/material/autocomplete.md index b1772f8de32a..aadcf50793a1 100644 --- a/tools/public_api_guard/material/autocomplete.md +++ b/tools/public_api_guard/material/autocomplete.md @@ -8,6 +8,7 @@ import { _AbstractConstructor } from '@angular/material/core'; import { ActiveDescendantKeyManager } from '@angular/cdk/a11y'; import { AfterContentInit } from '@angular/core'; import { AfterViewInit } from '@angular/core'; +import { AnimationEvent as AnimationEvent_2 } from '@angular/animations'; import { BooleanInput } from '@angular/cdk/coercion'; import { CanDisableRipple } from '@angular/material/core'; import { ChangeDetectorRef } from '@angular/core'; @@ -68,11 +69,15 @@ export const MAT_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY_PROVIDER: { export const MAT_AUTOCOMPLETE_VALUE_ACCESSOR: any; // @public (undocumented) -export class MatAutocomplete extends _MatAutocompleteBase { +export class MatAutocomplete extends _MatAutocompleteBase implements OnDestroy { + // (undocumented) + _animationDone: EventEmitter; // (undocumented) protected _hiddenClass: string; get hideSingleSelectionIndicator(): boolean; set hideSingleSelectionIndicator(value: BooleanInput); + // (undocumented) + ngOnDestroy(): void; optionGroups: QueryList; options: QueryList; // (undocumented) @@ -95,6 +100,7 @@ export interface MatAutocompleteActivatedEvent { // @public export abstract class _MatAutocompleteBase extends _MatAutocompleteMixinBase implements AfterContentInit, CanDisableRipple, OnDestroy { constructor(_changeDetectorRef: ChangeDetectorRef, _elementRef: ElementRef, _defaults: MatAutocompleteDefaultOptions, platform?: Platform); + abstract _animationDone: EventEmitter | null; ariaLabel: string; ariaLabelledby: string; get autoActiveFirstOption(): boolean; @@ -131,6 +137,8 @@ export abstract class _MatAutocompleteBase extends _MatAutocompleteMixinBase imp readonly optionSelected: EventEmitter; panel: ElementRef; panelWidth: string | number; + get requireSelection(): boolean; + set requireSelection(value: BooleanInput); _setColor(value: ThemePalette): void; _setScrollTop(scrollTop: number): void; _setVisibility(): void; @@ -140,7 +148,7 @@ export abstract class _MatAutocompleteBase extends _MatAutocompleteMixinBase imp template: TemplateRef; protected abstract _visibleClass: string; // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration<_MatAutocompleteBase, never, never, { "ariaLabel": { "alias": "aria-label"; "required": false; }; "ariaLabelledby": { "alias": "aria-labelledby"; "required": false; }; "displayWith": { "alias": "displayWith"; "required": false; }; "autoActiveFirstOption": { "alias": "autoActiveFirstOption"; "required": false; }; "autoSelectActiveOption": { "alias": "autoSelectActiveOption"; "required": false; }; "panelWidth": { "alias": "panelWidth"; "required": false; }; "classList": { "alias": "class"; "required": false; }; }, { "optionSelected": "optionSelected"; "opened": "opened"; "closed": "closed"; "optionActivated": "optionActivated"; }, never, never, false, never>; + static ɵdir: i0.ɵɵDirectiveDeclaration<_MatAutocompleteBase, never, never, { "ariaLabel": { "alias": "aria-label"; "required": false; }; "ariaLabelledby": { "alias": "aria-labelledby"; "required": false; }; "displayWith": { "alias": "displayWith"; "required": false; }; "autoActiveFirstOption": { "alias": "autoActiveFirstOption"; "required": false; }; "autoSelectActiveOption": { "alias": "autoSelectActiveOption"; "required": false; }; "requireSelection": { "alias": "requireSelection"; "required": false; }; "panelWidth": { "alias": "panelWidth"; "required": false; }; "classList": { "alias": "class"; "required": false; }; }, { "optionSelected": "optionSelected"; "opened": "opened"; "closed": "closed"; "optionActivated": "optionActivated"; }, never, never, false, never>; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration<_MatAutocompleteBase, never>; } @@ -151,6 +159,7 @@ export interface MatAutocompleteDefaultOptions { autoSelectActiveOption?: boolean; hideSingleSelectionIndicator?: boolean; overlayPanelClass?: string | string[]; + requireSelection?: boolean; } // @public (undocumented) diff --git a/tools/public_api_guard/material/legacy-autocomplete.md b/tools/public_api_guard/material/legacy-autocomplete.md index 28e9bbf9d226..d3a762afd2cf 100644 --- a/tools/public_api_guard/material/legacy-autocomplete.md +++ b/tools/public_api_guard/material/legacy-autocomplete.md @@ -43,6 +43,8 @@ export const MAT_LEGACY_AUTOCOMPLETE_VALUE_ACCESSOR: any; // @public @deprecated (undocumented) export class MatLegacyAutocomplete extends _MatAutocompleteBase { + // (undocumented) + _animationDone: null; // (undocumented) protected _hiddenClass: string; optionGroups: QueryList;