Skip to content

Templates ​

Pantanal Grid provides extensive template support for customizing the appearance and behavior of cells, headers, footers, and rows.

Cell Templates ​

Customize cell rendering using the template property:

vue
<script setup lang="ts">
import { h } from 'vue'
import { PantanalGrid, type ColumnDef } from '@pantanal/grid'

const columns: ColumnDef[] = [
  {
    field: 'status',
    title: 'Status',
    template: ({ value }) => {
      return h('span', {
        class: value === 'active' ? 'text-green-500' : 'text-red-500'
      }, value)
    }
  }
]
</script>

String Template ​

Use HTML string templates:

vue
<script setup lang="ts">
const columns: ColumnDef[] = [
  {
    field: 'price',
    title: 'Price',
    template: ({ value }) => `<strong class="text-blue-600">$${value.toFixed(2)}</strong>`
  }
]
</script>

Function Template ​

Use function templates for dynamic content:

vue
<script setup lang="ts">
const columns: ColumnDef[] = [
  {
    field: 'rating',
    title: 'Rating',
    template: ({ value, row }) => {
      const stars = '⭐'.repeat(Math.floor(value))
      return `<div>${stars} (${value}/5)</div>`
    }
  }
]
</script>

Header Templates ​

Customize column headers:

vue
<script setup lang="ts">
const columns: ColumnDef[] = [
  {
    field: 'name',
    title: 'Name',
    headerTemplate: (column) => `<div class="font-bold text-blue-600">${column.title}</div>`
  }
]
</script>

Customize footer cells (for aggregates):

vue
<script setup lang="ts">
const columns: ColumnDef[] = [
  {
    field: 'price',
    title: 'Price',
    footerTemplate: (aggregates) => {
      const sum = aggregates.price_sum || 0
      const avg = aggregates.price_avg || 0
      return `<div>Sum: $${sum.toFixed(2)} | Avg: $${avg.toFixed(2)}</div>`
    }
  }
]
</script>

Group Header Templates ​

Customize group headers:

vue
<script setup lang="ts">
const columns: ColumnDef[] = [
  {
    field: 'category',
    title: 'Category',
    groupHeaderTemplate: (group) => {
      return `<div class="font-bold">
        ${group.field}: ${group.value} 
        <span class="text-gray-500">(${group.items.length} items)</span>
      </div>`
    }
  }
]
</script>

Customize group footers:

vue
<script setup lang="ts">
const columns: ColumnDef[] = [
  {
    field: 'price',
    title: 'Price',
    groupFooterTemplate: (group) => {
      const sum = group.aggregates?.price_sum || 0
      return `<div class="font-semibold">Total: $${sum.toFixed(2)}</div>`
    }
  }
]
</script>

Row Templates ​

Customize entire rows:

vue
<script setup lang="ts">
const rowTemplate = (row: any, rowIndex: number) => {
  return `
    <tr class="${rowIndex % 2 === 0 ? 'bg-gray-50' : 'bg-white'}">
      <td>${row.id}</td>
      <td class="font-bold">${row.name}</td>
      <td>$${row.price.toFixed(2)}</td>
    </tr>
  `
}
</script>

<template>
  <PantanalGrid
    :rows="rows"
    :columns="columns"
    key-field="id"
    :row-template="rowTemplate"
  />
</template>

Detail Template (Master-Detail) ​

Add expandable detail rows:

vue
<script setup lang="ts">
const detailTemplate = (row: any) => {
  return `
    <div class="p-4 bg-gray-50">
      <h3 class="font-bold">Details</h3>
      <p>Category: ${row.category}</p>
      <p>Stock: ${row.stock}</p>
      <p>Description: ${row.description}</p>
    </div>
  `
}
</script>

<template>
  <PantanalGrid
    :rows="rows"
    :columns="columns"
    key-field="id"
    :detail-template="detailTemplate"
  />
</template>

Using Slots ​

Use Vue slots for more complex templates:

vue
<template>
  <PantanalGrid
    :rows="rows"
    :columns="columns"
    key-field="id"
  >
    <template #cell="{ column, row, value }">
      <div v-if="column.field === 'status'">
        <span :class="value === 'active' ? 'text-green-500' : 'text-red-500'">
          {{ value }}
        </span>
      </div>
      <div v-else>{{ value }}</div>
    </template>
  </PantanalGrid>
</template>

Complete Example ​

vue
<script setup lang="ts">
import { ref } from 'vue'
import { PantanalGrid, type ColumnDef } from '@pantanal/grid'

const rows = ref([
  { 
    id: 1, 
    name: 'Product A', 
    price: 29.99, 
    category: 'Electronics',
    status: 'active',
    rating: 4.5,
    stock: 150
  },
  { 
    id: 2, 
    name: 'Product B', 
    price: 49.99, 
    category: 'Clothing',
    status: 'inactive',
    rating: 3.8,
    stock: 75
  }
])

const columns: ColumnDef[] = [
  { field: 'id', title: 'ID', width: 80 },
  {
    field: 'name',
    title: 'Name',
    headerTemplate: (col) => `<div class="font-bold">${col.title}</div>`
  },
  {
    field: 'price',
    title: 'Price',
    template: ({ value }) => `<strong class="text-blue-600">$${value.toFixed(2)}</strong>`
  },
  {
    field: 'status',
    title: 'Status',
    template: ({ value }) => {
      const color = value === 'active' ? 'green' : 'red'
      return `<span class="text-${color}-500 font-semibold">${value}</span>`
    }
  },
  {
    field: 'rating',
    title: 'Rating',
    template: ({ value }) => {
      const stars = '⭐'.repeat(Math.floor(value))
      return `<div>${stars} ${value}/5</div>`
    }
  }
]

const detailTemplate = (row: any) => {
  return `
    <div style="padding: 1rem; background: #f3f4f6;">
      <h3 style="font-weight: bold; margin-bottom: 0.5rem;">Product Details</h3>
      <p><strong>Category:</strong> ${row.category}</p>
      <p><strong>Stock:</strong> ${row.stock}</p>
      <p><strong>Price:</strong> $${row.price.toFixed(2)}</p>
    </div>
  `
}
</script>

<template>
  <PantanalGrid
    :rows="rows"
    :columns="columns"
    key-field="id"
    :detail-template="detailTemplate"
    locale="en"
  />
</template>

Tips ​

  • Use templates for custom formatting and styling
  • Templates can return Vue components, HTML strings, or use render functions
  • Use slots for complex interactive templates
  • Detail templates are great for showing additional information
  • Group templates help visualize hierarchical data
  • Footer templates are useful for displaying aggregates