Skip to content

Event Manager

Learn how to handle, customize, and optimize events to create complex interactions and workflows with the Event Manager Plugin for RevoGrid. The main purpose of this plugin is to collect and dispatch edit events in one centralized location, simplifying event management.

The Event Manager Plugin aggregates various editing events into a single event, gridedit. This approach reduces the complexity of handling multiple event types individually, such as clipboardrangepaste, beforeedit, and beforerangeedit. By using the Event Manager Plugin, you can catch all these events through one unified event, making it a robust starting point for building your custom event management system.

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

import { EventManagerPlugin } from '@revolist/revogrid-pro';

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

export function load(parentSelector: string) {
  const grid = document.createElement('revo-grid');
  grid.range = true;
  grid.source = createRandomData(100);
  // Define columns
  grid.columns = [
    {
      name: '🆔 ID',
      prop: 'id',
    },
    {
      name: '🍎 Fruit',
      prop: 'name',
    },
    {
      name: 'đź’° Price',
      prop: 'price',
    },
  ];

  // Define plugin
  grid.plugins = [EventManagerPlugin];
  grid.addEventListener('gridedit', (event) => {
    const log = document.querySelector('.output .code');
    if (!log) {
      return;
    }
    log.innerHTML = JSON.stringify(event.detail);
  });

  grid.theme = isDark() ? 'darkCompact' : 'compact';
  grid.hideAttribution = true;
  document.querySelector(parentSelector)?.appendChild(grid);
}
Vuevue
// src/components/event-manager/EventManager.vue

<template>
  <div class="grid grid-cols-2 gap-1 p-0">
    <RevoGrid
      style="min-height: 400px;"
      ref="grid"
      :theme="isDark ? 'darkCompact' : 'compact'"
      :columns="columns"
      :source="rows"
      :plugins="plugins"
      stretch="all"
      :range="true"
      hide-attribution
      @gridedit="handleGridEdit"
    />
    <ExpressiveCode :code="log" language="json" style="max-height: 400px;" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { currentThemeVue, useRandomData } from '../composables/useRandomData';
import RevoGrid from '@revolist/vue3-datagrid';
import { EventManagerPlugin, ColumnStretchPlugin } from '@revolist/revogrid-pro';
import ExpressiveCode from '../sys/ExpressiveCode.vue';

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

const columns = ref([
  { name: '🍎 Fruit', prop: 'name' },
]);

const plugins = ref([EventManagerPlugin, ColumnStretchPlugin]);

const rows = ref(createRandomData(10));


const log = ref(' Output Vue');

const handleGridEdit = (event: CustomEvent) => {
  log.value = JSON.stringify(event.detail, null, 2);
};
</script>
Reacttsx
// src/components/event-manager/EventManager.tsx

import { useState, useMemo, useRef } from 'react';
import { RevoGrid, type DataType } from '@revolist/react-datagrid';
import { EventManagerPlugin } from '@revolist/revogrid-pro';

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

function EventManager() {
  const gridRef = useRef<HTMLRevoGridElement>(null);
  const [source] = useState<DataType[]>(createRandomData(100));
  const [log, setLog] = useState<string>(' Output');

  const columns = useMemo(
    () => [
      { name: '🆔 ID', prop: 'id' },
      { name: '🍎 Fruit', prop: 'name' },
      { name: 'đź’° Price', prop: 'price' },
    ],
    [],
  );

  const handleGridEdit = (event: CustomEvent<any>) => {
    setLog(JSON.stringify(event.detail));
  };

  return (
    <div>
      <RevoGrid
        ref={gridRef}
        columns={columns}
        source={source}
        rowHeaders
        range
        stretch="all"
        hide-attribution
        theme={isDark() ? 'darkCompact' : 'compact'}
        plugins={[EventManagerPlugin]}
        onGridedit={handleGridEdit}
      />
      <br />
      <div className="expressive-code">
        <pre className="code pa-5">
          <code>{log}</code>
        </pre>
      </div>
    </div>
  );
}

export default EventManager;
Angularts
// src/components/event-manager/EventManagerAngular.ts
import { Component, ViewEncapsulation, type OnInit } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { EventManagerPlugin } from '@revolist/revogrid-pro';
import { currentTheme, useRandomData } from '../composables/useRandomData';

@Component({
  selector: 'event-manager-grid',
  standalone: true,
  imports: [RevoGrid],
  template: `
    <revo-grid
      #gridRef
      [range]="true"
      [columns]="columns"
      [source]="source"
      [hideAttribution]="true"
      [theme]="theme"
      [plugins]="plugins"
      style="min-height: 400px; "
      (gridedit)="handleGridEdit($event)"
    ></revo-grid>
    <br />
    <div class="expressive-code">
      <pre class="code">
        <code>{{ log }}</code>
      </pre>
    </div>`,
  encapsulation: ViewEncapsulation.None,
})
export class EventManagerGridComponent implements OnInit {
  theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
  source = useRandomData().createRandomData(100);
  log = ' Output';

  columns = [
    { name: '🆔 ID', prop: 'id' },
    { name: '🍎 Fruit', prop: 'name' },
    { name: 'đź’° Price', prop: 'price' },
  ];

  plugins = [EventManagerPlugin];

  ngOnInit() {
    // Additional initialization if needed
  }

  handleGridEdit(event: any) {
    this.log = JSON.stringify(event.detail);
  }
}
  • Centralized Event Handling: Collects multiple edit events into a single gridedit event.
  • Simplified Event Management: Reduces the need to manage numerous event types individually.
  • Customizable Workflows: Provides a foundation to create complex interactions and workflows.

data, previousData, and models always use RevoGrid’s normal numeric-row and field-patch format. Feature plugins can add semantic information through the ordered domainChanges array without changing that grid-data contract. Replay-only feature state is kept separately in keyed history snapshots, so internal undo data never appears as a synthetic grid field.

For example, a Kanban move can expose a normal status patch in data, identify the operation as a card move in domainChanges, and keep its compact undo state in history.

grid.addEventListener('gridedit', (event) => {
const { data, previousData, domainChanges = [], eventTypes } = event.detail;
// Common RevoGrid patches, regardless of which feature produced the edit.
saveGridChanges({ data, previousData });
// Optional feature semantics. One transaction may contain several entries.
for (const change of domainChanges) {
if (change.type === 'kanban-card') {
const { action, cardId, previousCard, card } = change.detail;
saveCardChange({ action, cardId, previousCard, card });
}
}
if (eventTypes.includes('kanban-card-change')) {
refreshKanbanSummary();
}
});

The currently exported domain types and tags are:

FeatureDomain typeeventTypes tag
Gantt taskgantt-taskgantt-task-change
Gantt dependencygantt-dependencygantt-dependency-change
Gantt assignmentgantt-assignmentgantt-assignment-change
Kanban cardkanban-cardkanban-card-change
Scheduler eventevent-scheduler-eventevent-scheduler-event-change

Domain changes are ordered. Bulk operations and scheduling cascades include all affected entities, not only the item directly manipulated. Structural changes can have no safe numeric row patch, so data may be empty while domainChanges still fully describes the committed operation.

Use feature-specific types when you only consume one producer:

import type { KanbanEventManagerEvent } from '@revolist/kanban';
interface MyCard {
id: string;
status: string;
}
grid.addEventListener('gridedit', (event) => {
const detail = event.detail as KanbanEventManagerEvent<MyCard>;
detail.domainChanges?.forEach(({ detail: cardChange }) => {
console.log(cardChange.action, cardChange.cardId);
});
});

For ordinary grid edits, EventManager owns applying data; canceling gridedit prevents that write. A feature event with sourceMutation: 'producer' was already committed by Gantt, Kanban, or Scheduler. Its gridedit is therefore observational and canceling it does not roll the mutation back. Use the producer’s cancelable before event for validation instead.

When collection is enabled, consecutive compatible changes become one ordered gridedit transaction and one undo step:

grid.eventManager = {
collectEvents: true,
collectEventsDelay: 100,
};

The transaction preserves the earliest previous state and latest committed state for each entity. Do not rely on exact equality of eventTypes; use eventTypes.includes(...) because a transaction may contain several tags.

Plugins that submit producer-owned history must also handle beforehistoryedit. After successfully restoring their state, they call markEventManagerHistoryReplayHandled(detail, ownedHistoryKeys) and cancel that replay event. This keeps malformed or unavailable producer history on its current undo/redo stack instead of silently consuming it. Applications normally do not need to read history; it is replay state owned by HistoryPlugin and the producer.

Here’s a brief overview of how to implement the Event Manager Plugin in your RevoGrid setup.

  1. Import Required Modules: Import the necessary modules from the RevoGrid library to start setting up the Event Manager Plugin.

  2. Define Edit Details Type: Create a type for edit details that will help structure the event data.

  3. Create EventManagerPlugin Class: Develop the EventManagerPlugin class to handle and dispatch edit events. This class listens for various edit events and dispatches a unified gridedit event.

    • Collect Edit Events: Listens for clipboardrangepaste, beforeedit, and beforerangeedit events.
    • Dispatch Unified Event: Dispatches the gridedit event after collecting details from the various edit events.
  4. Add Plugin to RevoGrid: Attach the Event Manager Plugin to your RevoGrid instance and start listening for the gridedit event.

    ...
    grid.plugins = [EventManagerPlugin];
    ...

For detailed implementation, please refer to the full code below.

The Event Manager Plugin is a powerful tool for managing edit events. By consolidating multiple event types into a single, manageable event, it simplifies event handling and provides a solid foundation for developing custom workflows and interactions.