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.cssfor 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.cssand 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'.