Skip to content

Row Transpose

The Row Transpose Plugin modifies the grid by transposing its data, meaning that the original rows are converted into columns and vice versa. This can be especially useful when dealing with datasets that require pivoting or when users need to explore data from different perspectives.

Source code
TypeScript ts
// src/components/row-transpose/rowTranspose.ts

import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();

import { useRandomData, currentTheme } from '../composables/useRandomData';
const { createRandomData } = useRandomData();
const { isDark } = currentTheme();

import { RowTransposePlugin, TransposedRow } from '@revolist/revogrid-pro';
import { ColumnStretchPlugin } from '@revolist/revogrid-pro';

export function load(parentSelector: string) {
  const parent = document.querySelector(parentSelector);
  if (!parent) return;

  const grid = document.createElement('revo-grid');
  grid.source = createRandomData(10);

  // Define columns
  grid.columns = [
    {
      name: '🆔 ID',
      prop: 'id',
      size: 80,
    },
    {
      name: '🍎 Fruit',
      prop: 'name',
    },
    {
      name: '💰 Price',
      prop: 'price',
    },
  ];

  // Define plugin
  grid.plugins = [RowTransposePlugin, ColumnStretchPlugin];
  grid.theme = isDark() ? 'darkCompact' : 'compact';

  // Define additional data
  Object.assign(grid, {
    transpose: {
      transposedRowHeader: { columnTemplate: () => '' },
      transposedColumnHeader: { columnTemplate: () => '' },
    },
    stretch: 'all',
  })

  grid.addEventListener('beforeedit', async (e) => {
    const model = e.detail.model as TransposedRow;
    // Example how to transform TransposedRow to original model
    console.log(
      'beforeedit',
      e.detail,
      model.getOriginalModels([e.detail.prop]),
    );
  });

  grid.hideAttribution = true;

  const container = document.createElement('div');
  container.style.display = 'flex';
  container.style.flexDirection = 'column';
  container.style.gap = '0.5rem';

  const buttonContainer = document.createElement('div');
  const button = document.createElement('button');
  button.innerText = 'Transpose Rows';
  button.className = 'rv-btn';
  button.onclick = async () => {
    const plugins = await grid.getPlugins();
    const plugin = plugins.find((p) => p instanceof RowTransposePlugin) as RowTransposePlugin;
    plugin?.transpose();
  };
  buttonContainer.appendChild(button);
  container.appendChild(buttonContainer);
  container.appendChild(grid);

  parent.appendChild(container);
}
Vue vue
<template>
  <div class="flex flex-col gap-2">
    <div>
      <button @click="triggerTranspose" class="rv-btn" id="transpose">Transpose Rows</button>
    </div>
    <RevoGrid
      class="rounded-lg overflow-hidden"
      ref="grid"
      :columns="columns"
      :source="source"
      :plugins="plugins"
      :theme="isDark ? 'darkCompact' : 'compact'"
      :additionalData="{
        transpose: {
          transposedRowHeader: { columnTemplate: () => '' },
          transposedColumnHeader: { columnTemplate: () => '' },
          hideColumnHeader: true,
        },
        stretch: 'all',
      }"
      :col-size="150"
      hide-attribution
      @beforeedit="handleBeforeEdit"
    />
    </div>
</template>

<script setup lang="ts">
import RevoGrid, { type ColumnRegular } from '@revolist/vue3-datagrid';
import { RowTransposePlugin, ColumnStretchPlugin, type TransposedRow, avatarRenderer, ratingStarRenderer } from '@revolist/revogrid-pro';
import { ref } from 'vue';
import { currentThemeVue } from '../composables/useRandomData';
import { makeData } from '../composables/makeData';

const { isDark } = currentThemeVue();

const grid = ref<{ $el: HTMLRevoGridElement } | null>(null);

const source = ref(makeData(10));

const columns: ColumnRegular[] = [
  { name: 'Avatar', prop: 'avatar', cellTemplate: avatarRenderer, rectangular: true },
  { name: 'Full name', prop: 'fullName' },
  { name: 'Rating', prop: 'rating', cellTemplate: ratingStarRenderer },
  { name: 'Email', prop: 'email' },
  { name: 'Age', prop: 'age' },
  { name: 'Address', prop: 'address' },
  { name: 'City', prop: 'city' },
  { name: 'State', prop: 'state' },
  { name: 'Zip code', prop: 'zipCode' },
];

const plugins = [RowTransposePlugin];

function handleBeforeEdit(e: CustomEvent) {
  const model = e.detail.model as TransposedRow;
  console.log('beforeedit', e.detail, model.getOriginalModels([e.detail.prop]));
}

function triggerTranspose() {
  grid.value?.$el.getPlugins().then((plugins) => {
    const plugin = plugins.find((p) => p instanceof RowTransposePlugin) as RowTransposePlugin;
    plugin?.transpose();
    if (!grid.value?.$el) {
      return;
    }
    if (plugin.isTransposed) {
      grid.value.$el.rowDefinitions = [{
        type: 'rgRow',
        size: 150,
        index: 0,
      }];
      grid.value.$el.colSize = 250;
    } else {
      grid.value.$el.rowDefinitions = [];
      grid.value.$el.colSize = 100;
    }
  });
}
</script>
React tsx
import React, { useMemo, useCallback, useRef } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import { RowTransposePlugin, ColumnStretchPlugin, type TransposedRow } from '@revolist/revogrid-pro';
import { useRandomData, currentTheme } from '../composables/useRandomData';

const { createRandomData } = useRandomData();
const { isDark } = currentTheme();

function RowTranspose() {
  const gridRef = useRef<HTMLRevoGridElement>(null);
  const source = createRandomData(10);

  const columns = useMemo(
    () => [
      { name: '🆔 ID', prop: 'id', size: 80 },
      { name: '🍎 Fruit', prop: 'name' },
      { name: '💰 Price', prop: 'price' },
    ],
    []
  );

  const plugins = useMemo(() => [RowTransposePlugin, ColumnStretchPlugin], []);

  const additionalData = useMemo(
    () => ({
      transpose: {
        transposedRowHeader: { columnTemplate: () => '' },
        transposedColumnHeader: { columnTemplate: () => '' },
      },
      stretch: 'all',
    }),
    []
  );

  const handleBeforeEdit = useCallback((e: CustomEvent) => {
    const model = e.detail.model as TransposedRow;
    console.log('beforeedit', e.detail, model.getOriginalModels([e.detail.prop]));
  }, []);

  const triggerTranspose = () => {
    gridRef?.current?.getPlugins().then((plugins) => {
      const plugin = plugins.find((p) => p instanceof RowTransposePlugin);
      (plugin as RowTransposePlugin)?.transpose();
    });
  };

  return (
    <div className="flex flex-col gap-2">
      <div>
        <button
          onClick={triggerTranspose}
          className="rv-btn"
        >
          Transpose Rows
        </button>
      </div>
      <RevoGrid
        ref={gridRef}
        source={source}
        columns={columns}
        plugins={plugins}
        additionalData={additionalData}
        theme={isDark() ? 'darkCompact' : 'compact'}
        hideAttribution
        onBeforeedit={handleBeforeEdit}
      />
    </div>
  );
}

export default RowTranspose;
Angular ts
import { Component, ViewEncapsulation, ViewChild, NO_ERRORS_SCHEMA } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { RowTransposePlugin, ColumnStretchPlugin, TransposedRow } from '@revolist/revogrid-pro';
import { useRandomData, currentTheme } from '../composables/useRandomData';

@Component({
  selector: 'row-transpose-grid',
  standalone: true,
  imports: [RevoGrid],
  template: `
    <div style="display: flex; flex-direction: column; gap: 0.5rem;">
      <div>
        <button
          (click)="triggerTranspose()"
          class="rv-btn"
        >
          Transpose Rows
        </button>
      </div>
      <revo-grid
        #gridRef
        [source]="source"
        [columns]="columns"
        [plugins]="plugins"
        [theme]="theme"
        [rowTranspose]="rowTranspose"
        [stretch]="stretch"
        [hideAttribution]="true"
        (beforeedit)="handleBeforeEdit($event)"
        style="min-height: 400px;"
      ></revo-grid>
    </div>
  `,
  encapsulation: ViewEncapsulation.None,
  // Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
  schemas: [NO_ERRORS_SCHEMA],
})
export class RowTransposeGridComponent {
  @ViewChild('gridRef', { static: true }) gridRef!: RevoGrid;
  source = useRandomData().createRandomData(10);

  columns = [
    { name: '🆔 ID', prop: 'id', size: 80 },
    { name: '🍎 Fruit', prop: 'name' },
    { name: '💰 Price', prop: 'price' },
  ];

  plugins = [RowTransposePlugin, ColumnStretchPlugin];

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

  rowTranspose = {
    transposedRowHeader: { columnTemplate: () => '' },
    transposedColumnHeader: { columnTemplate: () => '' },
  };

  stretch = 'all';

  handleBeforeEdit(event: any) {
    const model = event.detail.model as TransposedRow;
    console.log('beforeedit', event.detail, model.getOriginalModels([event.detail.prop]));
  }

  triggerTranspose() {
    this.gridRef?.getPlugins().then((plugins) => {
      const plugin = plugins.find((p) => p instanceof RowTransposePlugin);
      (plugin as RowTransposePlugin)?.transpose();
    });
  }
}

Key Features

  • Toggle Transpose: Switch between the original view and the transposed view.
  • Dynamic Headers: Customizable transposed headers for better data representation.
  • Custom Transpose Configuration: Define how the columns and rows should be transformed, including whether to transpose column headers into rows.

The plugin offers two primary functionalities:

  • Original View: Displays rows and columns as defined in your data model.
  • Transposed View: When activated, this feature converts rows into columns and columns into rows.

The RowTransposePlugin listens for the row-transpose. When triggered, it switches between the original view and the transposed view while preserving the original structure, allowing you to revert to the standard view at any time.

Alternatively, you can access the plugin directly using the following JavaScript code:

document.querySelector('revo-grid').getPlugins().then(plugins => {
const plugin = plugins.find(p => p instanceof RowTransposePlugin);
plugin?.transpose();
});

When the rows are transposed, RevoGrid enters a virtual transposed mode. This means that events related to row and column edits will return instances of the TransposedRow class, which represent the transposed data model. If you wish to retrieve the original model, you can use the TransposedRow.getOriginalModels method. This method takes transposed column properties as input and converts the corresponding transposed cell back to the original model.

For example:

document.querySelector('revo-grid').addEventListener('beforeedit', async (e) => {
const model = e.detail.model;
console.log('beforeedit', model.getOriginalModels([e.detail.prop])); // returns original row model
});

The plugin configuration allows you to fine-tune the transposing process. For example, you can choose to transpose the column headers into rows, or leave them unchanged.

document.querySelector('revo-grid').additionalData = {
transpose: {
transposedRowHeader: { cellTemplate: () => '', columnTemplate: () => '' }, // defined row/header template
transposedColumnHeader: { columnTemplate: () => '' }, // template for transposed columns
}
};

The Row Transpose Plugin is a powerful tool for displaying data in different formats within RevoGrid. Whether you’re working with complex datasets that require pivoting or simply need to view your data from a different perspective, this plugin provides the flexibility to switch between views seamlessly. With customizable options for how rows and columns are transposed, you can easily tailor the grid to your needs.