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 index 08fa67536b1f..951aee759961 100644 --- 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 @@ -2,6 +2,7 @@ min-width: 150px; max-width: 500px; width: 100%; + margin-top: 16px; } .example-full-width { 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 index 6b428b0b45c9..e5c96b37e9d3 100644 --- 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 @@ -1,18 +1,20 @@ +Control value: {{myControl.value || 'empty'}} +
Number - + [matAutocomplete]="auto" + (input)="filter()" + (focus)="filter()"> - + {{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 index a93619ae3748..5da4fc881088 100644 --- 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 @@ -1,14 +1,12 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, ElementRef, ViewChild} 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. + * @title Require an autocomplete option to be selected */ @Component({ selector: 'autocomplete-require-selection-example', @@ -25,21 +23,18 @@ import {MatFormFieldModule} from '@angular/material/form-field'; AsyncPipe, ], }) -export class AutocompleteRequireSelectionExample implements OnInit { +export class AutocompleteRequireSelectionExample { + @ViewChild('input') input: ElementRef; myControl = new FormControl(''); - options: string[] = ['One', 'Two', 'Three', 'Three', 'Four']; - filteredOptions: Observable; + options: string[] = ['One', 'Two', 'Three', 'Four', 'Five']; + filteredOptions: string[]; - ngOnInit() { - this.filteredOptions = this.myControl.valueChanges.pipe( - startWith(''), - map(value => this._filter(value || '')), - ); + constructor() { + this.filteredOptions = this.options.slice(); } - private _filter(value: string): string[] { - const filterValue = value.toLowerCase(); - - return this.options.filter(option => option.toLowerCase().includes(filterValue)); + filter(): void { + const filterValue = this.input.nativeElement.value.toLowerCase(); + this.filteredOptions = this.options.filter(o => o.toLowerCase().includes(filterValue)); } } diff --git a/src/dev-app/autocomplete/autocomplete-demo.html b/src/dev-app/autocomplete/autocomplete-demo.html index e2d04e637bd8..27960dc3fce6 100644 --- a/src/dev-app/autocomplete/autocomplete-demo.html +++ b/src/dev-app/autocomplete/autocomplete-demo.html @@ -1,21 +1,27 @@ Space above cards:
- - Reactive length: {{ tempStates?.length }} + + Reactive length: {{ reactiveStates.length }}
Reactive value: {{ stateCtrl.value | json }}
Reactive dirty: {{ stateCtrl.dirty }}
State - + - {{ state.name }} ({{ state.code }}) diff --git a/src/dev-app/autocomplete/autocomplete-demo.ts b/src/dev-app/autocomplete/autocomplete-demo.ts index d43a358ad72d..8c87e89ae06c 100644 --- a/src/dev-app/autocomplete/autocomplete-demo.ts +++ b/src/dev-app/autocomplete/autocomplete-demo.ts @@ -14,8 +14,6 @@ import {MatButtonModule} from '@angular/material/button'; import {MatCardModule} from '@angular/material/card'; import {MatCheckboxModule} from '@angular/material/checkbox'; import {MatInputModule} from '@angular/material/input'; -import {Observable} from 'rxjs'; -import {map, startWith} from 'rxjs/operators'; import {ThemePalette} from '@angular/material/core'; import {MatDialog, MatDialogModule, MatDialogRef} from '@angular/material/dialog'; @@ -50,12 +48,12 @@ type DisableStateOption = 'none' | 'first-middle-last' | 'all'; ], }) export class AutocompleteDemo { - stateCtrl = new FormControl({code: 'CA', name: 'California'}); + stateCtrl = new FormControl(); currentState = ''; currentGroupedState = ''; topHeightCtrl = new FormControl(0); - reactiveStates: Observable; + reactiveStates: State[]; tdStates: State[]; tdDisabled = false; @@ -138,12 +136,8 @@ export class AutocompleteDemo { ].map((state, index) => ({...state, index})); constructor() { - this.tdStates = this.states; - this.reactiveStates = this.stateCtrl.valueChanges.pipe( - startWith(this.stateCtrl.value), - map(val => this.displayFn(val)), - map(name => this.filterStates(name)), - ); + this.tdStates = this.states.slice(); + this.reactiveStates = this.states.slice(); this.filteredGroupedStates = this.groupedStates = this.states.reduce( (groups, state) => { diff --git a/src/material/autocomplete/autocomplete-trigger.ts b/src/material/autocomplete/autocomplete-trigger.ts index 9a2460b75c39..b2cd6c5612e4 100644 --- a/src/material/autocomplete/autocomplete-trigger.ts +++ b/src/material/autocomplete/autocomplete-trigger.ts @@ -470,7 +470,13 @@ export abstract class _MatAutocompleteTriggerBase if (this._previousValue !== value) { this._previousValue = value; this._pendingAutoselectedOption = null; - this._onChange(value); + + // If selection is required we don't write to the CVA while the user is typing. + // At the end of the selection either the user will have picked something + // or we'll reset the value back to null. + if (!this.autocomplete || !this.autocomplete.requireSelection) { + this._onChange(value); + } if (!value) { this._clearPreviousSelectedOption(null, false); diff --git a/src/material/autocomplete/autocomplete.spec.ts b/src/material/autocomplete/autocomplete.spec.ts index ca4dd3f62773..27365fa5b76f 100644 --- a/src/material/autocomplete/autocomplete.spec.ts +++ b/src/material/autocomplete/autocomplete.spec.ts @@ -2622,7 +2622,7 @@ describe('MDC-based MatAutocomplete', () => { tick(); expect(input.value).toBe('Cali'); - expect(stateCtrl.value).toBe('Cali'); + expect(stateCtrl.value).toEqual({code: 'CA', name: 'California'}); expect(spy).not.toHaveBeenCalled(); dispatchFakeEvent(document, 'click');