FreeGridv1.6.0English

이벤트

실행 예제이벤트

클릭·편집·정렬·필터를 할 때마다 그리드가 보내는 이벤트를 바로 보여 줍니다.

이 데모의 코드
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()을 부르면 그 변경이 일어나지 않습니다.