Skip to content

Master Rows

The Master Row Plugin enhances the RevoGrid grid component by enabling the creation and management of master-detail rows. It is an extremely complex and advanced feature, despite its seemingly simple nature. Read more below.

Source code
TypeScript ts
import { defineCustomElements } from '@revolist/revogrid/loader';
import { EXPAND_COLUMN, MasterRowPlugin, CellColumnFocusVerifyPlugin, ColumnStretchPlugin, OverlayPlugin, type RowMasterConfig } from '@revolist/revogrid-pro';
import { makeData, type Person } from '../composables/makeData';
import { currentTheme } from '../composables/useRandomData';

defineCustomElements();

export function load(parentSelector: string) {
  const grid = document.createElement('revo-grid');
  const { isDark } = currentTheme();

  grid.theme = isDark() ? 'darkCompact' : 'compact';
  grid.source = makeData(500, 20);
  grid.columns = [
    { prop: 'expand', focus: () => false, ...EXPAND_COLUMN },
    { name: 'First Name', prop: 'firstName', size: 180 },
    { name: 'Last Name', prop: 'lastName' },
  ];

  grid.plugins = [
    MasterRowPlugin,
    OverlayPlugin,
    CellColumnFocusVerifyPlugin,
    ColumnStretchPlugin,
  ];

  const masterRow: RowMasterConfig = {
    rowHeight: 60,
    template: (h, data) => {
      const subItems = data.model.subRows?.map((subRow: Person) =>
        h('span', { class: { 'master-row': true } }, [subRow.firstName + ' ' + subRow.lastName, ', ']),
      );
      return h('div', null, [h('strong', {}, 'Team: '), subItems]);
    },
  };

  Object.assign(grid, {
    masterRow,
    stretch: 'last',
  })

  document.querySelector(parentSelector)?.appendChild(grid);
}
Vue vue
<template>
  <RevoGrid
    class="rounded-lg overflow-hidden"
    :theme="isDark ? 'darkCompact' : 'compact'"
    :source="source"
    :columns="columns"
    :plugins="plugins"
    :additionalData="additionalData"
    hide-attribution
  />
</template>

<script setup lang="ts">
import RevoGrid, { type ColumnRegular } from '@revolist/vue3-datagrid';
import {
  EXPAND_COLUMN,
  MasterRowPlugin,
  CellColumnFocusVerifyPlugin,
  ColumnStretchPlugin,
  OverlayPlugin,
  RowOddPlugin,
  RowSelectPlugin,
  type RowMasterConfig,
} from '@revolist/revogrid-pro';
import { ref } from 'vue';
import { currentThemeVue } from '../composables/useRandomData';
import { makeData, allColumns, type Person } from '../composables/makeData';

const { isDark } = currentThemeVue();

const source = ref(makeData(100, 5));

const columns: ColumnRegular[] = [
  { prop: 'expand', focus: () => false, ...EXPAND_COLUMN },
  ...allColumns(),
];
columns[1].pin = 'colPinStart';
columns[2].rowSelect = true;
columns[5].pin = 'colPinEnd';

const plugins = [
  MasterRowPlugin,
  OverlayPlugin,
  CellColumnFocusVerifyPlugin,
  ColumnStretchPlugin,
  RowOddPlugin,
  RowSelectPlugin,
];

const masterRow: RowMasterConfig = {
  rowHeight: 250,
  template: (h, data) => {
    const subItems = data.model.subRows?.map((subRow: Person) =>
      h('li', { class: { 'master-row flex gap-2 items-center': true } }, [
        h('img', { class: 'rounded-lg', src: subRow.avatar, width: 20, height: 20 }),
        subRow.fullName,
      ]),
    );
    return h('div', { class: 'flex gap-5 items-center my-2 p-2' }, [
      h('img', { class: 'rounded-lg self-start', src: data.model.avatar }),
      h('div', { class: 'flex flex-col gap-2' }, [
        h('span', { class: 'font-bold' }, [data.model.fullName, ' | ', data.model.age, ' years old']),
        h('span', { class: 'text-sm text-gray-500' }, data.model.email),
        h('span', { class: 'font-bold' }, 'Relatives:'),
        h('ul', { class: 'list-disc list-inside px-0' }, subItems),
      ]),
    ]);
  },
};

const additionalData = {
  masterRow,
  stretch: 'last',
};
</script>

<style scoped>
:deep(.revo-master-row) {
  background-color: var(--revo-grid-background);
  box-shadow: 0 1px 0 var(--revo-grid-cell-border), 0 2px 5px rgba(0, 0, 0, 0.1) inset;
}
</style>
React tsx
import React, { useMemo } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import {
  EXPAND_COLUMN,
  MasterRowPlugin,
  CellColumnFocusVerifyPlugin,
  ColumnStretchPlugin,
  OverlayPlugin,
  type RowMasterConfig,
} from '@revolist/revogrid-pro';
import { makeData, type Person } from '../composables/makeData';
import { currentTheme } from '../composables/useRandomData';

const { isDark } = currentTheme();

function RowMaster() {
  const source = makeData(500, 20);

  const columns = useMemo(
    () => [
      { prop: 'expand', focus: () => false, ...EXPAND_COLUMN },
      { name: 'First Name', prop: 'firstName', size: 180 },
      { name: 'Last Name', prop: 'lastName' },
    ],
    [],
  );

  const plugins = [
    MasterRowPlugin,
    OverlayPlugin,
    CellColumnFocusVerifyPlugin,
    ColumnStretchPlugin,
  ];

  const masterRow: RowMasterConfig = useMemo(
    () => ({
      rowHeight: 60,
      template: (h: any, data: any) => {
        const subItems = data.model.subRows?.map((subRow: Person) =>
          h(
            'span',
            { className: 'master-row' },
            `${subRow.firstName} ${subRow.lastName}, `,
          ),
        );
        return h('div', null, [h('strong', {}, 'Team: '), subItems]);
      },
    }),
    [],
  );

  const additionalData = {
    masterRow,
    stretch: 'last',
  };

  return (
    <RevoGrid
      theme={isDark() ? 'darkCompact' : 'compact'}
      source={source}
      columns={columns}
      plugins={plugins}
      additionalData={additionalData}
      hideAttribution
    />
  );
}

export default RowMaster;
Angular ts
import { Component, ViewEncapsulation, OnInit, NO_ERRORS_SCHEMA } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import {
  EXPAND_COLUMN,
  MasterRowPlugin,
  CellColumnFocusVerifyPlugin,
  ColumnStretchPlugin,
  OverlayPlugin,
} from '@revolist/revogrid-pro';
import { makeData, type Person } from '../composables/makeData';
import { currentTheme } from '../composables/useRandomData';

@Component({
  selector: 'row-master-grid',
  standalone: true,
  imports: [RevoGrid],
  template: `
    <revo-grid
      [source]="source"
      [columns]="columns"
      [plugins]="plugins"
      [theme]="theme"
      [masterRow]="additionalData.masterRow"
      [stretch]="additionalData.stretch"
      [hideAttribution]="true"
      style="min-height: 400px;"
    ></revo-grid>
  `,
  encapsulation: ViewEncapsulation.None,
  // Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
  schemas: [NO_ERRORS_SCHEMA],
})
export class RowMasterGridComponent implements OnInit {
  source = makeData(500, 20);

  columns = [
    { prop: 'expand', focus: () => false, ...EXPAND_COLUMN },
    { name: 'First Name', prop: 'firstName', size: 180 },
    { name: 'Last Name', prop: 'lastName' },
  ];

  plugins = [
    MasterRowPlugin,
    OverlayPlugin,
    CellColumnFocusVerifyPlugin,
    ColumnStretchPlugin,
  ];

  theme = currentTheme().isDark() ? 'darkCompact' : 'compact';

  additionalData = {
    masterRow: {
      rowHeight: 60,
      template: (h: any, data: any) => {
        const subItems = data.model.subRows?.map((subRow: Person) =>
          h(
            'span',
            { class: 'master-row' },
            `${subRow.firstName} ${subRow.lastName}, `,
          ),
        );
        return h('div', null, [h('strong', {}, 'Team: '), subItems]);
      },
    },
    stretch: 'last',
  };

  ngOnInit() {}
}

  • Expandable Rows: Easily expand rows to reveal additional details.
  • Custom Templates: Define how master rows are rendered using customizable templates.

  1. Import Necessary Plugins and Utilities:

    import { EXPAND_COLUMN, MasterRowPlugin, OverlayPlugin, CellColumnFocusVerifyPlugin } from '@revolist/revogrid-pro';
    import type { RowMasterConfig } from '@revolist/revogrid';
    import './row-master.style.css';
  2. Define Columns:

    const columns: HTMLRevoGridElement['columns'] = [
    {
    prop: 'expand',
    ...EXPAND_COLUMN,
    },
    // ...
    ];
  3. Initialize the Grid with Plugins:

    const grid = document.querySelector('revo-grid');
    grid.source = makeData(10, 20);
    grid.columns = columns;
    // Define plugin
    grid.plugins = [
    MasterRowPlugin, // required
    OverlayPlugin, // required
    ];
    const masterRow: RowMasterConfig = {
    template: (h, data) => {
    return data.model.subRows?.map((subRow: Person) =>
    h('span', { class: { 'master-row': true } }, [
    h('img', { src: subRow.avatar, width: '20', height: '20' }),
    `${subRow.firstName} ${subRow.lastName} `,
    ]),
    );
    },
    };
    grid.additionalData = {
    masterRow,
    rowHeight: 100,
    };
    }

The Master Row Plugin can be customized using the RowMasterConfig interface. The primary configuration option is the template function, which defines how the master row content is rendered.

PropertyTypeDescription
template(h: Function, data: any) => JSX.ElementA function that returns the JSX structure for the master row. It receives a rendering function h and the data context data.

Customize the master row by defining a template function that returns the desired JSX structure. This allows for flexible and dynamic rendering based on the row data.

const masterRow: RowMasterConfig = {
template: (h, data) => {
return data.model.subRows?.map((subRow: Person) =>
h('div', { class: { 'custom-master-row': true } }, [
h('img', { src: subRow.avatar, width: '30', height: '30' }),
`${subRow.firstName} ${subRow.lastName}`,
]),
);
},
};

A plugin class that manages master-detail rows within the RevoGrid component.

  • constructor(revogrid: HTMLRevoGridElement, providers: PluginProviders)

    Initializes the plugin with the RevoGrid instance and plugin providers.

  • expandRow(type: DimensionRows, virtRowIndex: number)

    Expands a specific row to show its master details.

  • collapseRow(type: DimensionRows, rowVIndexes: number[])

    Collapses one or more rows, hiding their master details.

  • destroy()

    Cleans up event listeners and other resources when the plugin is destroyed.

The plugin listens to various RevoGrid events to manage the state of master rows, including:

  • AFTER_GRID_RENDER_EVENT
  • BEFORE_ROW_RENDER_EVENT
  • ROW_MASTER
  • OVERLAY_NODE
  • …and more.