What Is the Forms Module?
Quick Start (30 seconds)
<!-- Your HTML form -->
<form id="loginForm">
<input name="email" type="email" required />
<input name="password" type="password" required />
<button type="submit">Login</button>
</form>// Read all values instantly
const data = Forms.loginForm.values;
// → { email: 'user@example.com', password: 'secret' }
// Validate before submitting
const { isValid, errors } = Forms.loginForm.validate();
// Submit to your server
await Forms.loginForm.submitData({
url: '/api/login',
onSuccess: (result) => console.log('Logged in!', result)
});That's it. No manual querySelector. No FormData boilerplate. No custom fetch setup. The Forms module does it all.
What Is the Forms Module?
Forms is a smart, proxy-based system that gives every <form> element on your page a rich API for reading values, validating fields, serializing data, and submitting to a server — all through a simple, consistent interface.
Think of it as giving your plain HTML forms superpowers — they can now read and write their own data, validate themselves, and even talk to your server, without any extra setup.
Why Does This Exist?
Working with Forms in Plain JavaScript
Without the Forms module, handling even a basic form requires a lot of repetitive work:
// Get values — manual and tedious
const form = document.getElementById('loginForm');
const email = form.querySelector('[name="email"]').value;
const password = form.querySelector('[name="password"]').value;
const remember = form.querySelector('[name="remember"]').checked;
// Validate — you write all the logic yourself
let errors = {};
if (!email || !email.includes('@')) errors.email = 'Invalid email';
if (!password || password.length < 6) errors.password = 'Too short';
// Show errors — more manual DOM work
if (errors.email) {
const field = form.querySelector('[name="email"]');
field.classList.add('invalid');
// inject an error element...
}
// Submit — wire up fetch yourself
if (Object.keys(errors).length === 0) {
fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password, remember })
})
.then(res => res.json())
.then(data => { /* handle */ })
.catch(err => { /* handle */ });
}Problems: ❌ Reading each field individually is tedious and error-prone ❌ Validation logic must be written from scratch every time ❌ Showing/clearing error messages requires manual DOM manipulation ❌ Fetch boilerplate repeats on every form in your project ❌ Forgetting one field means a bug that's hard to catch
The Forms Module Way
// Read all values in one go
const { isValid, errors } = Forms.loginForm.validate({
email: { required: true, email: true },
password: { required: true, minLength: 6 }
});
if (isValid) {
await Forms.loginForm.submitData({
url: '/api/login',
onSuccess: (result) => console.log('Done!', result),
onError: (error) => console.error('Failed:', error)
});
}Benefits: ✅ All field values in one object — no manual querySelector ✅ Built-in validation with HTML5 + custom rules ✅ Error messages appear and clear automatically ✅ Fetch is handled for you — just pass a URL ✅ Works the same way for every form in your project
Mental Model: The Smart Form Assistant
Think of the Forms module like a dedicated assistant sitting at a front desk, managing every form in your office building.
Without Forms (you doing everything manually):
├── Walk to desk 1, ask for the name field value
├── Walk to desk 2, ask for the email field value
├── Check each value yourself
├── Write the error note and tape it to each desk
├── Package everything up and mail it
└── Handle the response yourself
With Forms (the smart assistant handles it):
├── "Hey Forms.contactForm — give me all the values"
│ → { name: 'Alice', email: 'alice@example.com' }
├── "Validate against these rules"
│ → { isValid: true, errors: {} }
└── "Submit to /api/contact"
→ handles fetch, errors, and responses for youYou describe what you want. The Forms module handles how to do it.
How Does It Work?
The Forms object is a JavaScript Proxy. When you write Forms.loginForm, it intercepts that property access and:
Forms.loginForm
↓
Proxy intercepts "loginForm"
↓
Looks up document.getElementById("loginForm")
↓
Finds a <form> element — enhances it
↓
Returns the enhanced form with full API
↓
You call .values, .validate(), .submitData()The enhancement pipeline runs once per form and adds all the methods:
Raw <form> element
↓
Step 1: Core .update() from EnhancedUpdateUtility
↓
Step 2: .values, .validate(), .reset(), .serialize(), .submitData()
↓
Step 3: Form-aware .update() wrapper (handles values/validate/reset/submit keys)
↓
Step 4: External enhancers (from 02_dh-form-enhance.js or your own plugins)
↓
Fully enhanced <form> element ✨The result is cached in a Map, so the second time you access Forms.loginForm, it returns the already-enhanced element instantly.
What Can the Forms Module Do?
Here's the complete list of capabilities added to every form:
| Feature | How to use |
|---|---|
| Read all values | form.values |
| Write all values | form.values = { name: 'Alice' } |
| Validate fields | form.validate(rules) |
| Clear validation | form.clearValidation() |
| Read a single field | form.getField('email') |
| Write a single field | form.setField('email', 'alice@example.com') |
| Serialize data | form.serialize('json') |
| Submit via fetch | form.submitData(options) |
| Reset the form | form.reset() |
| Update anything | form.update({ values, validate, text, style, ... }) |
Accessing a Form
The Forms global object is your entry point. Access any form by its id:
// HTML: <form id="myForm">...</form>
const form = Forms.myForm; // get the enhanced form element
form.values; // read all valuesYou can also check if a form exists before using it:
if ('myForm' in Forms) {
console.log('Form found:', Forms.myForm);
}Global Utilities
Beyond per-form access, Forms also has global helpers:
// Get all forms on the page (enhanced)
const allForms = Forms.getAllForms();
// Validate every form at once
const results = Forms.validateAll();
// Reset every form
Forms.resetAll();
// Check cache stats
console.log(Forms.stats());
// → { hits: 5, misses: 2, cacheSize: 3, hitRate: 0.71 }The Complete Forms Namespace
Forms
├── Forms.formId → access any form by its HTML id
├── Forms.getAllForms() → array of all enhanced forms
├── Forms.validateAll() → validate every form at once
├── Forms.resetAll() → reset every form at once
├── Forms.stats() → cache hit/miss statistics
├── Forms.clear() → clear the internal cache
├── Forms.configure() → update module options
└── Forms.addEnhancer() → register a plugin function
Per-form API (available on every Forms.formId):
├── .values → getter/setter for all field data
├── .validate(rules) → run validation
├── .clearValidation() → remove error state
├── .getField(name) → get a single field element
├── .setField(name, val) → set a single field value
├── .serialize(format) → 'object' | 'json' | 'formdata' | 'urlencoded'
├── .submitData(options) → async fetch submission
├── .reset(options) → reset + clear validation
└── .update(object) → form-aware update methodKey Principle
"Access a form by its ID, then call methods on it — just like you would on any well-designed JavaScript object."
Forms.loginForm is not magic — it's a Proxy doing smart DOM lookups and caching. Once you have the form, everything else is a straightforward method call.
What's Next?
Now that you understand what the Forms module is and how it's structured:
- Reading and Writing Values — using
.valuesgetter and setter - Validation — built-in rules and custom validators
- Serialization — converting form data to JSON, FormData, etc.
- Submission — async fetch with lifecycle hooks
- The Enhanced Pipeline — advanced submission with loading states and retries