이벤트
실행 예제이벤트
클릭·편집·정렬·필터를 할 때마다 그리드가 보내는 이벤트를 바로 보여 줍니다.
이 데모의 코드
import { createGrid, type GridEvents } from '@freegridjs/core';
import { HEADERS, makeRows } from '../data';
import { button, separator, text, toggle, tr, type DemoContext } from '../ui';
export default function mount(ctx: DemoContext) {
const { lang } = ctx;
const h = (en: string, ko: string) => tr(lang, { en, ko });
const t = HEADERS[lang];
const grid = createGrid(ctx.host, {
columns: [
{ field: 'name', header: t.name, width: 150, editable: true },
{ field: 'country', header: t.country, width: 130, filter: 'set' },
{ field: 'q1', header: t.q1, type: 'number', format: '#,##0', width: 110, editable: true },
{ field: 'growth', header: t.growth, type: 'number', format: '0.0%', width: 100 },
{ field: 'status', header: t.status, width: 100, editable: true, editor: { type: 'select', options: ['Active', 'Pending', 'Closed'] } },
],
data: makeRows(80), locale: lang, getRowId: r => String(r.id), rowSelection: 'multiple', pagination: { pageSize: 20 },
});
// each event and a short line for the log; on() returns a function that stops listening
const show: { [K in keyof GridEvents]?: (e: GridEvents[K]) => string } = {
cellClick: e => `row ${e.rowIndex}, ${e.colId} = ${String(e.value)}`,
cellDoubleClick: e => `row ${e.rowIndex}, ${e.colId}`,
editStart: e => `row ${e.rowIndex}, ${e.colId}`,
cellValueChanged: e => `${e.colId}: ${String(e.oldValue)} → ${String(e.newValue)}`,
editStop: e => `committed: ${e.committed}`,
selectionChange: e => `${e.selected.length} ${h('selected', '개 선택')}`,
sortChange: e => JSON.stringify(e.sort),
filterChange: e => Object.keys(e.filters).join(', ') || h('(none)', '(없음)'),
columnResize: e => `${e.colId} → ${e.width}px`,
columnMove: e => `${e.colId} → ${e.toIndex}`,
pageChange: e => `${e.page + 1} / ${e.pageCount}`,
rangeChange: e => (e.range ? `${e.range.start.colId}${e.range.start.rowIndex}:${e.range.end.colId}${e.range.end.rowIndex}` : '-'),
};
const offs = new Map<string, () => void>();
const listen = (name: keyof GridEvents) => {
const fmt = show[name] as (e: unknown) => string;
offs.set(name, grid.on(name, ((e: unknown) => {
try {
ctx.log(`${name} → ${fmt(e)}`);
} catch {
ctx.log(name);
}
}) as never));
};
for (const name of Object.keys(show) as (keyof GridEvents)[]) listen(name);
text(ctx, h('Click, double-click to edit, sort, filter, resize, drag a column, select rows and change pages: every event appears in the log below.',
'클릭, 더블클릭 편집, 정렬, 필터, 너비 조절, 컬럼 끌기, 행 선택, 페이지 이동을 해 보세요. 모든 이벤트가 아래 기록에 나타납니다.'));
separator(ctx);
toggle(ctx, h('Log clicks', '클릭 기록'), true, on => {
if (on) listen('cellClick');
else offs.get('cellClick')?.();
});
button(ctx, h('Sort from code', '코드로 정렬'), () => grid.setSort([{ colId: 'q1', dir: 'desc' }]));
return grid;
}grid.on(이름, 함수)로 이벤트를 받고, 돌려받은 함수를 부르면 더 이상 받지 않습니다. React·Vue·Svelte·Angular 래퍼에서는 같은 이벤트를 onCellClick, @cell-click, (cellClick)처럼 씁니다. 위 데모에서 클릭·편집·정렬·필터를 해 보면 어떤 이벤트가 어떤 순서로 오는지 바로 볼 수 있습니다.
자주 쓰는 이벤트
// 행 클릭 → 상세 화면
grid.on('cellClick', ({ row, colId, value }) => openDetail(row.id));
grid.on('cellDoubleClick', ({ row }) => openEditor(row));
// 선택한 행 수를 버튼에 표시
grid.on('selectionChange', ({ selected }) => (deleteBtn.disabled = selected.length === 0));
// 정렬·필터가 바뀌면 화면 상태를 저장
const save = () => localStorage.setItem('orders-view', JSON.stringify(grid.getState()));
grid.on('sortChange', save);
grid.on('filterChange', save);
grid.on('columnResize', save);
grid.on('columnMove', save);
// 값이 바뀔 때마다 (편집, 붙여넣기, 채우기, 실행 취소)
grid.on('cellValueChanged', ({ row, colId, oldValue, newValue }) => console.log(row.id, colId, oldValue, '→', newValue));
// 그만 받기
const off = grid.on('rangeChange', ({ range }) => updateStatus(range));
off();막을 수 있는 이벤트 (before…)
beforeSort, beforeFilter, beforePageChange, beforeEdit, beforeCellChange는 변경이 일어나기 전에 오고, cancel()을 부르면 그 변경이 일어나지 않습니다. 권한 검사나 서버 정렬로 바꿀 때 씁니다.
// 승인된 주문은 고칠 수 없게
grid.on('beforeEdit', e => { if (e.row.status === '승인') e.cancel(); });
// 음수 수량 막기
grid.on('beforeCellChange', e => { if (e.colId === 'qty' && Number(e.newValue) < 0) e.cancel(); });
// 정렬을 서버에서: 그리드 정렬은 막고 서버에 다시 요청
grid.on('beforeSort', e => { e.cancel(); reload({ sort: e.sort }); });편집을 서버에 저장하기
// 셀을 고칠 때마다 저장
grid.on('cellValueChanged', async ({ row, colId, newValue }) => {
await fetch(`/api/orders/${row.id}`, { method: 'PATCH', body: JSON.stringify({ [colId]: newValue }) });
});
// 행 편집(editType: 'row')이 끝나면 한 번에 저장
grid.on('rowEditStop', ({ row, committed, changes }) => { if (committed && changes.length) saveRow(row); });
// 검증 실패 알림
grid.on('validationFailed', ({ message }) => toast(message));프레임워크에서
// React
<FreeGrid columns={columns} data={rows} onCellClick={e => open(e.row)} onSelectionChange={e => setSelected(e.selected)} />
// Vue
<FreeGrid :columns="columns" :data="rows" @cell-click="e => open(e.row)" @selection-change="e => selected = e.selected" />전체 이벤트
| 이벤트 | 전달 값 | 발생 시점 |
|---|---|---|
cellClick / cellDoubleClick | { row, rowIndex, colId, value, event } | 데이터 셀을 클릭했습니다. |
selectionChange | { selected: Row[] } | 선택된 행이 바뀌었습니다. |
sortChange | { sort } | 정렬이 바뀌었습니다. |
filterChange | { filters } | 컬럼 필터가 바뀌었습니다. |
columnResize | { colId, width } | 너비 조절이나 자동 맞춤이 끝났습니다. |
columnMove | { colId, toIndex } | 컬럼을 드래그해 위치를 옮겼습니다. |
columnVisible | { colId, visible } | 컬럼이 표시되거나 숨겨졌습니다. |
rangeChange | { range } | 셀 범위가 바뀌었습니다. |
cellValueChanged | { row, rowIndex, colId, oldValue, newValue } | 붙여넣기로 셀 값이 바뀌었습니다. |
paste | { changed, skipped } | 붙여넣기가 끝났습니다. |
editStart / editStop | { rowIndex, colId, committed? } | 편집기가 열리거나 닫혔습니다. |
validationFailed | { rowIndex, colId, value, message } | 입력값이 거부됐습니다. |
rowsAdded / rowsRemoved | { rows } | 행이 추가되거나 삭제됐습니다. |
groupChange / groupToggle | { groupBy } / { key, expanded } | 그룹 기준이 바뀌었거나 그룹·트리 행이 펼쳐지거나 접혔습니다. |
detailToggle | { row, expanded } | 상세 패널이 열리거나 닫혔습니다. |
rowDragEnd | { row, fromIndex, toIndex } | 행을 끌어 위치를 옮겼습니다. |
pageChange | { page, pageSize, pageCount } | 쪽이나 쪽당 행 수가 바뀌었습니다. |
rowsLoaded / rowsLoadFailed | { start, end, total? } / { start, end, error } | 서버 블록을 받았거나 실패했습니다. |
pivotChange | { pivot } | 피벗이 켜지거나 바뀌거나 꺼졌습니다. |
focusChange | { rowIndex, colId } | 포커스 셀이 이동했습니다. |
beforeSort / beforeFilter / beforePageChange | { sort | colId, model | page, pageSize, cancel() } | 바뀌기 전에 발생합니다. cancel()을 부르면 취소됩니다(예: 서버에서 정렬·필터할 때). |
beforeEdit / beforeCellChange | { row, rowIndex, colId, oldValue?, newValue?, cancel() } | 편집기가 열리기 전, 값이 써지기 전(편집, 붙여넣기, 채우기, 삭제)에 발생합니다. cancel()로 막습니다. |
rowDrop | { row, target, position, fromOtherGrid, fromGrid } | 이 그리드에 행이 놓였습니다(같은 드래그 그룹의 다른 그리드에서 온 경우 포함). |
scroll | { top, left } | 행 영역이 스크롤됐습니다. |
childrenLoaded / childrenLoadFailed | { row, rows } / { row, error } | treeData.loadChildren이 끝났거나 실패했습니다. |
transactionApplied | { added, updated, removed, notFound } | applyTransaction(또는 Async 묶음)이 끝났습니다. |
conditionalFormatsChange | { formats } | 조건부 서식이 바뀌었습니다(API 또는 대화 창). |
assistantAnswer | { prompt, message, applied, skipped } | 어시스턴트 창이 답을 받아 동작을 적용했습니다. |
rowEditStart / rowEditStop | { row, rowIndex } / { row, rowIndex, committed, changes } | editType 'row'에서 행 편집이 시작되거나 끝났습니다. changes는 바뀐 칸 목록. |
advancedFilterChange | { advancedFilter } | 고급 필터가 바뀌었습니다. |
columnGroupOpened | { groupId, open } | 접히는 컬럼 그룹이 펼쳐지거나 접혔습니다. |
이 페이지에 대한 질문
그리드 이벤트는 어떻게 받나요?
grid.on("cellClick", 함수)로 받고, 돌려받은 함수를 부르면 그만 받습니다. 래퍼에서는 onCellClick(React), @cell-click(Vue), (cellClick)(Angular)로 씁니다.
정렬이나 편집을 일어나기 전에 막을 수 있나요?
네. beforeSort, beforeFilter, beforePageChange, beforeEdit, beforeCellChange가 먼저 오고, cancel()을 부르면 그 변경이 일어나지 않습니다.