Ok here we go....
Let start with a little StackBlitz demo.
Note that you should also focus on the selected element when someone uses the keydown from the input, which I didn't implement as it is a bit beyond the scope of the question. Overall I would recommend using the default behaviour, but since you asked..
The css
In the html is a div
you can recognise by its role='listbox'
. This div
contains the mat-option
elements. When the mat-option
elements don't fit in the div
the div will add a scrollbar with overflow: auto
. So we just have to set the scrollTop
value on the div to scroll.
How to get the element
Get the div
through a property of the autocomplete object called panel
. In order to do that get the autocomplete object and reference it using @ViewChild()
.
Calculate the value to set on scrollTop
To calculate the value, get the height of the mat-option
. The default is 48, so you could just set that. You should be able to get the height from the AUTOCOMPLETE_OPTION_HEIGHT
.
Note: I wasn't able to get visible results from modifying this value. Maybe I did something wrong. Or there could be something going on why modifying this constant has no real effects. So I just set it to the default value of 48.
To get the correct scrollTop
value, calculate it by using the index of the matched element.
Add the logic with a method call
Call this logic with a method that detects a change in value on the input: (input)='changed_input()
.
Below my code
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {AUTOCOMPLETE_OPTION_HEIGHT} from '@angular/material/autocomplete';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatAutocompleteModule
],
declarations: [ AppComponent, HelloComponent ],
bootstrap: [ AppComponent ],
providers: [
{provide: AUTOCOMPLETE_OPTION_HEIGHT, useValue: 48 }
]
})
export class AppModule { }
app.component.html
<div [formGroup]="testForm">
<mat-form-field [appearance]="'outline'">
<mat-label>Select color</mat-label>
<input type="text" matInput [(ngModel)]="color"
(input)='changed_input()'formControlName="color" [matAutocomplete]="colorOptions">
<i class="icon-caret-down select-arrow" matSuffix></i>
<mat-hint>Select or type a color</mat-hint>
</mat-form-field>
<mat-autocomplete
#matAutocomplete #colorOptions="matAutocomplete">
<mat-option *ngFor="let option of colors; let i=index" [value]="option">
{{option}}
</mat-option>
</mat-autocomplete>
</div>
app.component.ts
import { Component, VERSION, ViewChild, Inject } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import {AUTOCOMPLETE_OPTION_HEIGHT} from '@angular/material/autocomplete';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
@ViewChild('matAutocomplete') matAutocomplete;
color = '';
colors = ['Red', 'Green', 'Blue', 'Yellow', 'Orange', 'White', 'Black', 'Purple', 'Grey', 'Brown'];
testForm: FormGroup;
constructor(
@Inject(AUTOCOMPLETE_OPTION_HEIGHT) public option_height: number
) {}
ngOnInit(){
this.testForm = new FormGroup({
color: new FormControl('')
})
}
public changed_input(): void {
const color_index = this.colors.findIndex( color_option => {
return color_option.toLowerCase() === this.color.toLowerCase();
});
if(color_index === -1 ) return;
this.matAutocomplete.panel.nativeElement.scrollTop = this.option_height*color_index;
}
}
A final note
This is all fun and giggles to play with, but seriously just use the default behaviour and save your future self some misery.