NgVuiGrid Templates & Actions

Comprehensive guide to custom templates and action columns with full Angular template syntax support

🎨
Custom Templates
Action Columns
🔧
Type Safety
🚀
High Performance

Templates Overview

The ng-vui-grid component supports custom column templates with full Angular template syntax. You can create action columns, custom cell renderers, and header templates with complete control over the presentation and behavior.

🎯

Template Power

Create rich, interactive grid experiences with action buttons, status badges, progress indicators, and complex UI components directly in your grid cells.

Template Features

Custom Cell Templates

Full Angular template syntax with row data, column info, and row index

Action Columns

Buttons, dropdowns, and custom UI components with event handling

Header Templates

Custom column headers with sorting indicators and filters

Template Context

Rich context with value, row data, column definition, and index

Programmatic Columns

Define columns programmatically with custom renderers

Dual Approaches

Both declarative (template-based) and programmatic approaches

Template Approaches

🔧 1. Template-Based Approach (Declarative)

Define templates directly in your component template using Angular directive syntax.

Complete Example with Multiple Column Types

<ng-vui-grid
  [rowData]="data"
  [gridOptions]="gridOptions"
>
  <!-- Regular column with custom avatar template -->
  <ng-template ngVuiGridColumn="name" headerName="Full Name" [width]="200">
    <ng-template let-value let-row="row" let-rowIndex="rowIndex">
      <div class="flex items-center space-x-3">
        <div class="w-8 h-8 bg-violet-100 rounded-full flex items-center justify-center">
          <span class="text-sm font-medium text-violet-600">
            {{ row.name.charAt(0).toUpperCase() }}
          </span>
        </div>
        <span class="font-medium text-gray-900">{{ value }}</span>
      </div>
    </ng-template>
  </ng-template>

  <!-- Action column with edit and delete buttons -->
  <ng-template ngVuiGridColumn="actions" headerName="Actions" [width]="200" [sortable]="false">
    <ng-template let-row="row" let-rowIndex="rowIndex">
      <div class="flex items-center space-x-2">
        <button
          (click)="editRow(row, rowIndex)"
          class="btn btn-sm btn-violet"
          title="Edit"
        >
          <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                  d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
          </svg>
        </button>

        <button
          (click)="deleteRow(row, rowIndex)"
          class="btn btn-sm btn-red"
          title="Delete"
        >
          <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                  d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
          </svg>
        </button>
      </div>
    </ng-template>
  </ng-template>

  <!-- Status column with colored badges -->
  <ng-template ngVuiGridColumn="status" headerName="Status" [width]="120">
    <ng-template let-value let-row="row">
      <span
        class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"
        [class]="getStatusClass(value)"
      >
        {{ value }}
      </span>
    </ng-template>
  </ng-template>
</ng-vui-grid>

⚙️ 2. Programmatic Approach

Define columns programmatically in your TypeScript component for dynamic configurations.

Component Setup

export class MyComponent {
  columnDefs: ColumnDefinition[] = [
    {
      field: 'name',
      headerName: 'Full Name',
      width: 200,
      cellTemplate: this.nameTemplate // TemplateRef
    },
    {
      field: 'email',
      headerName: 'Email',
      width: 250,
      valueFormatter: ({ value }) => value.toLowerCase()
    },
    {
      field: 'actions',
      headerName: 'Actions',
      width: 200,
      sortable: false,
      filterable: false,
      cellTemplate: this.actionsTemplate // TemplateRef
    }
  ];

  // Get template references in component
  @ViewChild('nameTemplate') nameTemplate!: TemplateRef<any>;
  @ViewChild('actionsTemplate') actionsTemplate!: TemplateRef<any>;
}

Template Context

Context Variables Available

Templates receive a rich context object with access to the cell value, row data, column definition, and row index for complete customization control.

interface TemplateContext {
  $implicit: any;           // The cell value (default template variable)
  value: any;               // The cell value (explicit access)
  row: RowData;             // The entire row data object
  column: ColumnDefinition; // Column configuration
  rowIndex: number;         // Zero-based row index
}

Template Usage Examples

let-value - Gets the cell value
let-row="row" - Gets the entire row
let-index="rowIndex" - Gets row index
let-col="column" - Gets column config

Context Usage

• Access any property from the row data
• Use row index for alternating styles
• Check column configuration for conditional logic
• Format values based on cell content

Action Column Examples

Simple Action Buttons

<ng-template ngVuiGridColumn="actions" headerName="Actions" [sortable]="false">
  <ng-template let-row="row" let-rowIndex="rowIndex">
    <div class="flex items-center space-x-2">
      <button (click)="edit(row)" class="btn btn-sm btn-violet">Edit</button>
      <button (click)="delete(row)" class="btn btn-sm btn-red">Delete</button>
    </div>
  </ng-template>
</ng-template>

Dropdown Menu Actions

<ng-template ngVuiGridColumn="actions" headerName="Actions" [sortable]="false">
  <ng-template let-row="row" let-rowIndex="rowIndex">
    <div class="relative">
      <button (click)="toggleMenu(rowIndex)" class="btn btn-sm">
        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                d="M12 5v.01M12 12v.01M12 19v.01"/>
        </svg>
      </button>

      @if (openMenuIndex === rowIndex) {
        <div class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg z-10">
          <button (click)="edit(row)"
                  class="block w-full text-left px-4 py-2 hover:bg-gray-100">
            Edit
          </button>
          <button (click)="duplicate(row)"
                  class="block w-full text-left px-4 py-2 hover:bg-gray-100">
            Duplicate
          </button>
          <button (click)="delete(row)"
                  class="block w-full text-left px-4 py-2 hover:bg-gray-100 text-red-600">
            Delete
          </button>
        </div>
      }
    </div>
  </ng-template>
</ng-template>

Icon-Only Actions

<ng-template ngVuiGridColumn="actions" headerName="" [width]="120" [sortable]="false">
  <ng-template let-row="row">
    <div class="flex items-center justify-center space-x-1">
      <button (click)="edit(row)"
              class="p-2 hover:bg-gray-100 rounded"
              title="Edit">
        <svg class="w-4 h-4 text-violet-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
        </svg>
      </button>
      <button (click)="view(row)"
              class="p-2 hover:bg-gray-100 rounded"
              title="View">
        <svg class="w-4 h-4 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
        </svg>
      </button>
      <button (click)="delete(row)"
              class="p-2 hover:bg-gray-100 rounded"
              title="Delete">
        <svg class="w-4 h-4 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
        </svg>
      </button>
    </div>
  </ng-template>
</ng-template>

Custom Cell Renderers

Avatar with Name & Email

<ng-template ngVuiGridColumn="user" headerName="User" [width]="250">
  <ng-template let-row="row">
    <div class="flex items-center space-x-3">
      <img
        [src]="row.avatar || '/default-avatar.png'"
        [alt]="row.name"
        class="w-8 h-8 rounded-full"
      />
      <div>
        <div class="font-medium text-gray-900">{{ row.name }}</div>
        <div class="text-sm text-gray-500">{{ row.email }}</div>
      </div>
    </div>
  </ng-template>
</ng-template>

Progress Bar

<ng-template ngVuiGridColumn="progress" headerName="Progress" [width]="150">
  <ng-template let-value="value">
    <div class="flex items-center space-x-2">
      <div class="flex-1 bg-gray-200 rounded-full h-2">
        <div
          class="bg-violet-600 h-2 rounded-full transition-all duration-300"
          [style.width.%]="value"
        ></div>
      </div>
      <span class="text-sm text-gray-600">{{ value }}%</span>
    </div>
  </ng-template>
</ng-template>

Tags & Chips

<ng-template ngVuiGridColumn="tags" headerName="Tags" [width]="200">
  <ng-template let-value="value">
    <div class="flex flex-wrap gap-1">
      @for (tag of value; track tag) {
        <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800">
          {{ tag }}
        </span>
      }
    </div>
  </ng-template>
</ng-template>

Status Badge

<ng-template ngVuiGridColumn="status" headerName="Status" [width]="120">
  <ng-template let-value="value">
    <span
      class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"
      [ngClass]="{
        'bg-green-100 text-green-800': value === 'Active',
        'bg-red-100 text-red-800': value === 'Inactive',
        'bg-yellow-100 text-yellow-800': value === 'Pending'
      }"
    >
      {{ value }}
    </span>
  </ng-template>
</ng-template>

Component Methods

Implement these methods in your component to handle action column interactions:

export class MyComponent {
  openMenuIndex: number | null = null;

  editRow(row: RowData, index: number): void {
    console.log('Edit:', row);
    // Implement edit logic
    // Example: Open edit dialog or navigate to edit page
  }

  deleteRow(row: RowData, index: number): void {
    if (confirm('Are you sure you want to delete this item?')) {
      this.rowData = this.rowData.filter(r => r.id !== row.id);
      // Or call your delete service
      this.dataService.delete(row.id).subscribe(() => {
        this.loadData();
      });
    }
  }

  viewRow(row: RowData, index: number): void {
    console.log('View:', row);
    // Implement view logic
    // Example: Open view dialog or navigate to detail page
  }

  duplicateRow(row: RowData, index: number): void {
    const newRow = {
      ...row,
      id: this.generateId(),
      name: `${row.name} (Copy)`
    };
    this.rowData = [...this.rowData, newRow];
  }

  toggleMenu(index: number): void {
    this.openMenuIndex = this.openMenuIndex === index ? null : index;
  }

  getStatusClass(status: string): string {
    switch (status) {
      case 'Active': return 'bg-green-100 text-green-800';
      case 'Inactive': return 'bg-red-100 text-red-800';
      case 'Pending': return 'bg-yellow-100 text-yellow-800';
      default: return 'bg-gray-100 text-gray-800';
    }
  }

  private generateId(): string {
    return Math.random().toString(36).substr(2, 9);
  }
}

Benefits of Template System

🔧

Flexible

Full Angular template syntax support with all directives and pipes

🛡️

Type-safe

Strong typing with TypeScript interfaces and compile-time checks

Performant

Templates are compiled and cached for optimal performance

Accessible

Full control over DOM structure and ARIA attributes

🎨

Styled

Easy integration with Tailwind CSS or any CSS framework

🔄

Reusable

Templates can be shared across components and applications

Migration from Other Grids

Easy Migration

The NgVuiGrid template syntax is designed to be intuitive and similar to other popular Angular grid libraries, making migration straightforward.

❌ Other Grid Libraries

<!-- ngx-datatable example -->
<ngx-datatable>
  <ngx-datatable-column name="actions">
    <ng-template let-value="value" let-row="row" ngx-datatable-cell-template>
      <!-- template content -->
    </ng-template>
  </ngx-datatable-column>
</ngx-datatable>

✅ NgVuiGrid

<!-- NgVuiGrid equivalent -->
<ng-vui-grid>
  <ng-template ngVuiGridColumn="actions">
    <ng-template let-value="value" let-row="row">
      <!-- template content -->
    </ng-template>
  </ng-template>
</ng-vui-grid>

Migration Checklist

  • Update template directive from library-specific to ngVuiGridColumn
  • Keep existing template variables (value, row, etc.) - they work the same
  • Update column configuration to use NgVuiGrid's ColumnDefinition
  • Replace grid options with NgVuiGrid's GridOptions interface

Template Best Practices

⚡ Performance Tips

  • Keep templates simple and avoid complex calculations
  • Use OnPush change detection for template components
  • Implement trackBy functions for better performance
  • Cache computed values in component methods

🎨 Template Design

  • Use consistent spacing and alignment
  • Provide clear visual feedback for interactive elements
  • Use tooltips for icon-only actions
  • Follow your application's design system

♿ Accessibility

  • Add proper ARIA labels to action buttons
  • Ensure keyboard navigation works correctly
  • Use semantic HTML elements where possible
  • Provide alternative text for images and icons

🛡️ Error Handling

  • Handle null and undefined values gracefully
  • Validate data before performing actions
  • Provide user feedback for long-running operations
  • Implement proper error boundaries