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
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);
}
// 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>
// 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;
// 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);
}
}
Key Features
Section titled “Key Features”- Centralized Event Handling: Collects multiple edit events into a single
grideditevent. - Simplified Event Management: Reduces the need to manage numerous event types individually.
- Customizable Workflows: Provides a foundation to create complex interactions and workflows.
Unified payload
Section titled “Unified payload”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:
| Feature | Domain type | eventTypes tag |
|---|---|---|
| Gantt task | gantt-task | gantt-task-change |
| Gantt dependency | gantt-dependency | gantt-dependency-change |
| Gantt assignment | gantt-assignment | gantt-assignment-change |
| Kanban card | kanban-card | kanban-card-change |
| Scheduler event | event-scheduler-event | event-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); });});Cancellation and source ownership
Section titled “Cancellation and source ownership”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.
Implementation
Section titled “Implementation”Here’s a brief overview of how to implement the Event Manager Plugin in your RevoGrid setup.
Step-by-Step Guide
Section titled “Step-by-Step Guide”-
Import Required Modules: Import the necessary modules from the RevoGrid library to start setting up the Event Manager Plugin.
-
Define Edit Details Type: Create a type for edit details that will help structure the event data.
-
Create EventManagerPlugin Class: Develop the
EventManagerPluginclass to handle and dispatch edit events. This class listens for various edit events and dispatches a unifiedgrideditevent.- Collect Edit Events: Listens for
clipboardrangepaste,beforeedit, andbeforerangeeditevents. - Dispatch Unified Event: Dispatches the
grideditevent after collecting details from the various edit events.
- Collect Edit Events: Listens for
-
Add Plugin to RevoGrid: Attach the Event Manager Plugin to your RevoGrid instance and start listening for the
grideditevent....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.