Skip to content

Sponsor

Sponsor

form.touchAll()

Quick Start (30 seconds)

javascript
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

javascript
// 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.

javascript
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

javascript
// 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 this

Reactivity 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 updates

Basic Usage

Example 1: Show All Errors on Submit

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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()

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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()

javascript
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

javascript
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

javascript
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

javascript
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

  1. touchAll() marks all fields as touched in one efficient operation.

  2. No parameters needed - affects all fields automatically.

  3. Perfect for form submission - reveals all validation errors at once.

  4. Single reactive update - UI updates once, not per field.

  5. Returns form instance - enables method chaining.

  6. 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

MethodFields AffectedUse Case
setTouched('email', true)One fieldSingle field control
setTouchedFields(['email', 'password'])Multiple specificBatch specific fields
touchAll()All fieldsSubmit 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