form.touchAll()
Quick Start (30 seconds)
const form = Forms.create({
firstName: '',
lastName: '',
email: '',
password: '',
confirmPassword: ''
});
// Initially, no fields are touched
console.log(form.touchedFields); // []
console.log(form.isDirty); // false
// Mark ALL fields as touched
form.touchAll();
console.log(form.touchedFields); // ['firstName', 'lastName', 'email', 'password', 'confirmPassword']
console.log(form.isDirty); // true
// Now all validation errors are visible
console.log(form.touched);
// {
// firstName: true,
// lastName: true,
// email: true,
// password: true,
// confirmPassword: true
// }What just happened? touchAll() marked every field in the form as touched in one call - perfect for showing all errors on submit!
What is form.touchAll()?
form.touchAll() is the complete touch activation method for marking all form fields as touched at once.
Simply put, it's a one-line way to mark every single field as touched, typically used when submitting a form to reveal all validation errors.
Key characteristics:
- ✅ Marks all fields as touched simultaneously
- ✅ Most efficient way to touch everything
- ✅ Perfect for submit button validation
- ✅ Updates all reactive UI automatically
- ✅ Returns the form instance for chaining
Syntax
// Mark all fields as touched
form.touchAll()
// With chaining
form
.touchAll()
.validateAllFields();Parameters: None
Returns: The form instance (this) for method chaining
Why Does This Exist?
The Challenge with Manual Touching
When submitting a form, you want to show all validation errors, not just the ones the user has touched. Manually touching each field is tedious.
const form = Forms.create({
field1: '',
field2: '',
field3: '',
field4: '',
field5: ''
});
// ❌ Tedious - manually touch each field
form.setTouched('field1', true);
form.setTouched('field2', true);
form.setTouched('field3', true);
form.setTouched('field4', true);
form.setTouched('field5', true);
// ❌ Or loop through fields
Object.keys(form.values).forEach(field => {
form.setTouched(field, true);
});
// ❌ Or use setTouchedFields
form.setTouchedFields(Object.keys(form.values));
// ✅ Simple - touch all at once
form.touchAll();When to use touchAll(): ✅ Form submission - Show all errors before submitting ✅ Validate all button - Manual validation trigger ✅ Final step validation - Multi-step form completion ✅ "Show all errors" mode - Toggle error visibility ✅ Testing/debugging - Quickly see all validation states
Mental Model
Think of touchAll() as flipping the master switch - it activates error visibility for every field at once.
Visual Representation
Before touchAll():
┌────────────────────────────────┐
│ Form Fields: │
│ ⚪ firstName (untouched) │
│ ⚪ lastName (untouched) │
│ ⚪ email (untouched) │
│ ✅ password (touched by user) │
│ ⚪ confirmPassword (untouched) │
└────────────────────────────────┘
↓
form.touchAll()
↓
After touchAll():
┌────────────────────────────────┐
│ Form Fields: │
│ ✅ firstName (touched) │
│ ✅ lastName (touched) │
│ ✅ email (touched) │
│ ✅ password (touched) │
│ ✅ confirmPassword (touched) │
└────────────────────────────────┘
All errors now visible!How Does It Work?
Internal Process
// When you call:
form.touchAll();
// Here's what happens internally:
1️⃣ Loop through all fields
Object.keys(form.values).forEach(field => {
form.touched[field] = true
})
2️⃣ Trigger reactive updates (once)
- form.isDirty → true
- form.touchedFields → all field names
3️⃣ All reactive effects fire (once)
- All error displays update
- All field styling updates
- Submit button state may change
4️⃣ Return form instance for chaining
return thisReactivity Flow Diagram
touchAll()
↓
Mark all fields as touched
↓
Reactive properties update:
- isDirty → true
- touchedFields → all fields
↓
All effects fire once
↓
All error messages appear
All field styling updatesBasic Usage
Example 1: Show All Errors on Submit
const form = Forms.create(
{
email: '',
password: '',
username: ''
},
{
email: (value) => !value.includes('@') ? 'Invalid email' : '',
password: (value) => value.length < 8 ? 'Too short' : '',
username: (value) => !value ? 'Required' : ''
}
);
submitButton.addEventListener('click', (e) => {
e.preventDefault();
// Mark all fields as touched
form.touchAll();
// Now all validation errors are visible
if (form.isValid) {
handleSubmit();
} else {
alert(`Please fix ${form.errorFields.length} error(s)`);
}
});Example 2: Validate All Button
const form = Forms.create({
field1: '',
field2: '',
field3: ''
});
validateAllButton.addEventListener('click', () => {
// Show all errors
form.touchAll();
// Run validation
form.validateAllFields();
// Show summary
if (form.isValid) {
alert('All fields are valid!');
} else {
alert(`${form.errorFields.length} field(s) have errors`);
}
});Example 3: Multi-Step Form Final Validation
const form = Forms.create({
// Step 1
firstName: '',
lastName: '',
// Step 2
email: '',
phone: '',
// Step 3
address: '',
city: ''
});
function finalizeForm() {
// Touch all fields to show any remaining errors
form.touchAll();
if (form.isValid) {
submitForm();
} else {
// Find first step with errors
const firstErrorField = form.errorFields[0];
const stepWithError = getStepForField(firstErrorField);
alert(`Please fix errors in step ${stepWithError}`);
goToStep(stepWithError);
}
}
submitButton.addEventListener('click', finalizeForm);Example 4: Toggle Error Display Mode
const form = Forms.create({
field1: '',
field2: '',
field3: ''
});
let showAllErrors = false;
showAllErrorsCheckbox.addEventListener('change', (e) => {
showAllErrors = e.target.checked;
if (showAllErrors) {
form.touchAll();
} else {
// Reset to only user-touched fields
resetTouchState();
}
});Example 5: Pre-Submit Validation Check
const form = Forms.create({
email: '',
password: ''
});
async function handleSubmit() {
// First, touch all fields to show errors
form.touchAll();
// Check if form is valid
if (!form.isValid) {
// Focus first error
const firstError = form.errorFields[0];
document.querySelector(`[name="${firstError}"]`)?.focus();
return;
}
// Proceed with submission
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(form.values)
});
// Handle response...
}Advanced Patterns
Pattern 1: Smart Submit with touchAll()
const form = Forms.create({
email: '',
password: '',
rememberMe: false
});
async function smartSubmit() {
// Touch all fields
form.touchAll();
// Validate
if (!form.isValid) {
// Collect error summary
const errors = form.errorFields.map(field => ({
field,
message: form.getError(field)
}));
// Show error modal
showErrorModal(errors);
return;
}
// Show loading
form.isSubmitting = true;
try {
await submitToServer(form.values);
showSuccessMessage();
} catch (error) {
showErrorMessage(error.message);
} finally {
form.isSubmitting = false;
}
}Pattern 2: Conditional touchAll() Based on Attempt Count
const form = Forms.create({
field1: '',
field2: '',
field3: ''
});
let submitAttempts = 0;
submitButton.addEventListener('click', () => {
submitAttempts++;
// Only touch all after 2 failed attempts
if (submitAttempts >= 2) {
form.touchAll();
}
if (form.isValid) {
handleSubmit();
submitAttempts = 0; // Reset on success
}
});Pattern 3: touchAll() with Progress Indicator
const form = Forms.create({
field1: '',
field2: '',
field3: '',
field4: '',
field5: ''
});
function showValidationProgress() {
// Touch all to enable validation
form.touchAll();
// Run validation with progress
const fields = Object.keys(form.values);
let validated = 0;
fields.forEach((field, index) => {
setTimeout(() => {
form.validateField(field);
validated++;
// Update progress
const progress = (validated / fields.length) * 100;
updateProgressBar(progress);
if (validated === fields.length) {
showValidationSummary();
}
}, index * 100);
});
}Pattern 4: touchAll() with Field Highlighting
const form = Forms.create({
field1: '',
field2: '',
field3: ''
});
function highlightAllErrors() {
form.touchAll();
// Highlight each field with error
form.errorFields.forEach(field => {
const inputEl = document.querySelector(`[name="${field}"]`);
inputEl?.classList.add('error-highlight');
// Flash animation
inputEl?.classList.add('error-flash');
setTimeout(() => {
inputEl?.classList.remove('error-flash');
}, 1000);
});
}
// CSS:
// .error-flash {
// animation: flash 1s ease;
// }
// @keyframes flash {
// 0%, 100% { background-color: transparent; }
// 50% { background-color: #ffcccc; }
// }Pattern 5: touchAll() with Scroll to First Error
const form = Forms.create({
field1: '',
field2: '',
field3: '',
field4: '',
field5: ''
});
function validateAndScrollToError() {
form.touchAll();
if (!form.isValid) {
// Find first error field
const firstErrorField = form.errorFields[0];
const element = document.querySelector(`[name="${firstErrorField}"]`);
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
// Focus after scroll completes
setTimeout(() => {
element.focus();
}, 500);
}
}
}Pattern 6: touchAll() with Error Grouping
const form = Forms.create({
personal_name: '',
personal_email: '',
company_name: '',
company_tax: '',
shipping_address: '',
shipping_city: ''
});
function touchAllAndGroupErrors() {
form.touchAll();
const errorsByGroup = {
personal: [],
company: [],
shipping: []
};
form.errorFields.forEach(field => {
if (field.startsWith('personal_')) {
errorsByGroup.personal.push(field);
} else if (field.startsWith('company_')) {
errorsByGroup.company.push(field);
} else if (field.startsWith('shipping_')) {
errorsByGroup.shipping.push(field);
}
});
// Display grouped errors
Object.entries(errorsByGroup).forEach(([group, fields]) => {
if (fields.length > 0) {
const groupEl = document.getElementById(`${group}-errors`);
groupEl.textContent = `${fields.length} error(s) in ${group} section`;
}
});
}Pattern 7: Delayed touchAll() for Better UX
const form = Forms.create({
field1: '',
field2: '',
field3: ''
});
function gentleTouchAll() {
const fields = Object.keys(form.values);
// Touch fields one by one with delay
fields.forEach((field, index) => {
setTimeout(() => {
form.setTouched(field, true);
}, index * 100);
});
}
// Gentler than instant touchAll()
submitButton.addEventListener('click', () => {
gentleTouchAll();
setTimeout(() => {
if (form.isValid) {
handleSubmit();
}
}, fields.length * 100);
});Pattern 8: touchAll() with Analytics
const form = Forms.create({
field1: '',
field2: '',
field3: ''
});
function touchAllWithAnalytics() {
const beforeTouch = {
touchedCount: form.touchedFields.length,
errorCount: form.errorFields.length,
validFieldCount: Object.keys(form.values).length - form.errorFields.length
};
form.touchAll();
const afterTouch = {
touchedCount: form.touchedFields.length,
errorCount: form.errorFields.length,
newlyRevealedErrors: form.errorFields.filter(field =>
!beforeTouch.touchedFields?.includes(field)
).length
};
// Send analytics
analytics.track('form_validation_triggered', {
totalFields: Object.keys(form.values).length,
errorCount: afterTouch.errorCount,
newlyRevealedErrors: afterTouch.newlyRevealedErrors
});
}Pattern 9: touchAll() with Confirmation
const form = Forms.create({
field1: '',
field2: '',
field3: '',
field4: '',
field5: ''
});
async function confirmAndTouchAll() {
const untouchedCount = Object.keys(form.values).length - form.touchedFields.length;
if (untouchedCount > 0) {
const confirmed = await showConfirmDialog(
`${untouchedCount} field(s) haven't been reviewed. Show all errors?`
);
if (!confirmed) {
return false;
}
}
form.touchAll();
return true;
}
submitButton.addEventListener('click', async () => {
const shouldProceed = await confirmAndTouchAll();
if (shouldProceed && form.isValid) {
handleSubmit();
}
});Pattern 10: touchAll() with State Restoration
const form = Forms.create({
field1: '',
field2: '',
field3: ''
});
let previousTouchState = {};
function touchAllWithRestore() {
// Save current touch state
previousTouchState = { ...form.touched };
// Touch all
form.touchAll();
// Return restore function
return () => {
Object.entries(previousTouchState).forEach(([field, touched]) => {
form.setTouched(field, touched);
});
};
}
// Usage:
const restore = touchAllWithRestore();
// ... show errors ...
// Later: restore previous state
cancelButton.addEventListener('click', restore);Common Pitfalls
Pitfall 1: Using touchAll() on Data Load
const form = Forms.create({
email: '',
password: ''
});
async function loadData() {
const data = await fetchUserData();
form.setValues(data);
// ❌ Don't touch all when loading data
form.touchAll(); // Shows errors immediately (bad UX)
}
// ✅ Don't touch fields when loading
async function loadData() {
const data = await fetchUserData();
Object.entries(data).forEach(([field, value]) => {
form.setValue(field, value);
form.setTouched(field, false); // Keep untouched
});
}Pitfall 2: Not Validating After touchAll()
const form = Forms.create({
email: ''
});
submitButton.addEventListener('click', () => {
// ❌ Touching doesn't run validation
form.touchAll();
if (form.isValid) {
// Might be true even if fields are invalid!
}
});
// ✅ Validate after touching (if needed)
submitButton.addEventListener('click', () => {
form.touchAll();
form.validateAllFields(); // Run validation
if (form.isValid) {
handleSubmit();
}
});
// Note: If you have validators defined, they run automatically
// But for manual validation scenarios, call validateAllFields()Pitfall 3: Using touchAll() Too Early
const form = Forms.create({
field1: '',
field2: ''
});
// ❌ Touching all fields on page load
window.addEventListener('load', () => {
form.touchAll(); // Bad UX - shows errors immediately
});
// ✅ Only touch all on explicit user action
submitButton.addEventListener('click', () => {
form.touchAll();
});Pitfall 4: Not Providing User Feedback
const form = Forms.create({
field1: '',
field2: '',
field3: ''
});
submitButton.addEventListener('click', () => {
// ❌ Silent touchAll - user doesn't know what happened
form.touchAll();
if (!form.isValid) {
// Form just shows errors with no explanation
}
});
// ✅ Provide feedback
submitButton.addEventListener('click', () => {
form.touchAll();
if (!form.isValid) {
const errorCount = form.errorFields.length;
showNotification(`Please fix ${errorCount} error(s) before submitting`);
}
});Pitfall 5: Assuming touchAll() Clears Errors
const form = Forms.create({
email: ''
});
form.setError('email', 'Server error');
// ❌ touchAll() doesn't clear errors
form.touchAll();
console.log(form.errors.email); // Still 'Server error'
// ✅ Clear errors separately if needed
form.clearErrors();
form.touchAll();Summary
Key Takeaways
touchAll()marks all fields as touched in one efficient operation.No parameters needed - affects all fields automatically.
Perfect for form submission - reveals all validation errors at once.
Single reactive update - UI updates once, not per field.
Returns form instance - enables method chaining.
Doesn't run validation - just marks fields as touched (validators run automatically if defined).
When to Use touchAll()
✅ Use touchAll() for:
- Form submission handlers
- "Validate All" button functionality
- Final step of multi-step forms
- Manual validation triggers
- Testing/debugging validation
❌ Don't use touchAll() for:
- Loading saved data (keep fields untouched)
- Partial form validation (use
setTouchedFields()) - Initial page load (bad UX)
- Automatic validation (let fields touch naturally)
Comparison Table
| Method | Fields Affected | Use Case |
|---|---|---|
setTouched('email', true) | One field | Single field control |
setTouchedFields(['email', 'password']) | Multiple specific | Batch specific fields |
touchAll() | All fields | Submit validation ✅ |
One-Line Rule
form.touchAll()marks every field in the form as touched - use it when submitting to reveal all validation errors at once.
What's Next?
- Learn about
form.isTouched()for checking individual field touch state - Explore
form.shouldShowError()for smart error display logic - Master touch state management strategies