Components: md-option: Possibility to set height

Created on 3 Mar 2017  路  24Comments  路  Source: angular/components

Bug, feature request, or proposal:

bug/feature request

What is the expected behavior?

panel position should be recalculated relatively to current md-option height

What is the current behavior?

md-option height is hardcoded, so panel position is same regardless of current md-option height which causes misalignment of the select panel

What are the steps to reproduce?

  1. Set height and line-height for md-option
  2. Select 3rd option in select
  3. Open select
    http://plnkr.co/edit/xCNGJhNLXtpExdUj4A8i?p=preview

What is the use-case or motivation for changing an existing behavior?

current height is way too big for some UI designs, there should be way to customize it

Fixed in MDC G P3 materiaselect feature

Most helpful comment

@crisbeto could you take a look, please

All 24 comments

any news here?

@crisbeto could you take a look, please

This would be great function for my use-case

This would be great function for any, who would like to use md-option components 鈽濓笍

Any news or known workaround ?

Nope, still waiting for implementation : /

No workaround is possible?

Nope, still waiting : /

Possible work-around:

In your component get a reference to the MatSelect with bigger options, and override it's _getItemHeight with your own height calculation.

  @ViewChild('xlFood')
  private xlFood: MatSelect; 

  ngAfterViewInit() {
    (<any> this.xlFood)._getItemHeight = () => this.xlFood._triggerFontSize * 5; // default is 3em
  }

Example: https://stackblitz.com/edit/angular-material2-issue-swsdrn?file=app%2Fapp.component.ts

Here is my workaround:

// the input element with the matAutocomplete attached to it (<input [matAutocomplete]="autoCompleteComponent">)
@ViewChild('autoCompleteInput', {read: MatAutocompleteTrigger}) matAutocompleteTrigger: MatAutocompleteTrigger;

// the mat auto-complete component itself (<mat-autocomplete #autoCompleteComponent="matAutocomplete">)
@ViewChild('autoCompleteComponent', {read: MatAutocomplete}) autoCompleteComponent: MatAutocomplete;

this.matAutocompleteTrigger['_scrollToOption'] = () => {
    const index: number = this.autoCompleteComponent['_keyManager'].activeItemIndex || 0;
    const labelCount = _countGroupLabelsBeforeOption(index, this.autoCompleteComponent.options, this.autoCompleteComponent.optionGroups);
    const newScrollPosition = _getOptionScrollPosition(index + labelCount, AUTOCOMPLETE_OPTION_HEIGHT, this.autoCompleteComponent._getScrollTop(), AUTOCOMPLETE_PANEL_HEIGHT);

    this.autoCompleteComponent._setScrollTop(newScrollPosition);
};

@danmana Thanks for this work around. It works really well. Even though for how long, since _getItemHeight() supposed to be private.

But by this workaround you can see, that it would not be so hard to make the itemHeight customisable. So I hope this can be somehow implemented in a future version.

Would be great if the AUTOCOMPLETE_OPTION_HEIGHT was an injection token that could be set. Or, completely remove the need for this, by using Element.scrollIntoView() on the option element.

@pwnball , can you kindly expand on your workaround? Any chance you can setup a quick Stackblitz? I'm not quite sure when you are calling the this.matAutocompleteTrigger['_scrollToOption'].
Will be appreciated. Tnx

@AsafAgranat I found that the <input> tag needs a #variable that you use in the "@ViewChild"
Example: //in the html <input #variable [matAutocomplete]="autoCompleteComponent">
//in the ts @ViewChild('variable', {read: MatAutocompleteTrigger}) matAutocompleteTrigger: MatAutocompleteTrigger;

I'm just using your hack @pwnball but instead of overriding the const values it will work just using:
element.scrollIntoView({block:'end'});
or
element.scrollIntoView({block:'start'});
depending on the direction...

This is my complete fix with no hardcoded height of neither panel nor option:

ngAfterViewInit(): void {
    this.matAutocompleteTrigger['_scrollToOption'] = () => {
      const activeMatOption: MatOption | null = this.activeMatOption;
      if (null !== activeMatOption) {
        const optionElement = activeMatOption._getHostElement();

        const optionBox = optionElement.getBoundingClientRect();
        const panelBox = this.matAutocomplete.panel.nativeElement.getBoundingClientRect();

        if (optionBox.top < panelBox.top) {
          optionElement.scrollIntoView({ block: 'start' });
        } else if (optionBox.bottom > panelBox.bottom) {
          optionElement.scrollIntoView({ block: 'end' });
        }
      }
    };
  }

@glebmachine @jcudzich I did create a pull request but guess something went wrong...

Here is my workaround:

// the input element with the matAutocomplete attached to it (<input [matAutocomplete]="autoCompleteComponent">)
@ViewChild('autoCompleteInput', {read: MatAutocompleteTrigger}) matAutocompleteTrigger: MatAutocompleteTrigger;

// the mat auto-complete component itself (<mat-autocomplete #autoCompleteComponent="matAutocomplete">)
@ViewChild('autoCompleteComponent', {read: MatAutocomplete}) autoCompleteComponent: MatAutocomplete;

this.matAutocompleteTrigger['_scrollToOption'] = () => {
    const index: number = this.autoCompleteComponent['_keyManager'].activeItemIndex || 0;
    const labelCount = _countGroupLabelsBeforeOption(index, this.autoCompleteComponent.options, this.autoCompleteComponent.optionGroups);
    const newScrollPosition = _getOptionScrollPosition(index + labelCount, AUTOCOMPLETE_OPTION_HEIGHT, this.autoCompleteComponent._getScrollTop(), AUTOCOMPLETE_PANEL_HEIGHT);

    this.autoCompleteComponent._setScrollTop(newScrollPosition);
};

@pwnball @AsafAgranat this.matAutocompleteTrigger['_scrollToOption']. Where to call this, I tried inside ngonint andaftervew init shows undefined

Hi guys,
is there a way to adapt this autocomplete-scenario to mat-select?
I already tried if there is something similar like this.matselect['_scrollToOption'] but it seems that there is no such event.

Any ideas?

Hi a solution for reuse the @danmana solution on all components
create a directive:

import { Directive } from '@angular/core';
import { MatSelect } from '@angular/material';

import {
  _countGroupLabelsBeforeOption,
  _getOptionScrollPosition
} from '@angular/material/core';

@Directive({
  selector: '[appMatOptionHeight]'
})
export class MatOptionHeightDirective {
    constructor(
      public matSelect: MatSelect
    ) {
      (<any> this.matSelect)._getItemHeight = () => this.matSelect._triggerFontSize * 1.5;
    }
}

and add to html <mat-select appMatOptionHeight>

In Angular 9.1.7 _getItemHeight is a private property of MatSelect. so unable to access outside.

@danmana @YanDuc solution work up to angular version 8

Any update on this issue ?

@Shrinivassab

Late response, but you're not intended to call that function. Its an internal function in the angular component that contains a flaw. Basically I rewrote the function to function with the value I wanted, rather than their hardcoded value.

@gowthaman-i2i the solutions still work in Angular 9.1.11

Note that the solutions shown here do not set the option height, you have to do that on your own, either via css or js.

I've extended @YanDuc directive (nice idea) to also set the option height (and accept a configurable height in em)
You can see it here https://stackblitz.com/edit/angular-ivy-9wfzdg?file=src%2Fapp%2Fmat-option-height.directive.ts

@gowthaman-i2i the solutions still work in Angular 9.1.11

Note that the solutions shown here do not set the option height, you have to do that on your own, either via css or js.

@danmana thanks for your reply. I have tried to get "_getItemHeight" property but it's masked as Private in angular/material select file so unable to access outside

tried example here https://stackblitz.com/edit/angular-yv2zvm

@gowthaman-i2i Yes, the field is private, but you can access it when casting as <any>. I don't see any errors running the code.

I noticed in your example that you forgot to add the directive to the NgModule declarations, so it was not getting picked up by Angular at all. That's why it wasn't working.

Here is a fork from your example https://stackblitz.com/edit/angular-yv2zvm-8vvase
Changes I did:

  • added the directive to the NgModule in main.ts
  • set the .mat-option css height to 100px so you can easily see that it's working
  • made the directive to return 100 as well - this must match whatever you set in css ! (I added some comments explaining this)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

julianobrasil picture julianobrasil  路  3Comments

LoganDupont picture LoganDupont  路  3Comments

Hiblton picture Hiblton  路  3Comments

3mp3ri0r picture 3mp3ri0r  路  3Comments

jelbourn picture jelbourn  路  3Comments