'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:
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!
Solution 1:[1]
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;
...
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | ilya.chepurnoy |