Creating a Customizable Grid Component in Angular with ngTemplateOutle

Creating a Customizable Grid Component in Angular with ngTemplateOutle

In this tutorial, we will build an advanced grid component in Angular that supports customization, dynamic templates, data binding, and event handling. The grid component will allow users to define custom cell templates, specify columns and data, and respond to row click events. Let’s dive into the details of each part of the grid component.

GridTemplateContext Interface

First, let’s define the GridTemplateContext interface. This interface will serve as the context for the cell templates and provide access to the data and row information.

interface GridTemplateContext {
  $implicit: any;
  row: any;
  [key: string]: any;
}

The GridTemplateContext interface has two properties:

  • $implicit: Represents the data for the current cell.
  • row: Represents the entire row data.

GridCellTemplateDirective

To enable custom cell templates, we need to create a directive called GridCellTemplateDirective. This directive will be used to define custom templates for specific columns in the grid.

import { Directive, Input, TemplateRef } from '@angular/core';

@Directive({
  selector: '[appGridCellTemplate]'
})
export class GridCellTemplateDirective {
  @Input('appGridCellTemplate') columnName!: string;

  constructor(public templateRef: TemplateRef<GridTemplateContext>) {}
}

The GridCellTemplateDirective is decorated with @Directive and has an input property called columnName, which represents the name of the column associated with the template. The directive also has a TemplateRef injected, which will hold the reference to the custom template.

GridComponent

Now, let’s implement the main GridComponent that will render the grid based on the provided columns, data, and templates.

import { Component, Input, Output, EventEmitter, ContentChildren, QueryList, TemplateRef, ContentChild } from '@angular/core';

@Component({
  selector: 'app-grid',
  template: `
    <table>
      <thead>
        <tr>
          <th *ngFor="let column of columns">{{ column }}</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let item of data" (click)="onRowClick(item)">
          <td *ngFor="let column of columns">
            <ng-container
              *ngTemplateOutlet="
                getCellTemplate(column);
                context: { $implicit: item[column], row: item }
              "
            ></ng-container>
          </td>
        </tr>
      </tbody>
    </table>
  `,
  styleUrls: ['./grid.component.css']
})
export class GridComponent {
  @Input() columns!: string[];
  @Input() data!: any[];
  @Output() rowClick: EventEmitter<any> = new EventEmitter();
  @ContentChildren(GridCellTemplateDirective) cellTemplates!: QueryList<GridCellTemplateDirective>;
  @ContentChild('defaultTemplate', { static: true }) defaultTemplate!: TemplateRef<GridTemplateContext>;

  getCellTemplate(column: string): TemplateRef<GridTemplateContext> {
    const template = this.cellTemplates.find(tmpl => tmpl.columnName === column);
    return template ? template.templateRef : this.defaultTemplate;
  }

  onRowClick(row: any) {
    this.rowClick.emit(row);
  }
}

The GridComponent is decorated with @Component and has several properties:

  • columns: Represents the column names of the grid.
  • data: Represents the data to be rendered in the grid.
  • rowClick: Represents an output event emitter that emits the clicked row data.
  • cellTemplates: Represents the list of GridCellTemplateDirective instances provided by the user.
  • defaultTemplate: Represents the default template provided by the user.

The GridComponent template consists of a <table> element that renders the grid structure. The <thead> section renders the column headers, and the <tbody> section iterates over the data and renders the rows. Inside each row, the <td> elements iterate over the columns and use ngTemplateOutlet to dynamically render the appropriate cell template.

The getCellTemplate method is responsible for determining the correct cell template based on the column name. It searches for a matching template in the cellTemplates list and returns it. If no matching template is found, it falls back to the default template.

The onRowClick method handles the row click event and emits the clicked row data using the rowClick event emitter.

Usage

To use the GridComponent in your application, follow these steps:

  1. Import the necessary dependencies in your module:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { GridComponent, GridCellTemplateDirective } from './grid/grid.component';

@NgModule({
  declarations: [AppComponent, GridComponent, GridCellTemplateDirective],
  imports: [BrowserModule],
  bootstrap: [AppComponent]
})
export class AppModule {}
  1. Define the grid component in your template:
<app-grid [columns]="gridColumns" [data]="gridData" (rowClick)="onRowClicked($event)">
  <ng-template appGridCellTemplate="name" let-item let-row>
    <span>Name: {{ item }}</span>
    <br />
    <span>Row Data: {{ row | json }}</span>
  </ng-template>
  <ng-template appGridCellTemplate="age" let-item>
    <span>Age: {{ item }}</span>
  </ng-template>
  <ng-template #defaultTemplate let-item>
    <span>{{ item }}</span>
  </ng-template>
</app-grid>

In this example, we provide custom cell templates for the “name” and “age” columns using the appGridCellTemplate directive. We also define a default template using the #defaultTemplate reference. The grid component receives the columns and data as inputs, and the (rowClick) event allows you to handle row click events.

That’s it! You have now successfully built an advanced grid component in Angular that supports customization and dynamic templates.


I hope this blog post helps you understand the implementation of the Angular advanced grid component and how each part contributes to its functionality. Feel free to customize and extend the component based on your specific requirements. Happy coding!

Please do not post any spam link in the comment box😊

إرسال تعليق (0)
أحدث أقدم