DEV Community

Cover image for Mastering Resizable Columns in Angular Table: A Step-by-Step Guide for Developers
chintanonweb
chintanonweb

Posted on

Mastering Resizable Columns in Angular Table: A Step-by-Step Guide for Developers

How to Create Resizable Columns in Angular Table: A Step-by-Step Guide

Angular Material tables provide a sleek way to display data. However, users often want additional functionality like the ability to resize table columns for better control over data display. In this guide, we'll walk through the process of creating resizable columns in an Angular table using a custom directive. You'll learn how to set up the directive, style the resizer, and implement column resizing step-by-step.

Introduction

Adding resizable columns to an Angular Material table involves creating a custom directive that listens to mouse events, allowing users to click and drag a column to adjust its width. This gives users flexibility, especially when dealing with large datasets, improving the user experience.

In this tutorial, we will:

  • Create a custom column resize directive.
  • Handle mouse events to resize columns.
  • Apply styles for a smooth user experience.
  • Attach the directive to an Angular Material table.

Let’s dive into it.

Step 1: Setting Up the Angular Material Table

First, ensure that your Angular project has Angular Material installed. If not, run the following command to add Angular Material to your project:

ng add @angular/material
Enter fullscreen mode Exit fullscreen mode

Once Angular Material is installed, you can create a basic table using the following code.

HTML for the Table:

<div class="resizable-table">
  <table mat-table [dataSource]="dataSource">
    <ng-container *ngFor="let column of displayedColumns" [matColumnDef]="column">
      <th mat-header-cell *matHeaderCellDef appColumnResize>{{ column }}</th>
      <td mat-cell *matCellDef="let element">{{ element[column] }}</td>
    </ng-container>
    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
  </table>
</div>
Enter fullscreen mode Exit fullscreen mode

Here, we use mat-table from Angular Material to display a simple table. The appColumnResize directive is applied to the th (header) elements to make columns resizable.

Data for the Table:

import { Component, ViewEncapsulation } from '@angular/core';

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' },
  // ... add more data
];

@Component({
  selector: 'table-basic-example',
  styleUrls: ['table-basic-example.scss'],
  templateUrl: 'table-basic-example.html',
  encapsulation: ViewEncapsulation.None,
})
export class TableBasicExample {
  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = ELEMENT_DATA;
}
Enter fullscreen mode Exit fullscreen mode

The component includes data for the periodic elements, which we will display in the table.

Step 2: Creating the Column Resize Directive

Next, we’ll implement a custom Angular directive that enables resizing functionality for the table columns.

Directive Implementation:

import {
  Directive,
  ElementRef,
  Renderer2,
  NgZone,
  Input,
  OnInit,
  OnDestroy,
} from '@angular/core';
import { fromEvent, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Directive({
  selector: '[appColumnResize]',
})
export class ColumnResizeDirective implements OnInit, OnDestroy {
  @Input() resizableTable: HTMLElement | null = null;

  private startX!: number;
  private startWidth!: number;
  private isResizing = false;
  private column: HTMLElement;
  private resizer!: HTMLElement;
  private destroy$ = new Subject<void>();

  constructor(
    private el: ElementRef,
    private renderer: Renderer2,
    private zone: NgZone
  ) {
    this.column = this.el.nativeElement;
  }

  ngOnInit() {
    this.createResizer();
    this.initializeResizeListener();
  }

  private createResizer() {
    this.resizer = this.renderer.createElement('div');
    this.renderer.addClass(this.resizer, 'column-resizer');
    this.renderer.setStyle(this.resizer, 'position', 'absolute');
    this.renderer.setStyle(this.resizer, 'right', '0');
    this.renderer.setStyle(this.resizer, 'top', '0');
    this.renderer.setStyle(this.resizer, 'width', '5px');
    this.renderer.setStyle(this.resizer, 'cursor', 'col-resize');
    this.renderer.appendChild(this.column, this.resizer);
  }

  private initializeResizeListener() {
    this.zone.runOutsideAngular(() => {
      fromEvent(this.resizer