Field Metadata
Field metadata is the information Pivot uses to describe fields before they become row groups, column groups, filters, or measures. Use it to give users clearer labels, keep internal fields out of normal field lists, and make generated totals easier to read.
Source code
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();
import NumberColumnType from '@revolist/revogrid-column-numeral';
import {
PivotPlugin,
filterPivotDimensions,
type PivotConfig,
type PivotConfigDimension,
} from '@revolist/revogrid-enterprise';
import { AdvanceFilterPlugin, RowOddPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
const PIVOT_FIELD_METADATA_ROWS = [
{ region: 'North', channel: 'Retail', product: 'Desk', quarter: 'Q1', internalSegment: 'Expansion', revenue: 18400, units: 31 },
{ region: 'North', channel: 'Retail', product: '', quarter: 'Q2', internalSegment: 'Expansion', revenue: 22600, units: 38 },
{ region: null, channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 9600, units: 15 },
{ region: 'South', channel: '', product: 'Chair', quarter: 'Q1', internalSegment: 'Strategic', revenue: 14200, units: 24 },
{ region: 'South', channel: 'Direct', product: null, quarter: 'Q2', internalSegment: 'Strategic', revenue: 17300, units: 29 },
{ region: 'West', channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 11800, units: 19 },
{ region: 'West', channel: 'Direct', product: 'Chair', quarter: 'Q2', internalSegment: 'Expansion', revenue: 20500, units: 34 },
];
const PIVOT_FIELD_METADATA_DIMENSIONS: PivotConfigDimension[] = [
{ prop: 'region', name: 'Region', description: 'Sales territory with null labels enabled.' },
{ prop: 'channel', name: 'Channel', description: 'Go-to-market channel with empty labels enabled.' },
{ prop: 'product', name: 'Product', description: 'Product family with empty and null members.' },
{ prop: 'quarter', name: 'Quarter', description: 'Column quarter.' },
{ prop: 'internalSegment', name: 'Internal Segment', description: 'Hidden field revealed by show hidden.', hidden: true },
{ prop: 'revenue', name: 'Revenue', description: 'Currency measure.' },
{ prop: 'units', name: 'Units', description: 'Unit measure.' },
];
function createPivotConfig(): PivotConfig {
return {
dimensions: PIVOT_FIELD_METADATA_DIMENSIONS,
rows: ['region', 'channel', 'product'],
columns: ['quarter', 'internalSegment'],
filters: ['internalSegment'],
values: [
{ prop: 'revenue', aggregator: 'sum', label: 'Revenue $' },
{ prop: 'units', aggregator: 'sum', name: 'Units Sold' },
],
hasConfigurator: true,
fieldPanel: {
visible: true,
allowFieldDragging: true,
allowFieldRemoving: true,
showDataFields: true,
showRowFields: true,
showColumnFields: true,
showFilterFields: true,
},
groupLabels: {
empty: '(Empty)',
null: '(No value)',
},
totals: {
grandTotal: true,
subtotals: true,
disabledSubtotals: {
rows: { fields: ['channel'] },
columns: { levels: [0] },
},
subtotalLabel: 'Subtotal',
grandTotalLabel: 'Grand Total',
},
};
}
function renderFieldList(target: HTMLElement, search: string, showHidden: boolean) {
const dimensions = filterPivotDimensions(PIVOT_FIELD_METADATA_DIMENSIONS, search, { showHidden });
target.replaceChildren(
...dimensions.map((dimension) => {
const item = document.createElement('li');
const label = document.createElement('span');
item.className = 'flex items-center justify-between gap-2 border-b border-slate-200 py-1 text-xs last:border-b-0';
label.textContent = dimension.name ?? String(dimension.prop);
item.append(label);
if (dimension.hidden) {
const hiddenBadge = document.createElement('span');
hiddenBadge.className = 'rounded bg-slate-200 px-1.5 py-0.5 text-[10px] uppercase';
hiddenBadge.textContent = 'hidden';
item.append(hiddenBadge);
}
return item;
}),
);
}
export function load(parentSelector: string, rows: any[] = PIVOT_FIELD_METADATA_ROWS) {
const { isDark } = currentTheme();
const parent = document.querySelector(parentSelector);
const data = Array.isArray(rows) && rows.length > 0 ? rows : PIVOT_FIELD_METADATA_ROWS;
const wrapper = document.createElement('div');
wrapper.className = 'flex h-full min-h-[620px] w-full gap-3';
const metadataPanel = document.createElement('aside');
metadataPanel.className = 'flex w-64 shrink-0 flex-col gap-2 rounded border border-slate-200 p-3 text-sm';
metadataPanel.innerHTML = `
<label class="text-xs font-semibold uppercase">Field metadata</label>
<input class="pivot-field-search rounded border border-slate-300 px-2 py-1 text-sm" type="search" placeholder="Search fields" />
<label class="flex items-center gap-2 text-xs">
<input type="checkbox" />
Show hidden fields
</label>
<ul class="m-0 list-none p-0"></ul>
`;
const searchInput = metadataPanel.querySelector('input[type="search"]') as HTMLInputElement;
const showHiddenInput = metadataPanel.querySelector('input[type="checkbox"]') as HTMLInputElement;
const fieldList = metadataPanel.querySelector('ul') as HTMLUListElement;
const refreshList = () => renderFieldList(fieldList, searchInput.value, showHiddenInput.checked);
searchInput.addEventListener('input', refreshList);
showHiddenInput.addEventListener('change', refreshList);
refreshList();
const grid = document.createElement('revo-grid') as any;
grid.className = 'grow h-full w-full cell-border';
grid.range = true;
grid.resize = true;
grid.filter = true;
grid.colSize = 170;
grid.readonly = true;
grid.hideAttribution = true;
grid.theme = isDark() ? 'darkCompact' : 'compact';
grid.columnTypes = {
currency: new NumberColumnType('$0,0.00'),
integer: new NumberColumnType('0,0'),
};
grid.plugins = [PivotPlugin, AdvanceFilterPlugin, RowOddPlugin];
grid.columns = [];
Object.assign(grid, {
pivot: createPivotConfig(),
});
wrapper.append(metadataPanel, grid);
parent?.appendChild(wrapper);
grid.source = data;
return () => {
wrapper.remove();
};
}
<template>
<div class="flex h-full min-h-[620px] w-full gap-3">
<aside class="flex w-64 shrink-0 flex-col gap-2 rounded border border-slate-200 p-3 text-sm">
<label class="text-xs font-semibold uppercase">Field metadata</label>
<input
v-model="search"
class="pivot-field-search rounded border border-slate-300 px-2 py-1 text-sm"
type="search"
placeholder="Search fields"
/>
<label class="flex items-center gap-2 text-xs">
<input v-model="showHidden" type="checkbox" />
Show hidden fields
</label>
<ul class="m-0 list-none p-0">
<li
v-for="dimension in filteredDimensions"
:key="String(dimension.prop)"
class="flex items-center justify-between gap-2 border-b border-slate-200 py-1 text-xs last:border-b-0"
>
<span>{{ dimension.name ?? dimension.prop }}</span>
<span v-if="dimension.hidden" class="rounded bg-slate-200 px-1.5 py-0.5 text-[10px] uppercase">hidden</span>
</li>
</ul>
</aside>
<RevoGrid
class="grow h-full cell-border"
hide-attribution
range
resize
filter
:colSize="170"
:source="gridRows"
:pivot.prop="pivot"
:theme="isDark ? 'darkCompact' : 'compact'"
:plugins="plugins"
:column-types="columnTypes"
readonly
/>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import NumberColumnType from '@revolist/revogrid-column-numeral';
import RevoGrid, { type GridPlugin } from '@revolist/vue3-datagrid';
import {
PivotPlugin,
filterPivotDimensions,
type PivotConfig,
type PivotConfigDimension,
} from '@revolist/revogrid-enterprise';
import { AdvanceFilterPlugin, RowOddPlugin } from '@revolist/revogrid-pro';
import { currentThemeVue } from '../composables/useRandomData';
const PIVOT_FIELD_METADATA_ROWS = [
{ region: 'North', channel: 'Retail', product: 'Desk', quarter: 'Q1', internalSegment: 'Expansion', revenue: 18400, units: 31 },
{ region: 'North', channel: 'Retail', product: '', quarter: 'Q2', internalSegment: 'Expansion', revenue: 22600, units: 38 },
{ region: null, channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 9600, units: 15 },
{ region: 'South', channel: '', product: 'Chair', quarter: 'Q1', internalSegment: 'Strategic', revenue: 14200, units: 24 },
{ region: 'South', channel: 'Direct', product: null, quarter: 'Q2', internalSegment: 'Strategic', revenue: 17300, units: 29 },
{ region: 'West', channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 11800, units: 19 },
{ region: 'West', channel: 'Direct', product: 'Chair', quarter: 'Q2', internalSegment: 'Expansion', revenue: 20500, units: 34 },
];
const PIVOT_FIELD_METADATA_DIMENSIONS: PivotConfigDimension[] = [
{ prop: 'region', name: 'Region', description: 'Sales territory with null labels enabled.' },
{ prop: 'channel', name: 'Channel', description: 'Go-to-market channel with empty labels enabled.' },
{ prop: 'product', name: 'Product', description: 'Product family with empty and null members.' },
{ prop: 'quarter', name: 'Quarter', description: 'Column quarter.' },
{ prop: 'internalSegment', name: 'Internal Segment', description: 'Hidden field revealed by show hidden.', hidden: true },
{ prop: 'revenue', name: 'Revenue', description: 'Currency measure.' },
{ prop: 'units', name: 'Units', description: 'Unit measure.' },
];
const props = defineProps<{
rows?: any[];
}>();
const { isDark } = currentThemeVue();
const search = ref('');
const showHidden = ref(false);
const gridRows = computed(() => (Array.isArray(props.rows) && props.rows.length > 0 ? props.rows : PIVOT_FIELD_METADATA_ROWS));
const filteredDimensions = computed(() =>
filterPivotDimensions(PIVOT_FIELD_METADATA_DIMENSIONS, search.value, { showHidden: showHidden.value }),
);
const columnTypes = ref({
currency: new NumberColumnType('$0,0.00'),
integer: new NumberColumnType('0,0'),
});
const plugins: GridPlugin[] = [PivotPlugin, AdvanceFilterPlugin, RowOddPlugin];
const pivot = computed((): PivotConfig => ({
dimensions: PIVOT_FIELD_METADATA_DIMENSIONS,
rows: ['region', 'channel', 'product'],
columns: ['quarter', 'internalSegment'],
filters: ['internalSegment'],
values: [
{ prop: 'revenue', aggregator: 'sum', label: 'Revenue $' },
{ prop: 'units', aggregator: 'sum', name: 'Units Sold' },
],
hasConfigurator: true,
fieldPanel: {
visible: true,
allowFieldDragging: true,
allowFieldRemoving: true,
showDataFields: true,
showRowFields: true,
showColumnFields: true,
showFilterFields: true,
},
groupLabels: {
empty: '(Empty)',
null: '(No value)',
},
totals: {
grandTotal: true,
subtotals: true,
disabledSubtotals: {
rows: { fields: ['channel'] },
columns: { levels: [0] },
},
subtotalLabel: 'Subtotal',
grandTotalLabel: 'Grand Total',
},
}));
</script>
import { useMemo, useState } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import NumberColumnType from '@revolist/revogrid-column-numeral';
import {
PivotPlugin,
filterPivotDimensions,
type PivotConfig,
type PivotConfigDimension,
} from '@revolist/revogrid-enterprise';
import { AdvanceFilterPlugin, RowOddPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
const PIVOT_FIELD_METADATA_ROWS = [
{ region: 'North', channel: 'Retail', product: 'Desk', quarter: 'Q1', internalSegment: 'Expansion', revenue: 18400, units: 31 },
{ region: 'North', channel: 'Retail', product: '', quarter: 'Q2', internalSegment: 'Expansion', revenue: 22600, units: 38 },
{ region: null, channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 9600, units: 15 },
{ region: 'South', channel: '', product: 'Chair', quarter: 'Q1', internalSegment: 'Strategic', revenue: 14200, units: 24 },
{ region: 'South', channel: 'Direct', product: null, quarter: 'Q2', internalSegment: 'Strategic', revenue: 17300, units: 29 },
{ region: 'West', channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 11800, units: 19 },
{ region: 'West', channel: 'Direct', product: 'Chair', quarter: 'Q2', internalSegment: 'Expansion', revenue: 20500, units: 34 },
];
const PIVOT_FIELD_METADATA_DIMENSIONS: PivotConfigDimension[] = [
{ prop: 'region', name: 'Region', description: 'Sales territory with null labels enabled.' },
{ prop: 'channel', name: 'Channel', description: 'Go-to-market channel with empty labels enabled.' },
{ prop: 'product', name: 'Product', description: 'Product family with empty and null members.' },
{ prop: 'quarter', name: 'Quarter', description: 'Column quarter.' },
{ prop: 'internalSegment', name: 'Internal Segment', description: 'Hidden field revealed by show hidden.', hidden: true },
{ prop: 'revenue', name: 'Revenue', description: 'Currency measure.' },
{ prop: 'units', name: 'Units', description: 'Unit measure.' },
];
interface PivotFieldMetadataProps {
rows?: any[];
}
function createPivotConfig(): PivotConfig {
return {
dimensions: PIVOT_FIELD_METADATA_DIMENSIONS,
rows: ['region', 'channel', 'product'],
columns: ['quarter', 'internalSegment'],
filters: ['internalSegment'],
values: [
{ prop: 'revenue', aggregator: 'sum', label: 'Revenue $' },
{ prop: 'units', aggregator: 'sum', name: 'Units Sold' },
],
hasConfigurator: true,
fieldPanel: {
visible: true,
allowFieldDragging: true,
allowFieldRemoving: true,
showDataFields: true,
showRowFields: true,
showColumnFields: true,
showFilterFields: true,
},
groupLabels: {
empty: '(Empty)',
null: '(No value)',
},
totals: {
grandTotal: true,
subtotals: true,
disabledSubtotals: {
rows: { fields: ['channel'] },
columns: { levels: [0] },
},
subtotalLabel: 'Subtotal',
grandTotalLabel: 'Grand Total',
},
};
}
function PivotFieldMetadata({ rows }: PivotFieldMetadataProps) {
const { isDark } = currentTheme();
const [search, setSearch] = useState('');
const [showHidden, setShowHidden] = useState(false);
const data = useMemo(() => (Array.isArray(rows) && rows.length > 0 ? rows : PIVOT_FIELD_METADATA_ROWS), [rows]);
const filteredDimensions = useMemo(
() => filterPivotDimensions(PIVOT_FIELD_METADATA_DIMENSIONS, search, { showHidden }),
[search, showHidden],
);
const columnTypes = useMemo(
() => ({
currency: new NumberColumnType('$0,0.00'),
integer: new NumberColumnType('0,0'),
}),
[],
);
const plugins = useMemo(() => [PivotPlugin, AdvanceFilterPlugin, RowOddPlugin], []);
const pivot = useMemo(() => createPivotConfig(), []);
return (
<div className="flex h-full min-h-[620px] w-full gap-3">
<aside className="flex w-64 shrink-0 flex-col gap-2 rounded border border-slate-200 p-3 text-sm">
<label className="text-xs font-semibold uppercase">Field metadata</label>
<input
className="pivot-field-search rounded border border-slate-300 px-2 py-1 text-sm"
type="search"
placeholder="Search fields"
value={search}
onChange={(event) => setSearch(event.currentTarget.value)}
/>
<label className="flex items-center gap-2 text-xs">
<input
type="checkbox"
checked={showHidden}
onChange={(event) => setShowHidden(event.currentTarget.checked)}
/>
Show hidden fields
</label>
<ul className="m-0 list-none p-0">
{filteredDimensions.map((dimension) => (
<li
className="flex items-center justify-between gap-2 border-b border-slate-200 py-1 text-xs last:border-b-0"
key={String(dimension.prop)}
>
<span>{dimension.name ?? String(dimension.prop)}</span>
{dimension.hidden && <span className="rounded bg-slate-200 px-1.5 py-0.5 text-[10px] uppercase">hidden</span>}
</li>
))}
</ul>
</aside>
<RevoGrid
className="grow h-full cell-border"
hideAttribution
range
resize
filter
colSize={170}
source={data}
columns={[]}
pivot={pivot}
theme={isDark() ? 'darkCompact' : 'compact'}
plugins={plugins}
columnTypes={columnTypes}
readonly
/>
</div>
);
}
export default PivotFieldMetadata;
import { CommonModule } from '@angular/common';
import { Component, Input, NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import NumberColumnType from '@revolist/revogrid-column-numeral';
import {
PivotPlugin,
filterPivotDimensions,
type PivotConfig,
type PivotConfigDimension,
} from '@revolist/revogrid-enterprise';
import { AdvanceFilterPlugin, RowOddPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
const PIVOT_FIELD_METADATA_ROWS = [
{ region: 'North', channel: 'Retail', product: 'Desk', quarter: 'Q1', internalSegment: 'Expansion', revenue: 18400, units: 31 },
{ region: 'North', channel: 'Retail', product: '', quarter: 'Q2', internalSegment: 'Expansion', revenue: 22600, units: 38 },
{ region: null, channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 9600, units: 15 },
{ region: 'South', channel: '', product: 'Chair', quarter: 'Q1', internalSegment: 'Strategic', revenue: 14200, units: 24 },
{ region: 'South', channel: 'Direct', product: null, quarter: 'Q2', internalSegment: 'Strategic', revenue: 17300, units: 29 },
{ region: 'West', channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 11800, units: 19 },
{ region: 'West', channel: 'Direct', product: 'Chair', quarter: 'Q2', internalSegment: 'Expansion', revenue: 20500, units: 34 },
];
const PIVOT_FIELD_METADATA_DIMENSIONS: PivotConfigDimension[] = [
{ prop: 'region', name: 'Region', description: 'Sales territory with null labels enabled.' },
{ prop: 'channel', name: 'Channel', description: 'Go-to-market channel with empty labels enabled.' },
{ prop: 'product', name: 'Product', description: 'Product family with empty and null members.' },
{ prop: 'quarter', name: 'Quarter', description: 'Column quarter.' },
{ prop: 'internalSegment', name: 'Internal Segment', description: 'Hidden field revealed by show hidden.', hidden: true },
{ prop: 'revenue', name: 'Revenue', description: 'Currency measure.' },
{ prop: 'units', name: 'Units', description: 'Unit measure.' },
];
@Component({
selector: 'pivot-field-metadata-grid',
standalone: true,
imports: [CommonModule, RevoGrid],
schemas: [NO_ERRORS_SCHEMA],
template: `
<div class="flex h-full min-h-[620px] w-full gap-3">
<aside class="flex w-64 shrink-0 flex-col gap-2 rounded border border-slate-200 p-3 text-sm">
<label class="text-xs font-semibold uppercase">Field metadata</label>
<input
class="pivot-field-search rounded border border-slate-300 px-2 py-1 text-sm"
type="search"
placeholder="Search fields"
[value]="search"
(input)="setSearch($event)"
/>
<label class="flex items-center gap-2 text-xs">
<input
type="checkbox"
[checked]="showHidden"
(change)="setShowHidden($event)"
/>
Show hidden fields
</label>
<ul class="m-0 list-none p-0">
<li
*ngFor="let dimension of filteredDimensions"
class="flex items-center justify-between gap-2 border-b border-slate-200 py-1 text-xs last:border-b-0"
>
<span>{{ dimension.name || dimension.prop }}</span>
<span
*ngIf="dimension.hidden"
class="rounded bg-slate-200 px-1.5 py-0.5 text-[10px] uppercase"
>hidden</span>
</li>
</ul>
</aside>
<revo-grid
class="grow h-full cell-border"
[hideAttribution]="true"
[range]="true"
[resize]="true"
[filter]="true"
[colSize]="170"
[source]="gridRows"
[pivot]="pivot"
[theme]="theme"
[plugins]="plugins"
[columnTypes]="columnTypes"
[readonly]="true"
></revo-grid>
</div>
`,
encapsulation: ViewEncapsulation.None,
})
export class PivotFieldMetadataGridComponent {
@Input() rows?: any[];
search = '';
showHidden = false;
theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
columnTypes = {
currency: new NumberColumnType('$0,0.00'),
integer: new NumberColumnType('0,0'),
};
plugins = [PivotPlugin, AdvanceFilterPlugin, RowOddPlugin];
pivot: PivotConfig = {
dimensions: PIVOT_FIELD_METADATA_DIMENSIONS,
rows: ['region', 'channel', 'product'],
columns: ['quarter', 'internalSegment'],
filters: ['internalSegment'],
values: [
{ prop: 'revenue', aggregator: 'sum', label: 'Revenue $' },
{ prop: 'units', aggregator: 'sum', name: 'Units Sold' },
],
hasConfigurator: true,
fieldPanel: {
visible: true,
allowFieldDragging: true,
allowFieldRemoving: true,
showDataFields: true,
showRowFields: true,
showColumnFields: true,
showFilterFields: true,
},
groupLabels: {
empty: '(Empty)',
null: '(No value)',
},
totals: {
grandTotal: true,
subtotals: true,
disabledSubtotals: {
rows: { fields: ['channel'] },
columns: { levels: [0] },
},
subtotalLabel: 'Subtotal',
grandTotalLabel: 'Grand Total',
},
};
get gridRows() {
return Array.isArray(this.rows) && this.rows.length > 0 ? this.rows : PIVOT_FIELD_METADATA_ROWS;
}
get filteredDimensions() {
return filterPivotDimensions(PIVOT_FIELD_METADATA_DIMENSIONS, this.search, { showHidden: this.showHidden });
}
setSearch(event: Event) {
this.search = (event.target as HTMLInputElement).value;
}
setShowHidden(event: Event) {
this.showHidden = (event.target as HTMLInputElement).checked;
}
}
Hidden Dimensions
Section titled “Hidden Dimensions”Mark a dimension as hidden when it should still be available to the Pivot config but should not appear in normal field lists.
const dimensions = [ { prop: 'region', name: 'Region' }, { prop: 'quarter', name: 'Quarter' }, { prop: 'internalSegment', name: 'Internal Segment', description: 'Internal reporting segment.', hidden: true, },];
const pivot = { dimensions, rows: ['region'], columns: ['quarter', 'internalSegment'], filters: ['internalSegment'], values: [{ prop: 'revenue', aggregator: 'sum' }],};Hidden does not remove the field from the Pivot engine. In this example, internalSegment can still be used in columns and filters; it is only hidden from helper-driven field lists unless you explicitly show hidden fields.
Show Hidden Fields In Search
Section titled “Show Hidden Fields In Search”Use filterPivotDimensions for searchable field lists. Hidden dimensions are omitted by default. Pass { showHidden: true } when you want an admin or advanced-user mode that reveals them.
import { filterPivotDimensions } from '@revolist/revogrid-enterprise';
const visibleFields = filterPivotDimensions(dimensions, searchText);
const allFields = filterPivotDimensions(dimensions, searchText, { showHidden: true,});The helper searches prop, name, and description, so descriptions are useful for discovery even when the visible label is short.
Empty And Null Group Labels
Section titled “Empty And Null Group Labels”Real data often contains empty strings, null, or undefined. Pivot can replace those values with user-facing labels in row and column groups.
const pivot = { rows: ['region', 'channel'], columns: ['quarter'], values: [{ prop: 'revenue', aggregator: 'sum' }], groupLabels: { empty: '(Empty)', null: '(No value)', },};groupLabels.empty is used for empty strings (''). groupLabels.null is used for null and undefined. Without these options, the current implementation displays empty labels for those group members.
Measure Aliases
Section titled “Measure Aliases”Each value can have its own display label. Use label for the preferred generated measure name. Use name as a fallback when label is not set.
const pivot = { values: [ { prop: 'revenue', aggregator: 'sum', label: 'Revenue $' }, { prop: 'units', aggregator: 'sum', name: 'Units Sold' }, ],};Aliases are useful when the source field name is technical, abbreviated, or shared by multiple reports.
Disabled Subtotals
Section titled “Disabled Subtotals”Global subtotals can stay enabled while specific subtotal rows or subtotal columns are skipped. Target subtotals by axis field or by zero-based axis level.
const pivot = { rows: ['region', 'channel', 'product'], columns: ['quarter', 'internalSegment'], values: [{ prop: 'revenue', aggregator: 'sum' }], totals: { grandTotal: true, subtotals: true, disabledSubtotals: { rows: { fields: ['channel'], }, columns: { levels: [0], }, }, },};In this example, Pivot keeps subtotal generation enabled overall, skips row subtotals for the channel field, and skips column subtotals at level 0.
Complete Config
Section titled “Complete Config”const pivot = { dimensions: [ { prop: 'region', name: 'Region', description: 'Sales territory.' }, { prop: 'channel', name: 'Channel', description: 'Go-to-market channel.' }, { prop: 'product', name: 'Product' }, { prop: 'quarter', name: 'Quarter' }, { prop: 'internalSegment', name: 'Internal Segment', hidden: true }, { prop: 'revenue', name: 'Revenue' }, { prop: 'units', name: 'Units' }, ], rows: ['region', 'channel', 'product'], columns: ['quarter', 'internalSegment'], filters: ['internalSegment'], values: [ { prop: 'revenue', aggregator: 'sum', label: 'Revenue $' }, { prop: 'units', aggregator: 'sum', name: 'Units Sold' }, ], groupLabels: { empty: '(Empty)', null: '(No value)', }, totals: { grandTotal: true, subtotals: true, disabledSubtotals: { rows: { fields: ['channel'] }, columns: { levels: [0] }, }, },};Common Mistakes
Section titled “Common Mistakes”- Expecting
hidden: trueto prevent a field from being used inrows,columns,filters, orvalues. It only affects field-list helpers. - Passing
{ showHidden: true }to the Pivot config. It belongs tofilterPivotDimensions, notpivot. - Using only
groupLabels.emptywhen the source data containsnullorundefined. - Disabling
totals.subtotalsand then expectingdisabledSubtotalsto do anything. Selective disabling only matters whensubtotals: true.