Embedding forms

<qf-form> is a standard web component. It works in any page and any framework, with no build step:

<link rel="stylesheet" href="quillflow.css" />
<script type="module" src="quillflow.bundle.js"></script>

<qf-form form="loan-application" api="/api"></qf-form>
Attribute / property Meaning
form The form id to load from the API.
version A fixed version; the latest when left out.
api Base URL of the quillflow API. Default /api.
language UI language. Default: the page's lang, then the form's default language.
.definition A definition passed directly instead of loading it.
.fetchDataSource Answer lookups yourself (tests, previews, your own transport).
.requestInit Extra fetch options for every API call, e.g. headers or credentials.
.strings Replace any built-in UI text.
.widgets Choose your own widget for a question (see below).

What people get

  • One page at a time, with "Trin 2 af 3", Back and Continue.
  • An error summary at the top whose links jump to the field; errors are shown above each input.
  • Focus moves to the page heading on a new page, and to the summary when there are errors.
  • "Required" errors wait until someone tries to continue. Format errors show once they have typed something.
  • Numbers typed the local way (1.234,50) and shown back readably.
  • A receipt with the reference number when the form is sent. Double clicks are harmless.

Events

All events bubble, so a page can listen on the form or on any parent.

Event detail When
qf-ready { formId, version } The form has loaded.
qf-change { path, value } Someone changed an answer. Fires per keystroke, like input.
qf-page { page, index, total } Someone moved to another page.
qf-submit { formId, version, data } About to send. Call preventDefault() to send it yourself.
qf-submitted { reference, receivedAt, data } The server accepted it.
qf-error { stage, message } Loading or sending failed.

Blazor

<QuillflowForm Form="loan-application" Api="/api" Language="@language"
               OnSubmitted="@(e => receipt = e.Reference)" />
  • The component loads the web component itself. Add _content/Quillflow.Blazor/quillflow.css for the default look.
  • It only subscribes to the events you handle, so a Blazor Server app is not sent a message for every keystroke.
  • Turn off prerendering on pages with a form: @rendermode @(new InteractiveServerRenderMode(prerender: false)). Otherwise Blazor replaces the form when the page becomes interactive, and early typing is lost. The build warns about this (QF0001).
  • HostSubmits="true" lets you send the data your own way (OnSubmit).

React, Angular, Vue

Use the element as it is, and listen for its events with addEventListener:

function Loan() {
  const ref = useRef<HTMLElement>(null);
  useEffect(() => {
    const done = (e: Event) => navigate(`/receipt/${(e as CustomEvent).detail.reference}`);
    ref.current?.addEventListener("qf-submitted", done);
    return () => ref.current?.removeEventListener("qf-submitted", done);
  }, []);
  return <qf-form ref={ref} form="loan-application" api="/api" />;
}

Theming

The form renders into the page's own DOM, not a shadow root. Native labels, browser autofill, screen readers and your stylesheet all work as usual.

  • Keep quillflow.css and adjust its variables: --qf-primary, --qf-error, --qf-focus, --qf-font, --qf-radius, --qf-max-width …
  • Or leave it out and style the qf-* classes with your own design system (e.g. Det Fælles Designsystem or GOV.UK Frontend).

Your own widgets

Every question type has a default widget. Replace one for every form on the page:

import { registerWidget } from "@quillflow/elements";
import { html } from "lit";

registerWidget("date", {
  layout: "label",
  render: (ctx) => html`<my-date-picker id=${ctx.id} .value=${ctx.field.value}
    @change=${(e) => ctx.set(e.target.value)}></my-date-picker>`,
});

Or per form, with the widgets property: a function from a question to a widget, or undefined for the default.

Security headers

The bundle needs no inline scripts and no eval. It works under a strict Content-Security-Policy such as default-src 'self'; script-src 'self'; style-src 'self'.