Introduction:
Table is a very important part of web application and when it comes to a complex table displaying content is a real challenge.
We can arrange our table material in a variety of ways to make it easily readable. We’ll talk about the size of the columns in a table, one of those options.
This narrative will teach us how to add column resize functionality to an angular material table.
Let’s start coding…
Step1: Create a new Project
To create a new project first open the visual studio code & type following command in terminal.
new new tablecolumnresize
Step2: Install Material
To use material component we need to install angular material as follows:
npm install angular-material --save
Step3: Import Module
In app.module.ts file import material module
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import {DragDropModule} from '@angular/cdk/drag-drop'; import {ScrollingModule} from '@angular/cdk/scrolling'; import {CdkTableModule} from '@angular/cdk/table'; import {CdkTreeModule} from '@angular/cdk/tree'; import {A11yModule} from '@angular/cdk/a11y'; import {MatTableModule} from '@angular/material'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, A11yModule, CdkTableModule, CdkTreeModule, DragDropModule, MatTableModule, ScrollingModule, ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Step4: Update app.component.html file
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" cdkDropListGroup> <ng-container *ngFor="let column of columns; let i = index" [matColumnDef]="column.field"> <th mat-header-cell *matHeaderCellDef> {{ column.field }} <span class="resize-handle" (mousedown)="onResizeColumn($event, i)"></span> </th> <td mat-cell *matCellDef="let row" > {{ row[column.field] }} </td> </ng-container> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table>
Step5 : Update App.component.ts
import { AfterViewInit, Component, ElementRef, HostListener, OnInit, Renderer2, ViewChild } from '@angular/core'; import { MatTable } from '@angular/material'; export interface PeriodicElement { name: string; position: number; weight: number; symbol: string; } const ELEMENT_DATA: PeriodicElement[] = [ {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'}, {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'}, {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'}, {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'}, {position: 5, name: 'Boron', weight: 10.811, symbol: 'B'}, {position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'}, {position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'}, {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'}, {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'}, {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'}, ]; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit, AfterViewInit { title = 'Material Table column Resize'; @ViewChild(MatTable, {read: ElementRef} ) private matTableRef: ElementRef; columns: any[] = [ { field: 'position', width: 20, }, { field: 'name', width: 20, }, { field: 'weight', width: 20, }, { field: 'symbol', width: 20, } ]; displayedColumns: string[] = []; dataSource = ELEMENT_DATA; pressed = false; currentResizeIndex: number; startX: number; startWidth: number; isResizingRight: boolean; resizableMousemove: () => void; resizableMouseup: () => void; constructor( private renderer: Renderer2 ) { } ngOnInit() { this.setDisplayedColumns(); } ngAfterViewInit() { this.setTableResize(this.matTableRef.nativeElement.clientWidth); } setTableResize(tableWidth: number) { let totWidth = 0; this.columns.forEach(( column) => { totWidth += column.width; }); const scale = (tableWidth - 5) / totWidth; this.columns.forEach(( column) => { column.width *= scale; this.setColumnWidth(column); }); } setDisplayedColumns() { this.columns.forEach(( column, index) => { column.index = index; this.displayedColumns[index] = column.field; }); } onResizeColumn(event: any, index: number) { console.log(event.target.parentElement); this.checkResizing(event, index); this.currentResizeIndex = index; this.pressed = true; this.startX = event.pageX; this.startWidth = event.target.parentElement.clientWidth; event.preventDefault(); this.mouseMove(index); } private checkResizing(event, index) { const cellData = this.getCellData(index); if ( ( index === 0 ) || ( Math.abs(event.pageX - cellData.right) < cellData.width / 2 && index !== this.columns.length - 1 ) ) { this.isResizingRight = true; } else { this.isResizingRight = false; } } private getCellData(index: number) { const headerRow = this.matTableRef.nativeElement.children[0].querySelector('tr'); const cell = headerRow.children[index]; return cell.getBoundingClientRect(); } mouseMove(index: number) { this.resizableMousemove = this.renderer.listen('document', 'mousemove', (event) => { if (this.pressed && event.buttons ) { const dx = (this.isResizingRight) ? (event.pageX - this.startX) : (-event.pageX + this.startX); const width = this.startWidth + dx; if ( this.currentResizeIndex === index && width > 50 ) { this.setColumnWidthChanges(index, width); } } }); this.resizableMouseup = this.renderer.listen('document', 'mouseup', (event) => { if (this.pressed) { this.pressed = false; this.currentResizeIndex = -1; this.resizableMousemove(); this.resizableMouseup(); } }); } setColumnWidthChanges(index: number, width: number) { const orgWidth = this.columns[index].width; const dx = width - orgWidth; if ( dx !== 0 ) { const j = ( this.isResizingRight ) ? index + 1 : index - 1; const newWidth = this.columns[j].width - dx; if ( newWidth > 50 ) { this.columns[index].width = width; this.setColumnWidth(this.columns[index]); this.columns[j].width = newWidth; this.setColumnWidth(this.columns[j]); } } } setColumnWidth(column: any) { const columnEls = Array.from( document.getElementsByClassName('mat-column-' + column.field) ); columnEls.forEach(( el: HTMLDivElement ) => { el.style.width = column.width + 'px'; }); } @HostListener('window:resize', ['$event']) onResize(event) { this.setTableResize(this.matTableRef.nativeElement.clientWidth); } }
app.component.ts
table { width: 100%; } table th { position: relative; } table th:hover .resize-handle { opacity: 1; transition: .3s ease-in-out; } .resize-handle { display: inline-block; border-right: 2px solid lightgray; position: absolute; top: 0; right: 0; height: 100%; cursor: col-resize; opacity: 0; } .resize-handle:hover { width: 20px; }
Now run the project.
OUTPUT: