6

What is the way to use the MAT_AUTOCOMPLETE_DEFAULT_OPTIONS injection token, or the const AUTOCOMPLETE_OPTION_HEIGHT to customize mat-autocomplete. These constants, among others are exposed in the public API here but no documentation on how to use them

Gidon
  • 255
  • 3
  • 5

2 Answers2

5

Add MAT_AUTOCOMPLETE_DEFAULT_OPTIONS to your module's providers array like this:

providers: [
  // ...
  {provide: MAT_AUTOCOMPLETE_DEFAULT_OPTIONS, useValue: {autoActiveFirstOption: false}}
]

As for the other constants, I'm not sure you can adjust them. You'll probably have to play around with the CSS styles, see for instance this issue for adjusting the panel height.

Jeto
  • 14,596
  • 2
  • 32
  • 46
  • 1
    Thanx Jeto, I though that token would help me set the hight of each option as well. Truth is, setting the heigh with css is a no brainer. The problem is that when navigating the list with the arrow keys, the list scrolles up much faster then needed because material thinks every option is 48 px height. Thats why I need to change the option hight constant, for the scroll strategy to work proparly. – Gidon Oct 28 '18 at 14:44
  • 1
    @Gidon did you found how to override the constants values? – Matias Fernandez Martinez Sep 23 '19 at 08:54
  • 1
    @MatiasFernandezMartinez nope – Gidon Sep 24 '19 at 09:45
1

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