FreeReportv0.9.0한국어

Events

Viewer and designer events with examples: onReady, onParamsSubmit, onPageChange, onError, onOutput, fetchData, onChange and onSave.

Events let your page react to what the viewer and the designer do. They are plain functions in the options, and none are required. In the order they happen when a user runs a report:

EventWhereWhen
onParamsSubmit(params)viewerThe user pressed Run. Return false to stay, or changed parameters. Not called for setParams().
fetchData(params)viewerLoad rows: at start, on Run and on setParams(). Return { rows } (or a promise).
onReady({ pages, rows, ms, params })viewerThe report is laid out and shown (after every run).
onPageChange(page, total)viewerThe page in view changed.
onOutput(e)viewer · createReportBefore every print and file (print, pdf, xlsx, xlsx-data, csv, hwpx). Return false to cancel.
onError(error, during)viewerSomething failed: 'data', 'layout' or 'output'.
onChange(definition)designerEvery change to the form, including undo and redo.
onSave(definition)designerThe user pressed Save or Ctrl+S.

Viewer events

Validate the parameters before loading, show counts after each run, follow the page, handle errors in one place and log every output:

const viewer = createViewer(el, {
  definition,
  params: { from: '2026-10-01', to: '2026-10-31', dept: '' },

  // 1. the user pressed 조회/Run: check or adjust the parameters first
  onParamsSubmit: params => {
    if (String(params.from) > String(params.to)) {
      alert('시작일이 종료일보다 늦습니다.');
      return false;                                   // stay on the panel, nothing is loaded
    }
    return { ...params, dept: params.dept || myDept };  // or return changed parameters
  },

  // 2. load the rows for those parameters (start, 조회, setParams)
  fetchData: params => fetch('/api/sales?' + new URLSearchParams(params as Record<string, string>)).then(r => r.json()),

  // 3. after every successful run
  onReady: ({ pages, rows, ms, params }) => {
    summary.textContent = pages + '쪽 · ' + rows.toLocaleString() + '건 (' + ms + 'ms)';
    printBtn.disabled = rows === 0;
  },

  // 4. the page in view changed (scroll, buttons, keyboard, goTo)
  onPageChange: (page, total) => { pager.textContent = page + ' / ' + total; },

  // 5. loading, layout, printing or saving failed (the viewer also shows a message)
  onError: (error, during) => {
    if (during === 'data' && /401/.test(error.message)) location.href = '/login';
    else reportError(error);                           // your error tracker
  },

  // 6. before every print and file: log it, ask for a reason, or return false to block it
  onOutput: async e => {
    const reason = e.kind === 'print' ? prompt('출력 사유를 입력하세요') : '';
    if (reason === null) return false;
    await fetch('/api/report-logs', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ ...e, reason }) });
  },
});
  • onParamsSubmit runs only when the user presses Run; viewer.setParams() from your code goes straight to fetchData.
  • onReady runs after the first load and after every Run, setData, setParams and setDefinition, so it is the place to update counts and buttons around the viewer.
  • If an event handler throws, the error goes to the console and the viewer keeps working.

Designer events

Keep a draft while the user edits, save to your server, and warn before leaving with unsaved changes:

let dirty = false;
const designer = createDesigner(el, {
  definition, data: { rows: sample },
  // every change (move, resize, property, undo/redo): keep a draft, mark the page as changed
  onChange: def => {
    dirty = true;
    localStorage.setItem('draft:sales', JSON.stringify(def));
  },
  // Ctrl+S or the Save button
  onSave: async def => {
    const res = await fetch('/api/reports/sales', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify(def) });
    if (res.ok) { dirty = false; localStorage.removeItem('draft:sales'); }
  },
});
addEventListener('beforeunload', e => { if (dirty) e.preventDefault(); });   // unsaved changes warning

In React, Vue, Svelte and Angular

// React
<FreeReportViewer definition={def} fetchData={load} onReady={e => setCount(e.rows)} onPageChange={setPage} onError={showError} />

<!-- Vue -->
<FreeReportViewer :definition="def" :fetch-data="load" :on-ready="e => (count = e.rows)" :on-page-change="p => (page = p)" />

<!-- Svelte 5: onReady gives the viewer itself; the core onReady is onRendered -->
<FreeReportViewer {definition} fetchData={load} onRendered={e => (count = e.rows)} onPageChange={p => (page = p)} />

<!-- Angular: outputs -->
<free-report-viewer [definition]="def" [fetchData]="load" (rendered)="count = $event.rows" (pageChange)="page = $event.page" (error)="show($event.error)" />

The output event (onOutput) has its own page with an output log example. Output log hook