VirtualForm
VirtualForm
programmatically builds and submits a hidden HTML
<form>.
It exists for situations where
Ajax
is the wrong tool. Where
Ajax
is for
API-style requests.
VirtualForm
is for browser-native navigation flows.
Use
VirtualForm
when you want:
- Real browser navigation.
- Full page reloads.
- Standard form submission semantics.
-
Compatibility with backend method override conventions (for example,
DELETE via
_method). -
To let other libraries intercept the native
submitevent.
The generated form is appended to
document.body, has
data-qite-virtual-form="1"
for identification and is styled with
display: none
Basic usage
The simplest form is:
new VirtualForm("/users/1", { name: "John" }).submit();
Where:
- The 1st argument is string and uss URL for form action
-
The 2nd argument is an object map or
[{ name, value }]which represents field names and values. - The 3rd argument is a map of configuration options.
It's usually desirable to set configuration defaults for all forms:
VirtualForm.configure({ method: "POST", csrf: "Rails" });
This modifies static defaults used by future instances, but per-instance options
provided via the 3rd argument to
new VirtualForm()
always override global
configuration.
Submitting
Calling
.submit()
does more than
form.submit(). Why? In browsers,
form.submit()
bypasses the
submit
event entirely, which means
there's no
preventDefault()
interception, which in turn means it's harder to
test and integrate with global listeners. For that reason
VirtualForm.submit()
does the following:
- Builds the form.
-
Appends it to
document.body -
Dispatches a cancelable
"submit"event manually. - If not prevented, performs the real submission.
If any listener calls
preventDefault(), navigation is skipped and the
form element is returned. This makes
VirtualForm
safe for tests and
integration with event-driven code.
HTTP methods and method override
Browsers only support
GET
and
POST
in
<form method="...">.
If you pass a different method:
new VirtualForm("/users/1", {}, { method: "DELETE" }).submit();
the form is still submitted as
POST, but a hidden
_method
field is
added with value
"DELETE". This matches common Rails and other backend
framework conventions. The idea is that you can easily create links or buttons
which, when clicked, should submit a request like "Delete profile" or "Sign out",
but that don't technically require a visible form on screen, because from
user's perspective it is clear what the action should do without providing
additional information.
CSRF support
VirtualForm
supports CSRF in two ways.
Template-based:
new VirtualForm("/users", {}, {
method: "POST",
csrf: "Rails"
}).submit();
When
csrf
is provided, Qite resolves how to read the token (via a special
csrf getter function), and what hidden field name to use.
Manual override:
new VirtualForm("/users", {}, {
csrf_getter: () => "...token...",
csrf_form_field_name: "authenticity_token"
}).submit();
If both
csrf_getter
and
csrf_form_field_name
are present, the getter is
called at submission time and a hidden field is added automatically.
If the getter returns
null
or
undefined, no CSRF field is added.
This mirrors the way CSRF is handled by the
Ajax
module.
Field handling
Fields may be:
-
An object map:
{ name: "John", age: 30 } -
Or an array of pairs:
[{ name: "user[name]", value: "John" }]
Implicit rules apply:
-
nullorundefinedvalues are ignored. - All values are converted to strings.
-
Hidden
<input type="hidden">elements are generated for each field.
Practical patterns
Redirect after client-side validation:
if (this.isValid()) {
new VirtualForm("/checkout", this.fields.all).submit();
}
Logout link that must perform POST:
new VirtualForm("/logout", {}, {
method: "DELETE",
csrf: "Rails"
}).submit();
Intercept submission globally:
document.addEventListener("submit", (e) => {
if (e.target.dataset.qiteVirtualForm === "1") {
// inspect, log, or cancel
}
});
VirtualForm
intentionally does very little: there's not validation, retries,
managing state or history.
VirtualForm
simply gives you a clean,
testable way to trigger real browser form navigation with modern configuration
and CSRF support. Use it when you want the browser to take over and use
Ajax
when you want to stay in control.