Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make Angular Material mat-select select list width automatic and adapt to the widest option?

Problem

I have a Angular 6.1.10 web app. I want the width of the mat-select list options to be automatic and adapt to the widest option in the options list. See the screenshot below: enter image description here

Code of the Page Showing the mat-select List

HTML Code

<div class="all-container">
  <div class="top-border"></div>
  <div class="content-container">

    <div *ngIf="statisticsPageToDisplay == 'surveyTemplateSelectionPage'">
      <div class="title">
        Select a survey to view statistics for
      </div>
      <div class="survey-templates-container">
        <div class="survey-templates-scroll-list">
          <div *ngFor="let surveyTemplate of surveyTemplates">
            <app-statistics-page-survey-template-panel (onClickEmitter)="surveyTemplatePanelClicked($event)"
              [surveyTemplate]="surveyTemplate"></app-statistics-page-survey-template-panel>
          </div>
        </div>
      </div>
    </div>

    <div *ngIf="statisticsPageToDisplay == 'surveyTemplateStatistics'">

      <div class="top-buttons-row">
        <button mat-raised-button style="float: left; margin-top: 0.75%; margin-left: 1.5%"
          (click)="selectAnotherSurveyTemplateClicked()" [color]="'primary'">
          <i class="fas fa-chevron-left"></i>
          <span style="margin-left: 0.75em !important">Select another survey</span>
        </button>
      </div>
      <div class="mat-select-formatting">
        <mat-form-field>
          <div class="team-selection-list">

            <mat-select (selectionChange)="selectedTeamOptionChange()" placeholder="Select team to view statistics for"
              [(value)]="selectedTeamOption">
              <mat-select-trigger>
                <i class="fas fa-building"></i>
                <span style="margin-left: 0.3em">
                  {{selectedTeamOption?.companyRef?.name}}, 
                </span>
                <i class="fas fa-users"></i>
                <span style="margin-left: 0.3em">
                  {{selectedTeamOption?.name}}
                </span>
              </mat-select-trigger>
              <mat-option *ngFor="let teamOption of teamOptions" [value]="teamOption">
                <i class="fas fa-building"></i>
                <span style="margin-left: 0.3em">
                  {{teamOption?.companyRef?.name}}, 
                </span>
                <i class="fas fa-users"></i>
                <span style="margin-left: 0.3em">
                  {{teamOption?.name}}
                </span>
              </mat-option>
            </mat-select>
          </div>
        </mat-form-field>
      </div>
      <div class="title-row">
        <div class="title smallest-top-margin">
          Statistics for survey {{currentStatisticsSurvey.title}} for team {{selectedTeamOption}}
        </div>
      </div>

      <div *ngIf="redDataPoints != null && yellowDataPoints != null && greenDataPoints != null">
        <app-smileys-time-line-chart [redDataPoints]="redDataPoints" [yellowDataPoints]="yellowDataPoints"
          [greenDataPoints]="greenDataPoints" (chartPointClicked)="chartPointClicked($event)">
        </app-smileys-time-line-chart>
      </div>

      <div *ngIf="selectedBarChartTime != null">
        <div class="title small-top-margin">
          Category statistics for survey {{currentStatisticsSurvey.title}} for team {{selectedTeamOption}} at
          {{selectedBarChartTime.toLocaleDateString()}}
        </div>
        <app-categories-bar-chart [categoryStatistics]="barChartStatistics"></app-categories-bar-chart>
      </div>

    </div>
    
  </div>
</div>

CSS Code

.mat-raised-button {
    height: 1.5em !important;
    line-height: 1em !important;
    margin-bottom: 0.6em !important;
    text-align: left !important; 
    padding-left: 0.5em !important;
    padding-right: 1em !important;
    margin-left: 0 !important;
}

.mat-raised-button i {
    margin-bottom: 0.1em !important;
}

.all-container {
    display: flex;
    flex-direction: column;
    justify-content: start;
    align-items: center;
    box-shadow: none !important;
}

.top-border {
    height: 1px;
    width: 100%;
    margin: auto;
    border-top: 0.75px solid rgb(225, 225, 225);
}

.content-container {
    width: 98%;
    margin: auto;
    padding-top: 5px;
    display: flex;
    flex-direction: column;
    justify-content: start;
    align-items: flex-start;
    box-shadow: none !important;
}

.top-buttons-row {
    width: 100%;
    height: auto;
    display: flex;
    flex-direction: row;
    justify-content: start;
    align-items: flex-start;
}

.title-row {
    width: 100%;
    height: auto;
    display: flex;
    flex-direction: row;
    justify-content: start;
    align-items: stretch;
}

.title {
  color: rgb(150, 150, 150);
  font-family: Verdana, Geneva, Tahoma, sans-serif;
  font-size: 1.2em;
  width: 100%;
}

.survey-templates-container {
    width: 30vw;
    height: 65vh;
    margin-top: 15px;
    background-color: white;
    border-radius: 5px;
    box-shadow: 2.5px 2.5px 10px 1px rgb(225, 225, 225);
    border: 0.5px solid rgb(220, 220, 220);
    overflow: hidden;
}

.survey-templates-scroll-list {
    width: 98%;
    height: 98%;
    overflow-x: hidden;
    overflow-y: auto;
    padding: 1%;
    display: flex;
    flex-direction: column;
    justify-content: start;
    align-items: center;
}

::ng-deep .mat-select-formatting .mat-form-field {
    margin-top: 7.5px !important;
    padding-top: 0px !important;
    padding-bottom: 5px !important;
    padding-left: 0px !important;
    padding-right: 0px !important;
    height: 2.5em !important;
    max-height: 2.5em !important;
    min-height: 2.5em !important;
    width: auto !important;
    max-width: 100% !important;
    color: rgb(125, 125, 125) !important;
}

.mat-select {
    width: 100% !important;
    min-width: 100% !important;
    max-width: 100% !important;
    cursor: pointer !important;
    color: rgb(125, 125, 125) !important;
}

.mat-select-content {
    width: 100% !important;
    min-width: 100% !important;
    max-width: 100% !important;
    cursor: pointer !important;
    padding-left: 1em !important;
    padding-right: 1em !important;
    color: rgb(125, 125, 125) !important;
}

.mat-select-placeholder {
    color: rgb(125, 125, 125) !important;
}

::ng-deep .mat-select-value {
    width: auto;
    max-width: 100% !important;
    color: rgb(125, 125, 125) !important;
}

::ng-deep .mat-select-value-text {
    color: rgb(125, 125, 125) !important;
}

.mat-select-panel {
    color: rgb(125, 125, 125) !important;
}

.mat-select-trigger {
    color: rgb(125, 125, 125) !important;
}

.mat-option {
    color: rgb(125, 125, 125) !important;
    height: 1.75em !important;
    min-height: 1.75em !important;
    max-height: 1.75em !important;
}

.mat-select:hover {
    cursor: pointer !important;
}

.mat-select:active {
    cursor: pointer !important;
}

.smallest-top-margin {
    margin-top: 7px;
}

.smaller-top-margin {
    margin-top: 10px;
}

.small-top-margin {
    margin-top: 15px;
    }

TypeScript Code

import { Component, OnInit } from '@angular/core';
import { SurveyTemplateModel } from 'src/shared_classes/models/SurveyTemplateModel';
import { AppAnswer } from 'src/shared_classes/app_models/AppAnswer';
import { Answer } from 'src/shared_classes/app_models/Answer';
import { AppQuestion } from 'src/shared_classes/app_models/AppQuestion';
import { Question } from 'src/shared_classes/app_models/Question';
import { Category } from 'src/shared_classes/app_models/Category';
import { AppCategory } from 'src/shared_classes/app_models/AppCategory';
import { SurveyTemplate } from 'src/shared_classes/app_models/SurveyTemplate';
import { PageLoadHttpService } from 'src/service/page-load-http.service';
import { StatisticsPageSurveyTemplatePanelComponent } from 'src/feature_components/statistics-page-survey-template-panel/statistics-page-survey-template-panel.component';
import { SurveyHttpServiceService } from 'src/service/survey-http-service.service';
import { GatheredTeamSurveyStatistics } from 'src/shared_classes/models/GatheredTeamSurveyStatistics';
import { OrgReference } from 'src/shared_classes/models/property_classes/OrgReference';
import { SurveyStatisticsDataPoint } from 'src/shared_classes/models/SurveyStatisticsDataPoint';
import { LineChartDataSet } from 'src/shared_classes/other_data_classes/LineChartDataSet';
import { TimeLineChartDataPoint } from 'src/shared_classes/models/TimeLineChartDataPoint';
import { CategoryBarChartStatistic } from 'src/shared_classes/models/CategoryBarChartStatistic';
import { TimePoint } from 'src/shared_classes/other_data_classes/Point';
import { Team } from 'src/shared_classes/app_models/Team';

@Component({
  selector: 'app-statistics-page',
  templateUrl: './statistics-page.component.html',
  styleUrls: ['./statistics-page.component.css']
})
export class StatisticsPageComponent implements OnInit {

  surveyTemplates: SurveyTemplate[];
  statisticsPageToDisplay: string;
  currentStatisticsSurvey: SurveyTemplate;
  teamOptions: Team[];
  selectedTeamOption: Team;
  greenDataPoints: TimeLineChartDataPoint[];
  yellowDataPoints: TimeLineChartDataPoint[];
  redDataPoints: TimeLineChartDataPoint[];
  barChartStatistics: CategoryBarChartStatistic[];
  allLoadedTeamStatistics: GatheredTeamSurveyStatistics[];
  selectedTeamStatisticsData: GatheredTeamSurveyStatistics;
  selectedBarChartTime: Date;

  constructor(private pageLoadHttpService: PageLoadHttpService,
    private surveyHttpService: SurveyHttpServiceService) { }

  ngOnInit() {
    this.statisticsPageToDisplay = "surveyTemplateSelectionPage";
    this.surveyTemplates = [];
    this.loadSurveyTemplates();
  }

  loadSurveyTemplates() {
    this.pageLoadHttpService.getSurveyTemplates().subscribe((surveyTemplModels: SurveyTemplateModel[]) => {
      this.surveyTemplates = surveyTemplModels.map(surveyTemplModel =>
        this.convertSurveyTemplateModelToSurveyTemplate(surveyTemplModel));
    });
  }

  loadSurveyStatisticsData(surveyTemplateGuid: string) {
    this.surveyHttpService.getTeamsSurveyStatisticsData(surveyTemplateGuid).subscribe((statisticsData: GatheredTeamSurveyStatistics[]) => {
      this.allLoadedTeamStatistics = statisticsData;
      this.useLoadedSurveyStatisticsData(statisticsData);
    });
  }

  useLoadedSurveyStatisticsData(statisticsData: GatheredTeamSurveyStatistics[]) {
    this.createTeamOptions(statisticsData);
    this.selectedTeamOption = this.teamOptions[0];
    this.selectedTeamStatisticsData = this.getSelectedTeamStatisticsData(statisticsData);

    this.createSmileyLineChart(this.selectedTeamStatisticsData.statisticsDataPoints);
  }

  getSelectedTeamStatisticsData(statisticsData: GatheredTeamSurveyStatistics[]): GatheredTeamSurveyStatistics {
    var selectedTeamStatisticsData: GatheredTeamSurveyStatistics;
    for (var i = 0; i < statisticsData.length; i++)
      if (statisticsData[i].teamRef.guid == this.selectedTeamOption.guid) {
        selectedTeamStatisticsData = statisticsData[i];
        break;
      }
    return selectedTeamStatisticsData;
  }

  createGreenTimeLineChartDataPoint(dataPoint: SurveyStatisticsDataPoint): TimeLineChartDataPoint {
    var timeLineChartDataPoint: TimeLineChartDataPoint = {
      score: dataPoint.greenDataPoint.score,
      greenGoalLimit: dataPoint.greenGoalLimit,
      yellowGoalLimit: dataPoint.yellowGoalLimit,
      redGoalLimit: dataPoint.redGoalLimit,
      time: dataPoint.greenDataPoint.time
    };
    return timeLineChartDataPoint;
  }

  createYellowTimeLineChartDataPoint(dataPoint: SurveyStatisticsDataPoint): TimeLineChartDataPoint {
    var timeLineChartDataPoint: TimeLineChartDataPoint = {
      score: dataPoint.yellowDataPoint.score,
      greenGoalLimit: dataPoint.greenGoalLimit,
      yellowGoalLimit: dataPoint.yellowGoalLimit,
      redGoalLimit: dataPoint.redGoalLimit,
      time: dataPoint.yellowDataPoint.time
    };
    return timeLineChartDataPoint;
  }

  createRedTimeLineChartDataPoint(dataPoint: SurveyStatisticsDataPoint): TimeLineChartDataPoint {
    var timeLineChartDataPoint: TimeLineChartDataPoint = {
      score: dataPoint.redDataPoint.score,
      greenGoalLimit: dataPoint.greenGoalLimit,
      yellowGoalLimit: dataPoint.yellowGoalLimit,
      redGoalLimit: dataPoint.redGoalLimit,
      time: dataPoint.redDataPoint.time
    };
    return timeLineChartDataPoint;
  }

  createSmileyLineChart(statisticsDataPoints: SurveyStatisticsDataPoint[]) {

    var redDataPoints: TimeLineChartDataPoint[] = [];
    var yellowDataPoints: TimeLineChartDataPoint[] = [];
    var greenDataPoints: TimeLineChartDataPoint[] = [];

    statisticsDataPoints.forEach(dataPoint => {
      redDataPoints.push(this.createRedTimeLineChartDataPoint(dataPoint));
      yellowDataPoints.push(this.createYellowTimeLineChartDataPoint(dataPoint));
      greenDataPoints.push(this.createGreenTimeLineChartDataPoint(dataPoint));
    });

    redDataPoints.forEach(redDataPoint => {
      redDataPoint.time = new Date(redDataPoint.time);
    });
    yellowDataPoints.forEach(yellowDataPoint => {
      yellowDataPoint.time = new Date(yellowDataPoint.time);
    });
    greenDataPoints.forEach(greenDataPoint => {
      greenDataPoint.time = new Date(greenDataPoint.time);
    });

    this.redDataPoints = redDataPoints;
    this.yellowDataPoints = yellowDataPoints;
    this.greenDataPoints = greenDataPoints;
  }

  createBarChart(statisticsDataPoints: SurveyStatisticsDataPoint[]) {
  }

  surveyTemplatePanelClicked(clickedPanel: StatisticsPageSurveyTemplatePanelComponent) {
    this.openStatisticsPage(clickedPanel.surveyTemplate);
  }

  openStatisticsPage(surveyTemplate: SurveyTemplate) {
    this.currentStatisticsSurvey = surveyTemplate;
    this.statisticsPageToDisplay = "surveyTemplateStatistics";
    this.loadSurveyStatisticsData(surveyTemplate.guid);
  }

  convertSurveyTemplateModelToSurveyTemplate(model: SurveyTemplateModel): SurveyTemplate {
    var appCategories: AppCategory[] = [];
    model.categories.forEach(c => {
      appCategories.push(this.convertCategoryToAppCategory(c));
    });

    var surveyTemplate: SurveyTemplate = {
      categories: appCategories,
      date: model.date,
      guid: model.guid,
      isChecked: false,
      companyGuids: model.companyGuids,
      teamGuids: model.teamGuids,
      userGuid: model.userGuid,
      title: model.title,
      info: model.info,
      defaultGreenGoalLimit: model.defaultGreenGoalLimit,
      defaultYellowGoalLimit: model.defaultYellowGoalLimit,
      defaultRedGoalLimit: model.defaultRedGoalLimit
    }
    return surveyTemplate;
  }

  convertCategoryToAppCategory(category: Category): AppCategory {
    var appQuestions: AppQuestion[] = [];
    category.questions.forEach(q => {
      appQuestions.push(this.convertQuestionToAppQuestion(q));
    });

    var appCategory: AppCategory = {
      questions: appQuestions,
      isChecked: false,
      title: category.title,
      nrGreenCategoryScores: category.nrGreenCategoryScores,
      nrYellowCategoryScores: category.nrYellowCategoryScores,
      nrRedCategoryScores: category.nrRedCategoryScores
    };

    return appCategory;
  }

  convertQuestionToAppQuestion(question: Question): AppQuestion {
    var appAnswers: AppAnswer[] = [];
    question.answers.forEach(a => {
      appAnswers.push(this.convertAnswerToAppAnswer(a));
    });

    var appQuestion: AppQuestion = {
      questionStr: question.questionStr,
      answers: appAnswers,
      pickedAnswer: null,
      isChecked: false
    };

    return appQuestion;
  }

  convertAnswerToAppAnswer(answer: Answer): AppAnswer {
    var appAnswer: AppAnswer = {
      answerStr: answer.answerStr,
      points: answer.points,
      isChecked: false
    };

    return appAnswer;
  }

  selectAnotherSurveyTemplateClicked() {
    this.statisticsPageToDisplay = "surveyTemplateSelectionPage";
  }

  createTeamOptions(gatheredTeamsSurveyStatistics: GatheredTeamSurveyStatistics[]) {
    this.teamOptions = [];
    gatheredTeamsSurveyStatistics.forEach(gtss => {
      var team = this.getStatisticsDataTeam(gtss);
      this.teamOptions.push(team);
    });
    this.sortTeamOptions();
  }

  sortTeamOptions() {
    this.teamOptions.sort(this.compareTeamNames);
  }

  compareTeamNames(a:Team, b:Team): number {
    var companyNameComparison = a.companyRef.name.toLowerCase().localeCompare(b.companyRef.name.toLowerCase());
    
    if(companyNameComparison !== 0)
      return companyNameComparison;

    return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
  }

  selectedTeamOptionChange() {
    console.log("Selected team: " + this.selectedTeamOption);
    this.selectedTeamStatisticsData = this.getSelectedTeamStatisticsData(this.allLoadedTeamStatistics);
    this.createSmileyLineChart(this.selectedTeamStatisticsData.statisticsDataPoints);
    this.selectedBarChartTime = null;
  }

  chartPointClicked(timePoint: TimePoint) {
    this.updateBarChart(timePoint);
  }

  updateBarChart(timePoint: TimePoint) {
    for (var i = 0; i < this.selectedTeamStatisticsData.statisticsDataPoints.length; i++) {
      if ((new Date(this.selectedTeamStatisticsData.statisticsDataPoints[i].greenDataPoint.time)).getTime() == (new Date(timePoint.x)).getTime()) {
        this.barChartStatistics = this.selectedTeamStatisticsData.statisticsDataPoints[i].categoryBarChartStatistics;
        break;
      }
    }
    this.selectedBarChartTime = timePoint.x;
  }

  getStatisticsDataTeam(statisticsData: GatheredTeamSurveyStatistics): Team {
    return new Team (
      statisticsData.teamRef.name,
      statisticsData.teamRef.guid,
      null,
      null,
      null,
      null,
      null,
      statisticsData.companyRef,
      null,
      null,
      null,
      null,
      null,
      null,
      null
    );
  }

}

Final Thoughts and Question

I have tried lots of different CSS code and inspecting the mat-select in the Chrome browser. But nothing has worked so far. How can I make the mat-select options list width be automatic and adapt to the widest option in the list so that they don't clip?

Thanks!

like image 981
EggBender Avatar asked Sep 15 '25 08:09

EggBender


1 Answers

For me, it was enough to override max-width on the following core CSS class. Do it via ViewEncapsulation.None, or via ::ng-deep. Set it to max-width: fit-content or max-width: initial. Example:

::ng-deep .my-custom-panel-class.mat-select-panel {
    ...
    max-width: fit-content;
    ...
}

Clarification from 29.09.2023: If you want to apply the change globally, skip ::ng-deep and add it to global styles.css file. If you like to scope your change to only your specific component, then prepend the selector above with :host .

like image 91
ilya.chepurnoy Avatar answered Sep 17 '25 00:09

ilya.chepurnoy