It's quite weird, why such a needed option like the height of item (option) isn't defined as input.
Because without changing of AUTOCOMPLETE_OPTION_HEIGHT we will get a bug (https://github.com/angular/components/issues/18030)
Here is a possible solution:
Step 1. Change style.
It's the easiest moment. We can just change the global style. Let's use 24px instead 48px
mat-option.mat-option{
position: relative;
height: 24px!important;
line-height: 24px!important;
max-height: 24px!important;
}
Step 2. Overwrite original _scrollToOption method of MatAutocompleteTrigger
The pain is located here: https://github.com/angular/components/blob/49a1324acc05cec1c5ff28d729abfe590f6772dd/src/material/autocomplete/autocomplete-trigger.ts line 498, method _scrollToOption.
This method takes two hardcoded constants (AUTOCOMPLETE_OPTION_HEIGHT, AUTOCOMPLETE_PANEL_HEIGHT) and uses it to calculate new scroll position. Sure, if you have not 48px, but less or more - it will work incorrectly.
So, we have to overwrite this method.
The most elegant way to do it - using Directive (keep in mind, we can get access with @ViewChild() to MatAutocompleteTrigger and change it in the scope of a parent component, but it's not a reusable solution).
Step 2.1. Create directive
import { Directive } from '@angular/core';
import { Host, Self, Optional, Input, OnInit } from '@angular/core';
import { MatAutocompleteTrigger, AUTOCOMPLETE_OPTION_HEIGHT, AUTOCOMPLETE_PANEL_HEIGHT } from '@angular/material';
import {
_countGroupLabelsBeforeOption,
_getOptionScrollPosition
} from '@angular/material/core';
@Directive({
selector: '[matAutocompleteTriggerAccessor]',
})
export class DirectiveAccessor implements OnInit {
@Input() optionHeight: number = AUTOCOMPLETE_OPTION_HEIGHT;
@Input() panelHeight: number = AUTOCOMPLETE_PANEL_HEIGHT;
constructor(
@Host() @Self() @Optional() public _refTrigger: MatAutocompleteTrigger
) { }
public ngOnInit() {
if (this._refTrigger === undefined || this._refTrigger === null) {
return;
}
this._refTrigger['_scrollToOption'] = this._scrollToOption.bind(this._refTrigger, this.optionHeight, this.panelHeight);
}
private _scrollToOption(
this: MatAutocompleteTrigger,
optionHeight: number,
panelHeight: number,
): void {
const index = this.autocomplete._keyManager.activeItemIndex || 0;
const labelCount = _countGroupLabelsBeforeOption(
index,
this.autocomplete.options,
this.autocomplete.optionGroups
);
if (index === 0 && labelCount === 1) {
this.autocomplete._setScrollTop(0);
} else {
const newScrollPosition = _getOptionScrollPosition(
index + labelCount,
optionHeight,
this.autocomplete._getScrollTop(),
panelHeight
);
this.autocomplete._setScrollTop(newScrollPosition);
}
}
}
- So, we use "original" code of
_scrollToOption method. Only one thing we do - avoid using constants: AUTOCOMPLETE_OPTION_HEIGHT, AUTOCOMPLETE_PANEL_HEIGHT
- instead these constants we are adding two inputs
optionHeight and panelHeight
- as defaults values for new inputs, we are using values of
AUTOCOMPLETE_OPTION_HEIGHT, AUTOCOMPLETE_PANEL_HEIGHT
Step 2.2. Add directive and addition inputs
Now we can modify our template and add a new directive and addition inputs.
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto" matAutocompleteTriggerAccessor [optionHeight]="24">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of options" [value]="option">
{{option}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
Note. Our directive matAutocompleteTriggerAccessor and input optionHeight should be attached to <input>, but not to <mat-autocomplete>.
And that's all. Now all works as it should.
Here is a full solution: https://stackblitz.com/edit/angular-cn4ox8