유효성 검사와 변경 추적
required, unique, validate로 잘못된 값을 막고, 저장 전 validate()로 전체를 검사하며, getModifiedRows, 실행 취소, commitChanges로 변경을 관리합니다.
실행 예제유효성 검사와 편집 규칙
셀 잠금, 중복·빈 값 검사, 시간 편집기, 고를 수 없는 옵션, 행 단위 편집, 변경 취소 이벤트, 전체 검사.
이 데모의 코드
import { createGrid, type ColumnDef, type Row } from '@freegridjs/core';
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 data: Row[] = Array.from({ length: 30 }, (_, i) => ({
sku: `SKU-${String(100 + i)}`,
name: [h('Pen', '펜'), h('Notebook', '노트'), h('Stapler', '스테이플러'), h('Marker', '마커'), h('Tape', '테이프')][i % 5],
qty: 1 + ((i * 7) % 12),
price: 2 + ((i * 13) % 40),
pickup: ['09:30', '13:00', '16:45'][i % 3],
status: ['open', 'hold', 'closed'][i % 3],
locked: i % 4 === 3,
}));
// a duplicate to find with validate()
data[5].sku = data[2].sku;
const columns: ColumnDef[] = [
{ field: 'sku', header: 'SKU', width: 110, editable: true, unique: true, required: true },
{ field: 'name', header: h('Item', '품목'), width: 120, editable: true },
// locked rows cannot be edited
{ field: 'qty', header: h('Qty', '수량'), type: 'number', width: 90, editable: ({ row }) => !row.locked,
cellClass: ({ row }) => (row.locked ? 'demo-locked' : undefined) },
{ field: 'price', header: h('Price', '단가'), type: 'number', width: 90, editable: ({ row }) => !row.locked },
// worked out from the row; not editable
{ id: 'total', header: h('Total', '금액'), type: 'number', format: '#,##0', width: 120, valueGetter: r => r.qty * r.price },
{ field: 'pickup', header: h('Pickup', '수령 시각'), width: 110, editable: true, editor: { type: 'time' } },
{ field: 'status', header: h('Status', '상태'), width: 120, editable: true,
editor: { type: 'select', options: [
{ value: 'open', label: h('Open', '진행') },
{ value: 'hold', label: h('On hold', '보류') },
{ value: 'closed', label: h('Closed (admin only)', '종료 (관리자 전용)'), disabled: true },
] } },
];
// editType 'row': Tab stays in the row, Enter or another row saves it, Escape puts the row back
const grid = createGrid(ctx.host, { columns, data, locale: lang, editType: 'row' });
let blockNegative = true;
grid.on('beforeCellChange', e => {
if (blockNegative && typeof e.newValue === 'number' && e.newValue < 0) {
e.cancel();
ctx.log(h(`beforeCellChange → refused ${e.newValue} in ${e.colId}`, `beforeCellChange → ${e.colId}의 ${e.newValue} 거부`));
}
});
grid.on('cellValueChanged', e => ctx.log(`cellValueChanged → ${e.colId}: ${String(e.oldValue)} → ${String(e.newValue)}`));
grid.on('validationFailed', e => ctx.log(`validationFailed → ${e.colId}: ${e.message}`));
text(ctx, h('Rows are edited as a whole: Tab stays in the row, Enter saves it, Escape puts it back. Gray rows are locked. SKU must be unique. Closed is a disabled option.',
'행 단위로 편집합니다. Tab은 같은 행 안에서 이동하고, Enter는 행 저장, Esc는 행 전체 되돌리기입니다. 회색 행은 수정할 수 없고, SKU는 중복될 수 없습니다. "종료"는 고를 수 없는 옵션입니다.'));
separator(ctx);
button(ctx, h('Check all rows (validate)', '전체 검사 (validate)'), () => {
const bad = grid.validate();
ctx.log(bad.length ? h(`${bad.length} problem(s):`, `문제 ${bad.length}건:`) : h('all rows are valid', '모든 행이 올바릅니다'));
for (const b of bad) ctx.log(` ${h('row', '행')} ${b.rowIndex + 1} · ${b.colId}: ${b.message}`);
});
toggle(ctx, h('Refuse negative numbers', '음수 입력 막기'), true, on => { blockNegative = on; });
grid.on('rowEditStop', e => ctx.log(`rowEditStop → ${e.committed ? h('saved', '저장') : h('put back', '되돌림')} ${e.changes.map(c => c.colId).join(', ')}`));
return grid;
}컬럼에 규칙을 주면 잘못된 값이 그리드에 들어오지 않습니다. required는 빈 값을, unique는 다른 행에 이미 있는 값을 거부하고(빈 값은 허용), validate(value, row)는 메시지를 돌려주어 음수 수량이나 @ 없는 이메일 주소처럼 그 밖의 값을 거부합니다. 편집하다 거부되면 값을 고치거나 Esc로 취소할 때까지 편집기에 메시지가 남아 있고, validationFailed 이벤트가 옵니다. 규칙에 맞지 않는 값을 붙여넣으면 그 셀은 건너뛰고 paste 이벤트의 skipped에 셉니다.
불러오거나 가져오거나 코드로 바꾼 데이터도 규칙에 어긋날 수 있습니다. 저장하기 전에 validate()를 부르세요. 편집 가능한 모든 셀을 검사해 규칙에 맞지 않는 셀을 행, 컬럼, 메시지와 함께 돌려주므로, 첫 문제로 스크롤하거나 목록으로 보여 주면 됩니다.
const grid = createGrid(el, {
columns: [
{ field: 'code', editable: true, unique: true, required: true },
{ field: 'name', editable: true, required: true },
{ field: 'qty', type: 'number', editable: true, validate: v => v >= 0 || 'Must be 0 or more' },
{ field: 'email', editable: true, validate: (v, row) => !v || v.includes('@') || 'Not an e-mail address' },
],
data,
});
grid.on('validationFailed', e => toast(e.message));
// before saving: every cell that breaks a rule
const problems = grid.validate(); // [{ row, rowIndex, colId, message }]
if (problems.length) grid.scrollToRow(problems[0].rowIndex);저장할 행처럼 그리드 일부만 검사하려면 다음을 보세요: 일부 행만 검사하기.
변경 추적과 실행 취소
편집, 붙여넣기, 행 추가와 삭제는 모두 기록됩니다. 바뀐 셀에는 모서리에 작은 표시가 생기고, Ctrl+Z와 Ctrl+Y로 실행 취소와 다시 실행을 할 수 있으며, 서버에 무엇을 보낼지도 그리드가 알고 있습니다. getModifiedRows()는 추가·수정·삭제된 행을, getChanges()는 바뀐 셀마다 이전 값과 새 값을 돌려줍니다. 서버에 저장한 뒤 commitChanges()를 부르면 지금 행이 새 기준이 되고 표시가 지워집니다. 반대로 revertChanges()는 모든 변경을 되돌립니다.
grid.addRows([{ name: 'New' }], 0); // insert at the top
grid.removeSelectedRows();
grid.undo(); grid.redo(); // Ctrl+Z / Ctrl+Y
grid.hasChanges(); // true
// send only what changed
const { created, updated, deleted } = grid.getModifiedRows();
await save({ created, updated, deleted, cells: grid.getChanges() });
grid.commitChanges(); // the saved state is the new baseline
// grid.revertChanges(); // or put everything backapplyTransaction으로 들어온 변경(실시간 데이터)은 편집이 아니므로 기록되지 않고 실행 취소할 수도 없습니다. "조회 → 편집 → 저장" 화면이라면 saveChanges()가 변경을 보내고 서버가 받아들이면 확정까지 해 줍니다. 서버 예제를 보세요.
옵션, 메서드, 이벤트
| 이름 | 타입 | 설명 |
|---|---|---|
required / unique | boolean | 컬럼: 빈 값을 거부합니다 / 다른 행에 이미 있는 값을 거부합니다(빈 값은 허용). |
validate(value, row) | true | string | 컬럼: 메시지를 돌려주면 값을 거부합니다. 편집기가 열린 채 메시지를 보여 줍니다. |
grid.validateCell(row, colId) | string | null | 셀 하나가 규칙에 어긋나는 이유, 맞으면 null. |
grid.validate({ rows?, colIds?, changedOnly? }) | { row, rowIndex, colId, message }[] | 편집 가능한 모든 셀을 규칙으로 검사합니다. 모두 맞으면 빈 배열. |
validationFailed | { rowIndex, colId, value, message } (event) | 입력한 값이 거부됐습니다(규칙에 맞지 않는 붙여넣기 값은 paste 이벤트에 셉니다). |
getModifiedRows() / getChanges() / hasChanges() | object | 추가·수정·삭제된 행, 바뀐 셀마다 이전 값과 새 값, 바뀐 것이 있는지 여부. |
commitChanges() / revertChanges() | void | 지금 행을 새 기준으로 삼거나(저장 뒤) 모든 변경을 되돌립니다. |
addRows(rows, index?) / removeRows(rows | fn) / removeSelectedRows() | Row[] | 행을 추가하거나 삭제합니다. 둘 다 변경 내역에 남고 실행 취소할 수 있습니다. |
undo() / redo() / canUndo() / canRedo() | void / boolean | 편집·붙여넣기·행 변경의 실행 취소 기록(Ctrl+Z / Ctrl+Y). |
이 페이지에 대한 질문
저장하기 전에 값을 검사하려면?
컬럼에 required, unique, validate를 주세요. 규칙에 맞지 않는 입력은 거부되고, 저장 전에 grid.validate()를 부르면 잘못된 셀 목록을 돌려줍니다.