I created a Material Table where I give the user the option to add or remove columns. The table can also sort, arrange columns, and has a sticky actions column. I want the table to take 100% of the available width space and scroll on the table when there are too many columns instead of fitting them to the screen size. The problem that I have is when only a few columns are displayed, for some reason the first column takes a lot more space then the other columns. I've tried many things and the only solution that I've found was making the table-layout: fixed but this will break the scrolling of overflow-x: scroll which is not what the users want. Any suggestions?
<mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort cdkDropList cdkDropListOrientation="horizontal" (cdkDropListDropped)="drop($event)">
<ng-container [matColumnDef]="column" *ngFor="let column of displayedColumns">
<ng-container *ngIf="column !== 'Actions'; else action">
<th [ngClass]="{'border': hasTableBorders}" mat-header-cell cdkDrag [hidden]="column == 'Id'" *matHeaderCellDef mat-sort-header>{{ column }}</th>
<td [ngClass]="{'border': hasTableBorders}" mat-cell [hidden]="column == 'Id'" *matCellDef="let element">{{ element[column] | shorten:22 }}</td>
</ng-container>
<ng-template #action>
<th mat-header-cell *matHeaderCellDef>Actions</th>
<td mat-cell class="m-0 p-0" *matCellDef="let element">
<h3 class="btn bi bi-pencil-fill" matTooltip="Quick Edit" (click)="onQuickEdit(element.Id)" [matTooltipPosition]="'above'"></h3>
<h2 class="btn bi bi-eye-fill" matTooltip="View" (click)="onFullEdit(element.Id)" [matTooltipPosition]="'above'"></h2>
</td>
</ng-template>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
<tr [ngClass]="{'alternate-row-colors': isAlternateRowColor}" mat-row *matRowDef="let row; columns: columnsToDisplay"></tr>
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="4">No data matching the filter "{{input.value}}"</td>
</tr>
</mat-table>
css
.mat-table {
overflow-x: scroll;
width: 100%;
}
.mat-cell, .mat-header-cell {
word-wrap: initial;
display: table-cell;
padding: 0px 10px;
line-break: unset;
width: 100%;
white-space: nowrap;
overflow: hidden;
vertical-align: middle;
}
.mat-row,
.mat-header-row {
display: table-row;
}
Looks good with many columns
Bad display
Related
I have a mat-table with 2 sticky headers. The code works fine in Firefox but in Chrome, when I scroll down, I can see the text in the background of 2 headers. There is some space between header cells that do not appear in the Firefox browser but does appear in chrome.
As an example, I have added the table background as red, and as you can see the red lines appear between the first 2 rows. Also, there are no borders. table-image
The Code is as below:
HTML Code:
<div>
<button mat-raised-button (click)="tables.push(tables.length)">Add table</button>
<button mat-raised-button (click)="tables.pop()">Remove table</button>
</div>
<div>
Sticky Headers:
<mat-button-toggle-group multiple [value]="['header-1']" #stickyHeaders="matButtonToggleGroup"
class="example-sticky-toggle-group">
<mat-button-toggle value="header-1"> Row 1 </mat-button-toggle>
<mat-button-toggle value="header-2"> Row 2 </mat-button-toggle>
</mat-button-toggle-group>
</div>
<div>
Sticky Footers:
<mat-button-toggle-group multiple [value]="['footer-1']" #stickyFooters="matButtonToggleGroup"
class="example-sticky-toggle-group">
<mat-button-toggle value="footer-1"> Row 1 </mat-button-toggle>
<mat-button-toggle value="footer-2"> Row 2 </mat-button-toggle>
</mat-button-toggle-group>
</div>
<div>
Sticky Columns:
<mat-button-toggle-group multiple [value]="['position', 'symbol']" #stickyColumns="matButtonToggleGroup"
class="example-sticky-toggle-group">
<mat-button-toggle value="position"> Position </mat-button-toggle>
<mat-button-toggle value="name"> Name </mat-button-toggle>
<mat-button-toggle value="weight"> Weight </mat-button-toggle>
<mat-button-toggle value="symbol"> Symbol </mat-button-toggle>
</mat-button-toggle-group>
</div>
<div class="example-container mat-elevation-z8">
<table mat-table [dataSource]="dataSource" *ngFor="let table of tables">
<ng-container matColumnDef="position" [sticky]="isSticky(stickyColumns, 'position')">
<mat-header-cell *matHeaderCellDef> Position </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.position}} </mat-cell>
<mat-footer-cell *matFooterCellDef> Position Footer </mat-footer-cell>
</ng-container>
<ng-container matColumnDef="name" [sticky]="isSticky(stickyColumns, 'name')">
<mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
<mat-footer-cell *matFooterCellDef> Name Footer </mat-footer-cell>
</ng-container>
<ng-container matColumnDef="weight" [stickyEnd]="isSticky(stickyColumns, 'weight')">
<mat-header-cell *matHeaderCellDef> Weight </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.weight}} </mat-cell>
<mat-footer-cell *matFooterCellDef> Weight Footer </mat-footer-cell>
</ng-container>
<ng-container matColumnDef="symbol" [stickyEnd]="isSticky(stickyColumns, 'symbol')">
<mat-header-cell *matHeaderCellDef> Symbol </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.symbol}} </mat-cell>
<mat-footer-cell *matFooterCellDef> Symbol Footer </mat-footer-cell>
</ng-container>
<ng-container matColumnDef="filler">
<mat-header-cell *matHeaderCellDef> Filler header cell </mat-header-cell>
<mat-cell *matCellDef="let element"> Filler data cell </mat-cell>
<mat-footer-cell *matFooterCellDef> Filler footer cell </mat-footer-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: isSticky(stickyHeaders, 'header-1')"></mat-header-row>
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: isSticky(stickyHeaders, 'header-2')"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
<mat-footer-row *matFooterRowDef="displayedColumns; sticky: isSticky(stickyFooters, 'footer-1')"></mat-footer-row>
<mat-footer-row *matFooterRowDef="displayedColumns; sticky: isSticky(stickyFooters, 'footer-2')"></mat-footer-row>
</table>
</div>
CSS Code
.example-container {
height: 400px;
overflow: auto;
}
.mat-table-sticky {
background: #59abfd;
opacity: 1;
border-collapse: collapse;
}
.mat-table {
background-color: red;
border-collapse: collapse;
}
.example-sticky-toggle-group {
margin: 8px;
}
.mat-column-filler {
padding: 0 8px;
font-size: 10px;
text-align: center;
}
.mat-header-cell,
.mat-footer-cell,
.mat-cell {
min-width: 80px;
box-sizing: border-box;
}
.mat-header-row,
.mat-footer-row,
.mat-row {
min-width: 1920px; /* 24 columns, 80px each */
}
.mat-table-sticky-border-elem-top {
border-bottom: 2px solid midnightblue;
}
.mat-table-sticky-border-elem-right {
border-left: 2px solid midnightblue;
}
.mat-table-sticky-border-elem-bottom {
border-top: 2px solid midnightblue;
}
.mat-table-sticky-border-elem-left {
border-right: 2px solid midnightblue;
}
I have already tried border-collapse but that does not work.
Add min-width: 200px;
As per your requirement, to the .mat-cell and .mat-header-cell. That will do
I have an angular page with 3 components i'm trying to get to fill the entire horizontal space. Currently the parent flexbox div is not expanding to the full length, even though I have width: 100% on it. I am able to change the width using pixel values instead, however I have no idea how wide the viewport is going to be when the page is loaded. I have tried setting the margin and padding to 0, making sure the child flexdivs widths add up to 100%, etc...
Relevant html:
<div fxLayout="row" fxLayoutGap="15px" id="bottomContainer">
<div fxFlex="1 0 40%"><mat-card gdArea="chart">
<mat-card-subtitle>Messages Flagged <br>{{period}}<button mat-icon-button [matMenuTriggerFor]="menu"><mat-icon>more_vert</mat-icon></button></mat-card-subtitle>
<mat-menu #menu="matMenu">
<button mat-menu-item (click)="period = 'Week'; periodInt = 0; changeMessagesFlaggedChartPeriod('Week')">Week</button>
<button mat-menu-item (click)="period = 'Month'; periodInt = 1; changeMessagesFlaggedChartPeriod('Month')">Month</button>
<button mat-menu-item (click)="period = 'Year'; periodInt = 2; changeMessagesFlaggedChartPeriod('Year')">Year</button>
</mat-menu>
<mat-card-content gdAreas="chart | list" gdRows="auto">
<div style="display: block">
<canvas baseChart
[datasets]="this.currentMessagesFlaggedDataset"
[labels]="this.MESSAGES_FLAGGED_LABELS"
chartType="radar"
[options]="this.messagesFlaggedChartOptions"
[colors]="this.channelData.chartColors"
height="100%"
width="100%"
></canvas>
</div>
</mat-card-content>
</mat-card></div>
<div fxFlex="1 1 30%"><mat-card class="mat-elevation-z2 channelUserTableContainer">
<mat-card-subtitle>Most Negative Users</mat-card-subtitle>
<table mat-table class="channelUserTable" [dataSource]="channelData.negativeUsers">
<!-- Username column -->
<ng-container matColumnDef="username">
<th mat-header-cell *matHeaderCellDef>Username</th>
<td mat-cell *matCellDef="let element"> {{element.username}}</td>
</ng-container>
<!-- Number of flags column -->
<ng-container matColumnDef="numFlags">
<th mat-header-cell class="textAlignRight" *matHeaderCellDef>Number of Flags</th>
<td mat-cell class="textAlignRight" *matCellDef="let element"> {{element.numFlags}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="userTableColumns"></tr>
<tr mat-row *matRowDef="let row; columns: userTableColumns;"></tr>
</table>
</mat-card></div>
<div fxFlex="1 1 30%"><mat-card class="mat-elevation-z2 channelUserTableContainer">
<mat-card-subtitle>Most Positive Users</mat-card-subtitle>
<table mat-table class="channelUserTable" [dataSource]="channelData.positiveUsers">
<!-- Username column -->
<ng-container matColumnDef="username">
<th mat-header-cell *matHeaderCellDef>Username</th>
<td mat-cell *matCellDef="let element"> {{element.username}}</td>
</ng-container>
<!-- Number of flags column -->
<ng-container matColumnDef="numFlags">
<th mat-header-cell class="textAlignRight" *matHeaderCellDef>Number of Flags</th>
<td mat-cell class="textAlignRight" *matCellDef="let element"> {{element.numFlags}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="userTableColumns"></tr>
<tr mat-row *matRowDef="let row; columns: userTableColumns;"></tr>
</table>
</mat-card></div>
</div>
CSS:
padding-left: 16px;
padding-top: 16px;
}
#channelHeaderText {
font-size: x-large;
font-weight: bold;
}
.mindChartNoMargin :nth-child(1) {
margin: 0;
}
.channelUserTable {
width: 100%;
}
.channelUserTableContainer {
background-color: white;
}
.textAlignRight {
text-align: right;
}
#bottomContainer {
width: 100%;
}
Help is much appreciated
Try moving the width to the container instead, also try making the container a flexbox and the items flex too.
.channelUserTableContainer {
background-color: white;
width: 100%;
display: flex;
}
.channelUserTable {
display: flex;
}
I like to add a dotted line to understand how the flexboxes are working so you could also add a dotted border
border: dotted;
I have some checkboxes, but they are positioned next each other and not under each other.
I have this:
.mat-header-cell mat-checkbox {
padding-top: 50px;
padding-right: 1px;
padding-left: -10px;
display: block;
}
.mat-header-cell:hover mat-checkbox {
padding-top: 50px;
padding-right: 1px;
padding-left: -10px;
display: block;
}
.mat-checkbox-inner-container{
display: block;
}
And this is the html:
<div class="col-sm-12" *ngFor="let item of returnProjectCodes; let i = index">
<mat-checkbox
(click)="$event.stopPropagation()"
(change)="selected = i"
[checked]="selelected === i"
>{{item.name}}
</mat-checkbox>
</div>
But the header 'projects' doesnt positioned correct anymore.[![enter image description here][1]][1]
<ng-container matColumnDef="projects">
<th mat-header-cell *matHeaderCellDef (mouseover)="show = true" (mouseout)="show = false" mat-sort-header i18n>
<div class="col">
<div class="row" *ngFor="let item of returnProjectCodes; let i = index">
<mat-checkbox (click)="$event.stopPropagation()" (change)="selected = i" [checked]="selelected === i"
>{{ item.name }}
</mat-checkbox>
</div>
</div>
Projects
</th>
<td mat-cell *matCellDef="let row">{{ row.projects }}</td>
</ng-container>
Not sure entirely what you are asking, but I assume it is for them to be in vertical order and not horizontal.
You can apply a CSS display as block as you are doing, however you should do it on the parent, and the col-sm-12 is probably keeping the width small so that they line up horizontally.
You can remove that and try setting the width to 100% (this probably wont be necessary, it should default to that width for a block div)
Please keep in mind the more context you can provide the better, such as a picture of what you have versus what you are going for.
I have this:
.mat-header-cell mat-checkbox {
padding-top: 1px;
padding-left: -10px;
margin-bottom: 1px;
display:table;
}
.mat-header-cell:hover mat-checkbox {
padding-top: 1px;
padding-left: -10px;
padding-bottom: 10px;
display: table;
}
And this is html:
<ng-container matColumnDef="projects">
<th mat-header-cell *matHeaderCellDef (mouseover)="show = true" (mouseout)="show = false" mat-sort-header i18n>
<div class="mat-checkbox-project">
<div class="mat-checkbox-label">
<div class="row" *ngFor="let item of returnProjectCodes; let i = index">
<mat-checkbox
(click)="$event.stopPropagation()"
(change)="filterProjects($event, i, item)"
[checked]="selected === i"
>{{ item.name }}
</mat-checkbox>
</div>
</div>
</div>
<div class="mat-sort-header-button-project">Project</div>
</th>
<td mat-cell *matCellDef="let row">{{ row.projects }}</td>
</ng-container>
But when I hovering over the project column header the project column will jump little bit down. So that it collide with the checkboxes.
But what I want is that when you hover over the column header the layout will not change.
So how to do that?
Thank you
Since you have added margin-bottom property in hover, the style will definitely change. Either remove the hover css or remove the padding-bottom: 10px; and replace back the margin-bottom: 1px; in hover state
I have a angular-material card inside here is a mat-table, containing notes.
HTML And CSS Below
<div fxLayout="column" style="flex: 1;">
<mat-card class="card-info" style="margin-right: 10px; max-height: 167px;">
<mat-card-content>
<div class="example-container">
<mat-table #table [dataSource]="notes">
<!-- Name Column -->
<ng-container matColumnDef="date">
<mat-header-cell *matHeaderCellDef> Date </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.created | date }} </mat-cell>
</ng-container>
<!-- Weight Column -->
<ng-container matColumnDef="subject">
<mat-header-cell *matHeaderCellDef> Subject </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.subject}} </mat-cell>
</ng-container>
<!-- Symbol Column -->
<ng-container matColumnDef="note">
<mat-header-cell *matHeaderCellDef> Note </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.value}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
</div>
</mat-card-content>
</mat-card>
</div>
CSS:
.example-container {
display: flex;
flex-direction: column;
max-height: 500px;
min-width: 300px;
}
.mat-table {
overflow: auto;
max-height: 500px;
}
mat-header-cell, mat-cell {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
Although the text inside the cell does truncate, it doesn't truncate enough and causes the card to grow. I don't want to put a max-width, as I want it to flex to fill remainder of the screen.
Here is an image of how it looks:
As you can see it makes the card grow off the screen.