StringForm Module
Declarative, HTML-first validation for complex forms. StringForm reads rule strings from your markup, filters keystrokes in real time, mirrors error states in CSS, and emits events once everything is valid.
Activation primer: Add
string="form"to every<form>you wantStringFormto manage. Supplystring-id="your-id"when you need a stable handle for events, mirrors, orstring-copy-from; otherwise StringTune generates one automatically.
HTML Attributes
| Attribute | Target | Default | Controls | Practical notes |
|---|---|---|---|---|
string-input="rule|list" | <input>, <select>, <textarea> | "" | Pipe-delimited validation rules | Works with native attributes (required, type) but enforces extra rules |
string-input="group[key]" | Any container | — | Mirrors the state of the field mapped by key (-error, -valid) | Match key to the field id or name; useful for wrapping labels/helpers |
string-input="error[key]" | Any element | — | Receives rendered error <span> nodes for the field referenced by key | Keeps markup clean and avoids manual error message wiring |
Module Snapshot
- Activation attribute:
string="form" - Field discovery: scans descendants with
string-input, excluding helper targets (group[*],error[*]) - Class states:
-inited(initialised),-valid,-errortoggle on both inputs and their mapped groups - Input filtering: intercepts
beforeinputto block invalid keystrokes for live rules likepatternornumber - Submission gate: prevents default submit, validates every field, focuses the first invalid control, and emits events
- Data output:
form:submit:<id>payload is a flat object respecting checkbox arrays, multi-select lists, and radio groups
Basic Usage
import StringTune, { StringForm } from '@fiddle-digital/string-tune';
const stringTune = StringTune.getInstance();
stringTune.use(StringForm);
stringTune.start(60);
<form string="form" string-id="contact">
<div class="field" string-input="group[name]">
<label for="name">Name</label>
<input id="name" name="name" string-input="required|min:2" autocomplete="name" />
<p class="error" string-input="error[name]"></p>
</div>
<div class="field" string-input="group[email]">
<label for="email">Email</label>
<input id="email" name="email" type="email" string-input="required|email" />
<p class="error" string-input="error[email]"></p>
</div>
<fieldset class="field" string-input="group[contact-pref]">
<legend>Preferred contact</legend>
<label><input type="radio" name="contact-pref" value="phone" string-input="required" /> Phone</label>
<label><input type="radio" name="contact-pref" value="email" string-input="required" /> Email</label>
<p class="error" string-input="error[contact-pref]"></p>
</fieldset>
<button type="submit">Send</button>
</form>
.field {
display: grid;
gap: 6px;
}
.field.-error input,
.field.-error textarea,
.field.-error select {
border-color: #d64949;
}
.error {
min-height: 1.25rem;
color: #d64949;
font-size: 0.85rem;
}
input.-valid {
border-color: #2f9b59;
}
Rule Library
Combine rule tokens with | (e.g. required|min:3|email). You can extend the parser globally via settings, but the built-ins cover most patterns.
| Rule | Purpose |
|---|---|
required | Rejects empty strings, unchecked radios/checkboxes, or empty arrays |
checked | Forces a checkbox-style field to be explicitly acknowledged |
number | Allows only digits, optional decimal point, and leading minus (guards both typing and submission) |
integer | Filters typing to whole numbers; pair with number if you also need submit-time enforcement |
email | Basic email format guard; complements native type="email" |
phone | Restricts to digits, spaces, +, -, (, ) with limited plus signs |
letters / lettersSpaces / lettersNumbers | Keystroke filters for alphabetic characters (submit-time validation not enforced) |
url | Permits URL-safe characters and validates structure on submit |
min:x / max:x | Length constraints for string values |
pattern:/.../ | Custom RegExp guard during typing; combine with other rules for submit-time validation |
Rules run both during
beforeinput(to stop illegal characters) and on submit, ensuring consistent enforcement.
How It Works
- Field mapping: each control gets a stable key based on
string-id,id, orname; this key wires up group and error targets. - Live validation: on
input/changethe module re-validates the control, updates error text, and toggles CSS classes. - Keystroke guards:
beforeinputinterception prevents the user from entering disallowed characters, keeping values clean. - Submission flow:
submitis intercepted, all fields re-validated, the first invalid control focused, and events raised accordingly. - Cleanup ready: listeners are tracked in
form-eventsso disconnecting the object removes hooks automatically.
Event Signals
| Channel pattern | Payload | Fired when | Use cases |
|---|---|---|---|
form:submit:<id> | { [key: string]: string | string[] | boolean } | Form passes validation during submit | Send payload via fetch, trigger success UI, analytics |
form:invalid:<id> | void | Submit attempted with at least one invalid field | Shake form, show toast, scroll to first error |
stringTune.on('form:submit:contact', (data) => {
console.log('Form ready', data);
});
stringTune.on('form:invalid:contact', () => {
console.warn('Please revise the highlighted fields.');
});
StringForm keeps validation declarative—describe your constraints inline, style via the emitted classes, and rely on the submit events to bridge into your app logic.