Skip to content

Save Card Changes

Use your application’s API to save moved or edited cards. Loading and saving are separate: kanban.remote fetches pages; application listeners submit changes.

Interactive example · supplied HTTP demo backend

Save a move in your application

A committed move event sends every changed card to the supplied HTTP backend.

Try it: Move the first card, then refresh to verify it. Try Fail a move and refresh to recover.

Starting demo backend…

HTTP reads: 0 · writes: 0

What to notice: An unconfirmed save pauses further moves until a refresh reconciles the board.

TypeScript source

The supplied backend handlers use MSW simulation in the browser. Replace the transport with your application endpoint.

remote-board.ts
import type { KanbanConfig, KanbanCardMoveDetail } from '@revolist/kanban';
import type { WorkCard } from './first-board';
import { boardConfig, configureBoard } from './first-board';
// apiUrl is YOUR endpoint. The documentation supplies an isolated HTTP demo backend.
export function remoteBoard(apiUrl: string): KanbanConfig<WorkCard> {
return { ...boardConfig, remote: {
chunkSize: 6,
query: { retry: false, refetchOnWindowFocus: false },
async loadData(skip, limit, _order, _single, _multi, _quick, _temporal, _ast, _execution, context) {
const response = await fetch(`${apiUrl}/cards?skip=${skip}&limit=${limit}`, { signal: context?.signal });
if (!response.ok) throw new Error(`Card loading failed: HTTP ${response.status}`);
return response.json(); // { data: Card[], total: number }
},
} };
}
// Read-only setup; add app-owned write handling before enabling moves.
export async function createServerBoard(container: HTMLElement, apiUrl: string) {
const grid = document.createElement('revo-grid');
grid.style.cssText = 'display:block;width:100%;height:360px';
container.append(grid);
try {
// configureBoard waits for grid initialization before activating the remote loader.
const kanban = await configureBoard(grid, { ...remoteBoard(apiUrl), readOnly: true }, []);
return { grid, kanban, destroy() { grid.remove(); } };
} catch (error) { grid.remove(); throw error; }
}
export async function saveMove(apiUrl: string, detail: KanbanCardMoveDetail, signal?: AbortSignal) {
const response = await fetch(`${apiUrl}/moves`, {
method: 'POST', signal, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ changedCards: detail.changedCards }),
});
if (!response.ok) throw new Error(`Move save could not be confirmed: HTTP ${response.status}`);
return response.json();
}
// grid.kanban = remoteBoard(apiUrl);
// grid.addEventListener('kanbancardmove', event => { void saveMove(apiUrl, event.detail); });
// In your app, pause conflicting moves while saving; on failure refresh before more edits.
// Pagination, refresh and Query policy belong to remote. There is no remote.commit method.
Download remote-board.ts

Try it: choose Move first card, wait for the saved message, then Refresh from server. The simulated backend should return its new position. Choose Fail a move to see an HTTP save failure; refresh before continuing. This move-only example disables editing and blocks further moves while a save is pending.

// For an event-owned integration, leave kanban.persistence unset.
grid.addEventListener('kanbancardmove', async ({ detail }) => {
const response = await fetch('/api/cards/moves', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ changedCards: detail.changedCards }),
});
if (!response.ok) showSaveError(); // App-owned recovery and reconciliation.
});

A move may also rebalance neighboring ranks. Save the complete changedCards batch atomically, rather than only the dragged cards. Create/update/delete have their own committed events. The generic gridedit stream can consolidate these changes. Final events are observational: cancelling them does not roll back a move. The app owns write ordering, pending/error UI, concurrency checks and undo/redo persistence in this flow.

Do not add an event save listener when using persistence.commit, or the same edit would be saved twice. The optional managed persistence adapter requires a complete canonical dataset and cannot be combined with paged remote loading.

  • Disable or gate conflicting edits while a save is pending, and preserve write order in your application.
  • If the backend rejects a change definitively, show its reason and reconcile the board with authoritative records.
  • If a response is lost, the write may already have succeeded. Refresh authoritative state before allowing another conflicting edit, or use your own idempotent operation protocol.
  • A final event cannot be cancelled to roll back an already committed local move. Synchronous rejection belongs in beforekanbancardmove or rules.

Observe kanbancardcreate, kanbancardupdate, kanbancarddelete and move events, or the unified gridedit card domain changes. Save the entire committed batch. Undo/redo changes also need backend persistence when the app owns saving.

Do not install a second event save listener alongside managed persistence.commit; that would submit the same change twice. For complete datasets needing coordinated versioned saving, see Managed Saving and Conflicts.

Continue with error recovery, events and history. See the Kanban API for exact types.