Skip to content

Grouping with Aggregation

The Grouping with Aggregation feature allows you to organize your data into hierarchical groups and display custom aggregated values for each group. This is particularly useful for displaying summary statistics, totals, averages, or custom calculations for grouped data.

  • Hierarchical Grouping: Group data by multiple columns in a tree-like structure
  • Custom Aggregations: Define custom aggregation functions for different data types
  • Visual Indicators: Expandable/collapsible groups with aggregation values displayed
  • Flexible Templates: Customize how group headers and aggregations are displayed
Source code
import { defineCustomElements } from '@revolist/revogrid/loader';
import { currentTheme } from '../composables/useRandomData';
import { createGroupingData, columns, grouping } from './groupingData';
defineCustomElements();
export function load(parentSelector: string) {
const grid = document.createElement('revo-grid');
const { isDark } = currentTheme();
// Generate realistic data using faker.js
grid.source = createGroupingData(50);
grid.columns = columns;
grid.grouping = grouping;
// Grouping is a built-in feature, no plugin needed
grid.theme = isDark() ? 'darkCompact' : 'compact';
grid.hideAttribution = true;
document.querySelector(parentSelector)?.appendChild(grid);
}
import { groupingAggregation } from '@revolist/revogrid-pro';

Create custom aggregation functions for different columns. Important: Aggregation functions receive an array of complete data objects, not just the column values. You need to extract the specific property you want to aggregate:

const aggregations = {
price: (values: any[]) => {
const prices = values.map(item => item.price);
const sum = prices.reduce((acc, val) => acc + val, 0);
const avg = sum / prices.length;
return `Avg: $${avg.toFixed(2)}`;
},
quantity: (values: any[]) => {
const quantities = values.map(item => item.quantity);
const total = quantities.reduce((acc, val) => acc + val, 0);
return `Total: ${total}`;
},
product: (values: any[]) => {
return `${values.length} products`;
}
};

Use the groupingAggregation function to create a template that displays aggregations:

const groupTemplate = (h: any, props: any) => {
return groupingAggregation(h, props, aggregations);
};

Apply the group template to columns that will be used for grouping:

const columns = [
{ name: '🆔 ID', prop: 'id', size: 60 },
{ name: '📂 Category', prop: 'category', size: 120, template: groupTemplate },
{ name: '📁 Subcategory', prop: 'subcategory', size: 120, template: groupTemplate },
{ name: '📦 Product', prop: 'product', size: 150 },
{ name: '💰 Price', prop: 'price', size: 100 },
{ name: '📊 Quantity', prop: 'quantity', size: 100 },
{ name: '📅 Order Date', prop: 'orderDate', size: 120 },
{ name: '🚚 Delivery Date', prop: 'deliveryDate', size: 120 },
{ name: '👤 Customer', prop: 'customer', size: 150 },
{ name: '🌍 Region', prop: 'region', size: 100 },
{ name: '📋 Status', prop: 'status', size: 100 },
];

Define which columns should be used for grouping:

const grouping = {
props: ['orderDate', 'category', 'subcategory']
};
const grid = document.createElement('revo-grid');
grid.source = yourData;
grid.columns = columns;
grid.grouping = grouping;
// Grouping is a built-in feature, no plugin needed
// Average
price: (values: any[]) => {
const prices = values.map(item => item.price);
const avg = prices.reduce((acc, val) => acc + val, 0) / prices.length;
return `Avg: $${avg.toFixed(2)}`;
}
// Sum
quantity: (values: any[]) => {
const quantities = values.map(item => item.quantity);
const total = quantities.reduce((acc, val) => acc + val, 0);
return `Total: ${total}`;
}
// Min/Max
price: (values: any[]) => {
const prices = values.map(item => item.price);
const min = Math.min(...prices);
const max = Math.max(...prices);
return `Range: $${min} - $${max}`;
}
// Count
product: (values: any[]) => {
return `${values.length} products`;
}
// Concatenation
names: (values: any[]) => {
const names = values.map(item => item.name);
return names.join(', ');
}
// Unique count
categories: (values: any[]) => {
const categories = values.map(item => item.category);
const unique = new Set(categories);
return `${unique.size} unique categories`;
}
// Date range
orderDate: (values: any[]) => {
const dates = values.map(item => new Date(item.orderDate));
const sorted = dates.sort((a, b) => a.getTime() - b.getTime());
const earliest = sorted[0].toLocaleDateString();
const latest = sorted[sorted.length - 1].toLocaleDateString();
return `${earliest} - ${latest}`;
}

You can customize the appearance of group headers by modifying the template function:

const customGroupTemplate = (h: any, props: any) => {
const aggregationValue = getAggregationValue(props);
return h('div', {
style: {
display: 'flex',
alignItems: 'center',
gap: '8px',
padding: '4px 8px',
backgroundColor: '#f0f0f0',
borderRadius: '4px'
}
}, [
h('span', { style: { fontWeight: 'bold' } }, props.name),
h('span', { style: { fontSize: '12px', color: '#666' } }, `(${aggregationValue})`)
]);
};

You can apply different aggregation logic based on the column or group:

const conditionalAggregations = {
price: (values: any[]) => {
const prices = values.map(item => item.price);
const sum = prices.reduce((a, b) => a + b, 0);
const avg = sum / prices.length;
return `Avg: $${avg.toFixed(2)}`;
}
};
  1. Performance: Keep aggregation functions lightweight, especially with large datasets
  2. User Experience: Provide clear, meaningful aggregation labels
  3. Data Types: Ensure aggregation functions handle the correct data types
  4. Error Handling: Add validation for edge cases (empty arrays, null values)
  5. Accessibility: Use descriptive text for screen readers
  • Sales Reports: Group by region/category with revenue totals
  • Inventory Management: Group by department with stock quantities
  • Financial Data: Group by account type with balance summaries
  • Project Management: Group by team with task counts and completion rates